2001-07-09 10:35:18 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
|
|
|
|
* Copyright (c) 1995 Martin Husemann
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
#ifndef lint
|
2012-10-21 12:01:11 +00:00
|
|
|
__RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $");
|
2001-07-09 10:35:18 +00:00
|
|
|
static const char rcsid[] =
|
|
|
|
"$FreeBSD$";
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "ext.h"
|
|
|
|
#include "fsutil.h"
|
|
|
|
|
2010-02-14 12:30:30 +00:00
|
|
|
static int checkclnum(struct bootblock *, u_int, cl_t, cl_t *);
|
|
|
|
static int clustdiffer(cl_t, cl_t *, cl_t *, u_int);
|
2002-03-20 22:57:10 +00:00
|
|
|
static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
|
2010-02-14 12:30:30 +00:00
|
|
|
static int _readfat(int, struct bootblock *, u_int, u_char **);
|
2001-07-09 10:35:18 +00:00
|
|
|
|
2004-02-05 06:55:12 +00:00
|
|
|
/*-
|
|
|
|
* The first 2 FAT entries contain pseudo-cluster numbers with the following
|
|
|
|
* layout:
|
|
|
|
*
|
|
|
|
* 31...... ........ ........ .......0
|
|
|
|
* rrrr1111 11111111 11111111 mmmmmmmm FAT32 entry 0
|
|
|
|
* rrrrsh11 11111111 11111111 11111xxx FAT32 entry 1
|
|
|
|
*
|
|
|
|
* 11111111 mmmmmmmm FAT16 entry 0
|
|
|
|
* sh111111 11111xxx FAT16 entry 1
|
|
|
|
*
|
|
|
|
* r = reserved
|
|
|
|
* m = BPB media ID byte
|
|
|
|
* s = clean flag (1 = dismounted; 0 = still mounted)
|
|
|
|
* h = hard error flag (1 = ok; 0 = I/O error)
|
|
|
|
* x = any value ok
|
|
|
|
*/
|
|
|
|
|
2004-02-05 06:32:16 +00:00
|
|
|
int
|
|
|
|
checkdirty(int fs, struct bootblock *boot)
|
|
|
|
{
|
|
|
|
off_t off;
|
|
|
|
u_char *buffer;
|
|
|
|
int ret = 0;
|
2012-10-21 12:01:11 +00:00
|
|
|
size_t len;
|
2004-02-05 06:32:16 +00:00
|
|
|
|
Fixed some bugs in checkdirty(). The check for the clean bit was
combined with the the signature check in a wrong way (basically
(dirty:= signature_recognised() && !clean) instead of
(mightbedirty:= !signature_recognized || !clean), so file systems
with unrecognized signatures were considered clean. Many of the
don't-care and reserved bits were not ignored, so some file systems
with valid signatures were unrecognized. One of my FAT32 file systems
has a signature of f8,ff,ff,ff,ff,ff,ff,f7 when dirty, but only
f8,ff,ff,0f,ff,ff,ff,07 was recognised as dirty for FAT32, so the
fail-unsafeness made my file system always considered clean.
Check the i/o non-error bit in checkdirty(). Its absence would give
an unrecognized signature in code that is unaware of it, but we now
mask it out of the signature so we have to check it explicitly. This
combines naturally with the check of the clean bit.
Reviewed by: rnordier (except for final details)
2004-02-05 15:18:18 +00:00
|
|
|
if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
|
2004-02-05 06:32:16 +00:00
|
|
|
return 0;
|
|
|
|
|
2010-02-14 12:31:28 +00:00
|
|
|
off = boot->bpbResSectors;
|
|
|
|
off *= boot->bpbBytesPerSec;
|
2004-02-05 06:32:16 +00:00
|
|
|
|
2012-10-21 12:01:11 +00:00
|
|
|
buffer = malloc(len = boot->bpbBytesPerSec);
|
2004-02-05 06:32:16 +00:00
|
|
|
if (buffer == NULL) {
|
2012-10-21 12:01:11 +00:00
|
|
|
perr("No space for FAT sectors (%zu)", len);
|
2004-02-05 06:32:16 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lseek(fs, off, SEEK_SET) != off) {
|
2012-10-21 12:01:11 +00:00
|
|
|
perr("Unable to read FAT");
|
2004-02-05 06:32:16 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2010-06-20 09:40:54 +00:00
|
|
|
if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) !=
|
|
|
|
boot->bpbBytesPerSec) {
|
2012-10-21 12:01:11 +00:00
|
|
|
perr("Unable to read FAT");
|
2004-02-05 06:32:16 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
Fixed some bugs in checkdirty(). The check for the clean bit was
combined with the the signature check in a wrong way (basically
(dirty:= signature_recognised() && !clean) instead of
(mightbedirty:= !signature_recognized || !clean), so file systems
with unrecognized signatures were considered clean. Many of the
don't-care and reserved bits were not ignored, so some file systems
with valid signatures were unrecognized. One of my FAT32 file systems
has a signature of f8,ff,ff,ff,ff,ff,ff,f7 when dirty, but only
f8,ff,ff,0f,ff,ff,ff,07 was recognised as dirty for FAT32, so the
fail-unsafeness made my file system always considered clean.
Check the i/o non-error bit in checkdirty(). Its absence would give
an unrecognized signature in code that is unaware of it, but we now
mask it out of the signature so we have to check it explicitly. This
combines naturally with the check of the clean bit.
Reviewed by: rnordier (except for final details)
2004-02-05 15:18:18 +00:00
|
|
|
/*
|
|
|
|
* If we don't understand the FAT, then the file system must be
|
|
|
|
* assumed to be unclean.
|
|
|
|
*/
|
2010-02-14 12:31:28 +00:00
|
|
|
if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff)
|
Fixed some bugs in checkdirty(). The check for the clean bit was
combined with the the signature check in a wrong way (basically
(dirty:= signature_recognised() && !clean) instead of
(mightbedirty:= !signature_recognized || !clean), so file systems
with unrecognized signatures were considered clean. Many of the
don't-care and reserved bits were not ignored, so some file systems
with valid signatures were unrecognized. One of my FAT32 file systems
has a signature of f8,ff,ff,ff,ff,ff,ff,f7 when dirty, but only
f8,ff,ff,0f,ff,ff,ff,07 was recognised as dirty for FAT32, so the
fail-unsafeness made my file system always considered clean.
Check the i/o non-error bit in checkdirty(). Its absence would give
an unrecognized signature in code that is unaware of it, but we now
mask it out of the signature so we have to check it explicitly. This
combines naturally with the check of the clean bit.
Reviewed by: rnordier (except for final details)
2004-02-05 15:18:18 +00:00
|
|
|
goto err;
|
|
|
|
if (boot->ClustMask == CLUST16_MASK) {
|
|
|
|
if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f)
|
|
|
|
goto err;
|
|
|
|
} else {
|
|
|
|
if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f
|
|
|
|
|| (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff
|
|
|
|
|| buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now check the actual clean flag (and the no-error flag).
|
|
|
|
*/
|
|
|
|
if (boot->ClustMask == CLUST16_MASK) {
|
|
|
|
if ((buffer[3] & 0xc0) == 0xc0)
|
|
|
|
ret = 1;
|
|
|
|
} else {
|
|
|
|
if ((buffer[7] & 0x0c) == 0x0c)
|
|
|
|
ret = 1;
|
|
|
|
}
|
2004-02-05 06:32:16 +00:00
|
|
|
|
|
|
|
err:
|
|
|
|
free(buffer);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2001-07-09 10:35:18 +00:00
|
|
|
/*
|
|
|
|
* Check a cluster number for valid value
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-14 12:30:30 +00:00
|
|
|
checkclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
if (*next >= (CLUST_RSRVD&boot->ClustMask))
|
|
|
|
*next |= ~boot->ClustMask;
|
|
|
|
if (*next == CLUST_FREE) {
|
|
|
|
boot->NumFree++;
|
|
|
|
return FSOK;
|
|
|
|
}
|
|
|
|
if (*next == CLUST_BAD) {
|
|
|
|
boot->NumBad++;
|
|
|
|
return FSOK;
|
|
|
|
}
|
|
|
|
if (*next < CLUST_FIRST
|
|
|
|
|| (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
|
|
|
|
pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n",
|
|
|
|
cl, fat,
|
|
|
|
*next < CLUST_RSRVD ? "out of range" : "reserved",
|
|
|
|
*next&boot->ClustMask);
|
|
|
|
if (ask(0, "Truncate")) {
|
|
|
|
*next = CLUST_EOF;
|
|
|
|
return FSFATMOD;
|
|
|
|
}
|
|
|
|
return FSERROR;
|
|
|
|
}
|
|
|
|
return FSOK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a FAT from disk. Returns 1 if successful, 0 otherwise.
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-14 12:30:30 +00:00
|
|
|
_readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
off_t off;
|
2012-10-21 12:01:11 +00:00
|
|
|
size_t len;
|
2001-07-09 10:35:18 +00:00
|
|
|
|
2012-10-21 12:01:11 +00:00
|
|
|
*buffer = malloc(len = boot->FATsecs * boot->bpbBytesPerSec);
|
2001-07-09 10:35:18 +00:00
|
|
|
if (*buffer == NULL) {
|
2012-10-21 12:01:11 +00:00
|
|
|
perr("No space for FAT sectors (%zu)", len);
|
2001-07-09 10:35:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-14 12:31:28 +00:00
|
|
|
off = boot->bpbResSectors + no * boot->FATsecs;
|
|
|
|
off *= boot->bpbBytesPerSec;
|
2001-07-09 10:35:18 +00:00
|
|
|
|
|
|
|
if (lseek(fs, off, SEEK_SET) != off) {
|
2012-10-21 12:01:11 +00:00
|
|
|
perr("Unable to read FAT");
|
2001-07-09 10:35:18 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2010-02-14 12:31:28 +00:00
|
|
|
if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec)
|
|
|
|
!= boot->FATsecs * boot->bpbBytesPerSec) {
|
2012-10-21 12:01:11 +00:00
|
|
|
perr("Unable to read FAT");
|
2001-07-09 10:35:18 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
err:
|
|
|
|
free(*buffer);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a FAT and decode it into internal format
|
|
|
|
*/
|
|
|
|
int
|
2010-02-14 12:30:30 +00:00
|
|
|
readfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
struct fatEntry *fat;
|
|
|
|
u_char *buffer, *p;
|
|
|
|
cl_t cl;
|
|
|
|
int ret = FSOK;
|
2010-02-14 12:30:30 +00:00
|
|
|
size_t len;
|
2001-07-09 10:35:18 +00:00
|
|
|
|
|
|
|
boot->NumFree = boot->NumBad = 0;
|
|
|
|
|
|
|
|
if (!_readfat(fs, boot, no, &buffer))
|
|
|
|
return FSFATAL;
|
2012-10-21 12:01:11 +00:00
|
|
|
|
2010-02-14 12:30:30 +00:00
|
|
|
fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry));
|
2001-07-09 10:35:18 +00:00
|
|
|
if (fat == NULL) {
|
2012-10-21 12:01:11 +00:00
|
|
|
perr("No space for FAT clusters (%zu)", len);
|
2001-07-09 10:35:18 +00:00
|
|
|
free(buffer);
|
|
|
|
return FSFATAL;
|
|
|
|
}
|
2010-02-14 12:30:30 +00:00
|
|
|
(void)memset(fat, 0, len);
|
2001-07-09 10:35:18 +00:00
|
|
|
|
2010-02-14 12:31:28 +00:00
|
|
|
if (buffer[0] != boot->bpbMedia
|
2001-07-09 10:35:18 +00:00
|
|
|
|| buffer[1] != 0xff || buffer[2] != 0xff
|
|
|
|
|| (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
|
|
|
|
|| (boot->ClustMask == CLUST32_MASK
|
|
|
|
&& ((buffer[3]&0x0f) != 0x0f
|
|
|
|
|| buffer[4] != 0xff || buffer[5] != 0xff
|
|
|
|
|| buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
|
|
|
|
|
|
|
|
/* Windows 95 OSR2 (and possibly any later) changes
|
|
|
|
* the FAT signature to 0xXXffff7f for FAT16 and to
|
|
|
|
* 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
|
2002-08-21 18:11:48 +00:00
|
|
|
* file system is dirty if it doesn't reboot cleanly.
|
2001-07-09 10:35:18 +00:00
|
|
|
* Check this special condition before errorring out.
|
|
|
|
*/
|
2010-02-14 12:31:28 +00:00
|
|
|
if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff
|
2001-07-09 10:35:18 +00:00
|
|
|
&& buffer[2] == 0xff
|
|
|
|
&& ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
|
|
|
|
|| (boot->ClustMask == CLUST32_MASK
|
|
|
|
&& buffer[3] == 0x0f && buffer[4] == 0xff
|
|
|
|
&& buffer[5] == 0xff && buffer[6] == 0xff
|
|
|
|
&& buffer[7] == 0x07)))
|
|
|
|
ret |= FSDIRTY;
|
|
|
|
else {
|
|
|
|
/* just some odd byte sequence in FAT */
|
2014-07-14 20:17:09 +00:00
|
|
|
|
2001-07-09 10:35:18 +00:00
|
|
|
switch (boot->ClustMask) {
|
|
|
|
case CLUST32_MASK:
|
|
|
|
pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
|
|
|
|
"FAT starts with odd byte sequence",
|
|
|
|
buffer[0], buffer[1], buffer[2], buffer[3],
|
|
|
|
buffer[4], buffer[5], buffer[6], buffer[7]);
|
|
|
|
break;
|
|
|
|
case CLUST16_MASK:
|
|
|
|
pwarn("%s (%02x%02x%02x%02x)\n",
|
|
|
|
"FAT starts with odd byte sequence",
|
|
|
|
buffer[0], buffer[1], buffer[2], buffer[3]);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pwarn("%s (%02x%02x%02x)\n",
|
|
|
|
"FAT starts with odd byte sequence",
|
|
|
|
buffer[0], buffer[1], buffer[2]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-07-14 20:17:09 +00:00
|
|
|
|
2001-07-09 10:35:18 +00:00
|
|
|
if (ask(1, "Correct"))
|
|
|
|
ret |= FSFIXFAT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (boot->ClustMask) {
|
|
|
|
case CLUST32_MASK:
|
|
|
|
p = buffer + 8;
|
|
|
|
break;
|
|
|
|
case CLUST16_MASK:
|
|
|
|
p = buffer + 4;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
p = buffer + 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
|
|
|
|
switch (boot->ClustMask) {
|
|
|
|
case CLUST32_MASK:
|
|
|
|
fat[cl].next = p[0] + (p[1] << 8)
|
|
|
|
+ (p[2] << 16) + (p[3] << 24);
|
|
|
|
fat[cl].next &= boot->ClustMask;
|
|
|
|
ret |= checkclnum(boot, no, cl, &fat[cl].next);
|
|
|
|
cl++;
|
|
|
|
p += 4;
|
|
|
|
break;
|
|
|
|
case CLUST16_MASK:
|
|
|
|
fat[cl].next = p[0] + (p[1] << 8);
|
|
|
|
ret |= checkclnum(boot, no, cl, &fat[cl].next);
|
|
|
|
cl++;
|
|
|
|
p += 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
|
|
|
|
ret |= checkclnum(boot, no, cl, &fat[cl].next);
|
|
|
|
cl++;
|
|
|
|
if (cl >= boot->NumClusters)
|
|
|
|
break;
|
|
|
|
fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
|
|
|
|
ret |= checkclnum(boot, no, cl, &fat[cl].next);
|
|
|
|
cl++;
|
|
|
|
p += 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(buffer);
|
2010-02-14 12:30:30 +00:00
|
|
|
if (ret & FSFATAL) {
|
|
|
|
free(fat);
|
|
|
|
*fp = NULL;
|
|
|
|
} else
|
|
|
|
*fp = fat;
|
2001-07-09 10:35:18 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get type of reserved cluster
|
|
|
|
*/
|
2012-10-21 12:01:19 +00:00
|
|
|
const char *
|
2002-03-20 22:57:10 +00:00
|
|
|
rsrvdcltype(cl_t cl)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
if (cl == CLUST_FREE)
|
|
|
|
return "free";
|
|
|
|
if (cl < CLUST_BAD)
|
|
|
|
return "reserved";
|
|
|
|
if (cl > CLUST_BAD)
|
|
|
|
return "as EOF";
|
|
|
|
return "bad";
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-02-14 12:30:30 +00:00
|
|
|
clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
|
|
|
|
if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
|
|
|
|
if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
|
|
|
|
&& *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
|
|
|
|
|| (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
|
2008-01-31 13:16:29 +00:00
|
|
|
pwarn("Cluster %u is marked %s with different indicators\n",
|
2001-07-09 10:35:18 +00:00
|
|
|
cl, rsrvdcltype(*cp1));
|
2008-01-31 13:16:29 +00:00
|
|
|
if (ask(1, "Fix")) {
|
2001-07-09 10:35:18 +00:00
|
|
|
*cp2 = *cp1;
|
|
|
|
return FSFATMOD;
|
|
|
|
}
|
|
|
|
return FSFATAL;
|
|
|
|
}
|
2010-02-14 12:30:30 +00:00
|
|
|
pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n",
|
2001-07-09 10:35:18 +00:00
|
|
|
cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
|
2008-01-31 13:22:13 +00:00
|
|
|
if (ask(0, "Use FAT 0's entry")) {
|
2001-07-09 10:35:18 +00:00
|
|
|
*cp2 = *cp1;
|
|
|
|
return FSFATMOD;
|
|
|
|
}
|
2010-02-14 12:30:30 +00:00
|
|
|
if (ask(0, "Use FAT %u's entry", fatnum)) {
|
2001-07-09 10:35:18 +00:00
|
|
|
*cp1 = *cp2;
|
|
|
|
return FSFATMOD;
|
|
|
|
}
|
|
|
|
return FSFATAL;
|
|
|
|
}
|
|
|
|
pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
|
|
|
|
cl, rsrvdcltype(*cp1), *cp2, fatnum);
|
2010-02-14 12:30:30 +00:00
|
|
|
if (ask(0, "Use continuation from FAT %u", fatnum)) {
|
2001-07-09 10:35:18 +00:00
|
|
|
*cp1 = *cp2;
|
|
|
|
return FSFATMOD;
|
|
|
|
}
|
|
|
|
if (ask(0, "Use mark from FAT 0")) {
|
|
|
|
*cp2 = *cp1;
|
|
|
|
return FSFATMOD;
|
|
|
|
}
|
|
|
|
return FSFATAL;
|
|
|
|
}
|
|
|
|
if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
|
2010-02-14 12:30:30 +00:00
|
|
|
pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n",
|
2001-07-09 10:35:18 +00:00
|
|
|
cl, *cp1, rsrvdcltype(*cp2), fatnum);
|
|
|
|
if (ask(0, "Use continuation from FAT 0")) {
|
|
|
|
*cp2 = *cp1;
|
|
|
|
return FSFATMOD;
|
|
|
|
}
|
|
|
|
if (ask(0, "Use mark from FAT %d", fatnum)) {
|
|
|
|
*cp1 = *cp2;
|
|
|
|
return FSFATMOD;
|
|
|
|
}
|
|
|
|
return FSERROR;
|
|
|
|
}
|
2010-02-14 12:30:30 +00:00
|
|
|
pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n",
|
2001-07-09 10:35:18 +00:00
|
|
|
cl, *cp1, *cp2, fatnum);
|
|
|
|
if (ask(0, "Use continuation from FAT 0")) {
|
|
|
|
*cp2 = *cp1;
|
|
|
|
return FSFATMOD;
|
|
|
|
}
|
2010-02-14 12:30:30 +00:00
|
|
|
if (ask(0, "Use continuation from FAT %u", fatnum)) {
|
2001-07-09 10:35:18 +00:00
|
|
|
*cp1 = *cp2;
|
|
|
|
return FSFATMOD;
|
|
|
|
}
|
|
|
|
return FSERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compare two FAT copies in memory. Resolve any conflicts and merge them
|
|
|
|
* into the first one.
|
|
|
|
*/
|
|
|
|
int
|
2010-02-14 12:30:30 +00:00
|
|
|
comparefat(struct bootblock *boot, struct fatEntry *first,
|
|
|
|
struct fatEntry *second, u_int fatnum)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
cl_t cl;
|
|
|
|
int ret = FSOK;
|
|
|
|
|
|
|
|
for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
|
|
|
|
if (first[cl].next != second[cl].next)
|
|
|
|
ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2002-03-20 22:57:10 +00:00
|
|
|
clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
cl_t p, q;
|
|
|
|
|
|
|
|
for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
|
|
|
|
if (fat[p].head != head)
|
|
|
|
break;
|
|
|
|
q = fat[p].next;
|
|
|
|
fat[p].next = fat[p].head = CLUST_FREE;
|
|
|
|
fat[p].length = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2012-10-21 12:01:11 +00:00
|
|
|
tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
if (ask(0, "Clear chain starting at %u", head)) {
|
|
|
|
clearchain(boot, fat, head);
|
|
|
|
return FSFATMOD;
|
|
|
|
} else if (ask(0, "Truncate")) {
|
2012-10-21 12:01:11 +00:00
|
|
|
*truncp = CLUST_EOF;
|
2001-07-09 10:35:18 +00:00
|
|
|
return FSFATMOD;
|
|
|
|
} else
|
|
|
|
return FSERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check a complete FAT in-memory for crosslinks
|
|
|
|
*/
|
|
|
|
int
|
2002-03-20 22:57:10 +00:00
|
|
|
checkfat(struct bootblock *boot, struct fatEntry *fat)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
cl_t head, p, h, n;
|
|
|
|
u_int len;
|
|
|
|
int ret = 0;
|
|
|
|
int conf;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pass 1: figure out the cluster chains.
|
|
|
|
*/
|
|
|
|
for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
|
|
|
|
/* find next untravelled chain */
|
|
|
|
if (fat[head].head != 0 /* cluster already belongs to some chain */
|
|
|
|
|| fat[head].next == CLUST_FREE
|
|
|
|
|| fat[head].next == CLUST_BAD)
|
|
|
|
continue; /* skip it. */
|
|
|
|
|
|
|
|
/* follow the chain and mark all clusters on the way */
|
|
|
|
for (len = 0, p = head;
|
|
|
|
p >= CLUST_FIRST && p < boot->NumClusters;
|
|
|
|
p = fat[p].next) {
|
|
|
|
fat[p].head = head;
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the head record gets the length */
|
|
|
|
fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
|
|
|
|
* we didn't know the real start of the chain then - would have treated partial
|
|
|
|
* chains as interlinked with their main chain)
|
|
|
|
*/
|
|
|
|
for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
|
|
|
|
/* find next untravelled chain */
|
|
|
|
if (fat[head].head != head)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* follow the chain to its end (hopefully) */
|
|
|
|
for (p = head;
|
|
|
|
(n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
|
|
|
|
p = n)
|
|
|
|
if (fat[n].head != head)
|
|
|
|
break;
|
|
|
|
if (n >= CLUST_EOFS)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (n == CLUST_FREE || n >= CLUST_RSRVD) {
|
|
|
|
pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
|
|
|
|
head, rsrvdcltype(n));
|
|
|
|
ret |= tryclear(boot, fat, head, &fat[p].next);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (n < CLUST_FIRST || n >= boot->NumClusters) {
|
|
|
|
pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
|
|
|
|
head, n);
|
|
|
|
ret |= tryclear(boot, fat, head, &fat[p].next);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
|
|
|
|
head, fat[n].head, n);
|
|
|
|
conf = tryclear(boot, fat, head, &fat[p].next);
|
|
|
|
if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
|
|
|
|
if (conf == FSERROR) {
|
|
|
|
/*
|
|
|
|
* Transfer the common chain to the one not cleared above.
|
|
|
|
*/
|
|
|
|
for (p = n;
|
|
|
|
p >= CLUST_FIRST && p < boot->NumClusters;
|
|
|
|
p = fat[p].next) {
|
|
|
|
if (h != fat[p].head) {
|
|
|
|
/*
|
|
|
|
* Have to reexamine this chain.
|
|
|
|
*/
|
|
|
|
head--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
fat[p].head = head;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
clearchain(boot, fat, h);
|
|
|
|
conf |= FSFATMOD;
|
|
|
|
}
|
|
|
|
ret |= conf;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write out FATs encoding them from the internal format
|
|
|
|
*/
|
|
|
|
int
|
2002-03-20 22:57:10 +00:00
|
|
|
writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
u_char *buffer, *p;
|
|
|
|
cl_t cl;
|
2010-02-14 12:30:30 +00:00
|
|
|
u_int i;
|
|
|
|
size_t fatsz;
|
2001-07-09 10:35:18 +00:00
|
|
|
off_t off;
|
|
|
|
int ret = FSOK;
|
|
|
|
|
2010-02-14 12:31:28 +00:00
|
|
|
buffer = malloc(fatsz = boot->FATsecs * boot->bpbBytesPerSec);
|
2001-07-09 10:35:18 +00:00
|
|
|
if (buffer == NULL) {
|
2012-10-21 12:01:11 +00:00
|
|
|
perr("No space for FAT sectors (%zu)", fatsz);
|
2001-07-09 10:35:18 +00:00
|
|
|
return FSFATAL;
|
|
|
|
}
|
|
|
|
memset(buffer, 0, fatsz);
|
|
|
|
boot->NumFree = 0;
|
|
|
|
p = buffer;
|
|
|
|
if (correct_fat) {
|
2010-02-14 12:31:28 +00:00
|
|
|
*p++ = (u_char)boot->bpbMedia;
|
2001-07-09 10:35:18 +00:00
|
|
|
*p++ = 0xff;
|
|
|
|
*p++ = 0xff;
|
|
|
|
switch (boot->ClustMask) {
|
|
|
|
case CLUST16_MASK:
|
|
|
|
*p++ = 0xff;
|
|
|
|
break;
|
|
|
|
case CLUST32_MASK:
|
|
|
|
*p++ = 0x0f;
|
|
|
|
*p++ = 0xff;
|
|
|
|
*p++ = 0xff;
|
|
|
|
*p++ = 0xff;
|
|
|
|
*p++ = 0x0f;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* use same FAT signature as the old FAT has */
|
|
|
|
int count;
|
|
|
|
u_char *old_fat;
|
|
|
|
|
|
|
|
switch (boot->ClustMask) {
|
|
|
|
case CLUST32_MASK:
|
|
|
|
count = 8;
|
|
|
|
break;
|
|
|
|
case CLUST16_MASK:
|
|
|
|
count = 4;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
count = 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
|
|
|
|
&old_fat)) {
|
|
|
|
free(buffer);
|
|
|
|
return FSFATAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(p, old_fat, count);
|
|
|
|
free(old_fat);
|
|
|
|
p += count;
|
|
|
|
}
|
2012-10-21 12:01:11 +00:00
|
|
|
|
2001-07-09 10:35:18 +00:00
|
|
|
for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
|
|
|
|
switch (boot->ClustMask) {
|
|
|
|
case CLUST32_MASK:
|
|
|
|
if (fat[cl].next == CLUST_FREE)
|
|
|
|
boot->NumFree++;
|
|
|
|
*p++ = (u_char)fat[cl].next;
|
|
|
|
*p++ = (u_char)(fat[cl].next >> 8);
|
|
|
|
*p++ = (u_char)(fat[cl].next >> 16);
|
|
|
|
*p &= 0xf0;
|
|
|
|
*p++ |= (fat[cl].next >> 24)&0x0f;
|
|
|
|
break;
|
|
|
|
case CLUST16_MASK:
|
|
|
|
if (fat[cl].next == CLUST_FREE)
|
|
|
|
boot->NumFree++;
|
|
|
|
*p++ = (u_char)fat[cl].next;
|
|
|
|
*p++ = (u_char)(fat[cl].next >> 8);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (fat[cl].next == CLUST_FREE)
|
|
|
|
boot->NumFree++;
|
|
|
|
if (cl + 1 < boot->NumClusters
|
|
|
|
&& fat[cl + 1].next == CLUST_FREE)
|
|
|
|
boot->NumFree++;
|
|
|
|
*p++ = (u_char)fat[cl].next;
|
|
|
|
*p++ = (u_char)((fat[cl].next >> 8) & 0xf)
|
|
|
|
|(u_char)(fat[cl+1].next << 4);
|
|
|
|
*p++ = (u_char)(fat[++cl].next >> 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-02-14 12:31:28 +00:00
|
|
|
for (i = 0; i < boot->bpbFATs; i++) {
|
|
|
|
off = boot->bpbResSectors + i * boot->FATsecs;
|
|
|
|
off *= boot->bpbBytesPerSec;
|
2001-07-09 10:35:18 +00:00
|
|
|
if (lseek(fs, off, SEEK_SET) != off
|
2010-02-14 12:30:30 +00:00
|
|
|
|| (size_t)write(fs, buffer, fatsz) != fatsz) {
|
2012-10-21 12:01:11 +00:00
|
|
|
perr("Unable to write FAT");
|
2001-07-09 10:35:18 +00:00
|
|
|
ret = FSFATAL; /* Return immediately? XXX */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(buffer);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check a complete in-memory FAT for lost cluster chains
|
|
|
|
*/
|
|
|
|
int
|
2002-03-20 22:57:10 +00:00
|
|
|
checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
|
2001-07-09 10:35:18 +00:00
|
|
|
{
|
|
|
|
cl_t head;
|
|
|
|
int mod = FSOK;
|
|
|
|
int ret;
|
2014-07-14 20:17:09 +00:00
|
|
|
|
2001-07-09 10:35:18 +00:00
|
|
|
for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
|
|
|
|
/* find next untravelled chain */
|
|
|
|
if (fat[head].head != head
|
|
|
|
|| fat[head].next == CLUST_FREE
|
|
|
|
|| (fat[head].next >= CLUST_RSRVD
|
|
|
|
&& fat[head].next < CLUST_EOFS)
|
|
|
|
|| (fat[head].flags & FAT_USED))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
|
|
|
|
head, fat[head].length);
|
|
|
|
mod |= ret = reconnect(dosfs, boot, fat, head);
|
|
|
|
if (mod & FSFATAL)
|
|
|
|
break;
|
|
|
|
if (ret == FSERROR && ask(0, "Clear")) {
|
|
|
|
clearchain(boot, fat, head);
|
|
|
|
mod |= FSFATMOD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finishlf();
|
|
|
|
|
2010-02-14 12:31:28 +00:00
|
|
|
if (boot->bpbFSInfo) {
|
2001-07-09 10:35:18 +00:00
|
|
|
ret = 0;
|
2014-07-14 20:17:09 +00:00
|
|
|
if (boot->FSFree != 0xffffffffU &&
|
|
|
|
boot->FSFree != boot->NumFree) {
|
|
|
|
pwarn("Free space in FSInfo block (%u) not correct (%u)\n",
|
2001-07-09 10:35:18 +00:00
|
|
|
boot->FSFree, boot->NumFree);
|
2008-01-31 13:16:29 +00:00
|
|
|
if (ask(1, "Fix")) {
|
2001-07-09 10:35:18 +00:00
|
|
|
boot->FSFree = boot->NumFree;
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret)
|
|
|
|
mod |= writefsinfo(dosfs, boot);
|
|
|
|
}
|
|
|
|
|
|
|
|
return mod;
|
|
|
|
}
|