Add fsck utility for msdosfs

This commit is contained in:
ws 1996-05-14 17:39:21 +00:00
parent 16b514adc6
commit 6ae4c91a82
10 changed files with 2200 additions and 2 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.28 1996/04/05 01:44:24 cgd Exp $
# $NetBSD: Makefile,v 1.29 1996/05/14 17:39:21 ws Exp $
# @(#)Makefile 8.5 (Berkeley) 3/31/94
# Not ported: XNSrouted enpload scsiformat startslip
@ -16,7 +16,7 @@ SUBDIR+= mount_ffs newfs fsck_ffs fsdb dumpfs dump restore clri tunefs
SUBDIR+= mount_kernfs
SUBDIR+= mount_lfs newlfs dumplfs
# mount_mfs -> newfs
SUBDIR+= mount_msdos
SUBDIR+= mount_msdos fsck_msdos
SUBDIR+= mount_nfs
SUBDIR+= mount_null
SUBDIR+= mount_portal

7
sbin/fsck_msdos/Makefile Normal file
View File

@ -0,0 +1,7 @@
# $NetBSD: Makefile,v 1.1 1996/05/14 17:39:26 ws Exp $
PROG= fsck_msdos
MAN= fsck_msdos.8
SRCS= main.c check.c boot.c fat.c dir.c
.include <bsd.prog.mk>

117
sbin/fsck_msdos/boot.c Normal file
View File

@ -0,0 +1,117 @@
/* $NetBSD: boot.c,v 1.1 1996/05/14 17:39:28 ws Exp $ */
/*
* Copyright (C) 1995 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Martin Husemann
* and Wolfgang Solfrank.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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.
*/
#ifndef lint
static char rcsid[] = "$NetBSD: boot.c,v 1.1 1996/05/14 17:39:28 ws Exp $";
#endif /* not lint */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include "ext.h"
int
readboot(dosfs, boot)
int dosfs;
struct bootblock *boot;
{
u_char block[DOSBOOTBLOCKSIZE];
int n;
if ((n = read(dosfs, block, sizeof block)) < (int)sizeof block) {
if (n < 0)
perror("could not read boot block");
else
pfatal("Short bootblock?");
return FSFATAL;
}
/* decode bios parameter block */
boot->BytesPerSec = block[11] + (block[12] << 8);
boot->SecPerClust = block[13];
boot->ResSectors = block[14] + (block[15] << 8);
boot->FATs = block[16];
boot->RootDirEnts = block[17] + (block[18] << 8);
boot->Sectors = block[19] + (block[20] << 8);
boot->Media = block[21];
boot->FATsecs = block[22] + (block[23] << 8);
boot->SecPerTrack = block[24] + (block[25] << 8);
boot->Heads = block[26] + (block[27] << 8);
boot->HiddenSecs = block[28] + (block[29] << 8) + (block[30] << 16) + (block[31] << 24);
boot->HugeSectors = block[32] + (block[33] << 8) + (block[34] << 16) + (block[35] << 24);
boot->ClusterOffset = (boot->RootDirEnts * 32 + boot->BytesPerSec - 1)
/ boot->BytesPerSec
+ boot->ResSectors
+ boot->FATs * boot->FATsecs
- CLUST_FIRST * boot->SecPerClust;
if (boot->BytesPerSec % DOSBOOTBLOCKSIZE != 0) {
pfatal("Invalid sector size: %u\n", boot->BytesPerSec);
return FSFATAL;
}
if (boot->SecPerClust == 0) {
pfatal("Invalid cluster size: %u\n", boot->SecPerClust);
return FSFATAL;
}
if (boot->Sectors) {
boot->HugeSectors = 0;
boot->NumSectors = boot->Sectors;
} else
boot->NumSectors = boot->HugeSectors;
boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) / boot->SecPerClust;
if (boot->NumClusters >= MAX12BITCLUSTERS)
boot->Is16BitFat = 1;
else
boot->Is16BitFat = 0;
if (boot->Is16BitFat)
boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 2;
else
boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec * 2) / 3;
if (boot->NumFatEntries < boot->NumClusters) {
pfatal("FAT size too small, %d entries won't fit into %u sectors\n",
boot->NumClusters, boot->FATsecs);
return FSFATAL;
}
boot->ClusterSize = boot->BytesPerSec * boot->SecPerClust;
boot->NumFiles = 1;
boot->NumFree = 0;
return FSOK;
}

196
sbin/fsck_msdos/check.c Normal file
View File

@ -0,0 +1,196 @@
/* $NetBSD: check.c,v 1.1 1996/05/14 17:39:29 ws Exp $ */
/*
* Copyright (C) 1995, 1996 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Martin Husemann
* and Wolfgang Solfrank.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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.
*/
#ifndef lint
static char rcsid[] = "$NetBSD: check.c,v 1.1 1996/05/14 17:39:29 ws Exp $";
#endif /* not lint */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include "ext.h"
int
checkfilesys(fname)
const char *fname;
{
int dosfs;
struct dosDirEntry *rootDir;
struct bootblock boot;
struct fatEntry * fat = NULL;
int i;
int mod = 0;
rdonly = alwaysno;
if (!preen)
printf("** %s", fname);
dosfs = open(fname, rdonly ? O_RDONLY : O_RDWR, 0);
if (dosfs < 0 && !rdonly) {
dosfs = open(fname, O_RDONLY, 0);
if (dosfs >= 0)
pwarn(" (NO WRITE)\n");
else if (!preen)
printf("\n");
rdonly = 1;
} else if (!preen)
printf("\n");
if (dosfs < 0) {
perror("Can't open");
return 8;
}
if (readboot(dosfs, &boot) != FSOK) {
close(dosfs);
return 8;
}
if (!preen)
printf("** Phase 1 - Read and Compare FATs\n");
for (i = 0; i < boot.FATs; i++) {
struct fatEntry *currentFat;
mod |= readfat(dosfs, &boot, i, &currentFat);
if (mod&FSFATAL) {
close(dosfs);
return 8;
}
if (fat == NULL)
fat = currentFat;
else {
mod |= comparefat(&boot, fat, currentFat, i + 1);
if (mod&FSFATAL) {
close(dosfs);
return 8;
}
}
}
if (!preen)
printf("** Phase 2 - Check Cluster Chains\n");
mod |= checkfat(&boot, fat);
if (mod&FSFATAL) {
close(dosfs);
return 8;
}
if (mod&FSFATMOD)
mod |= writefat(dosfs, &boot, fat); /* delay writing fats? XXX */
if (mod&FSFATAL) {
close(dosfs);
return 8;
}
if (!preen)
printf("** Phase 3 - Checking Directories\n");
rootDir = malloc(sizeof(struct dosDirEntry));
memset(rootDir, 0, sizeof(struct dosDirEntry));
rootDir->fullpath = strdup("/");
if (resetDosDirSection(&boot)&FSFATAL) {
close(dosfs);
return 8;
}
mod = readDosDirSection(dosfs, &boot, fat, rootDir);
if (mod&FSFATAL) {
close(dosfs);
return 8;
}
if (mod&FSFATMOD)
mod |= writefat(dosfs, &boot, fat); /* delay writing fats? XXX */
if (mod&FSFATAL) {
close(dosfs);
return 8;
}
/*
* process the directory todo list
*/
while (pendingDirectories) {
struct dosDirEntry *dir = pendingDirectories->dir;
struct dirTodoNode *n = pendingDirectories->next;
/*
* remove TODO entry now, the list might change during
* directory reads
*/
free(pendingDirectories);
pendingDirectories = n;
/*
* handle subdirectory
*/
mod |= readDosDirSection(dosfs, &boot, fat, dir);
if (mod&FSFATAL) {
close(dosfs);
return 8;
}
if (mod&FSFATMOD)
mod |= writefat(dosfs, &boot, fat); /* delay writing fats? XXX */
if (mod&FSFATAL) {
close(dosfs);
return 8;
}
}
finishDosDirSection();
if (!preen)
printf("** Phase 4 - Checking for Lost Files\n");
mod |= checklost(dosfs, &boot, fat, rootDir);
close(dosfs);
pwarn("%d files, %d free (%d clusters)\n",
boot.NumFiles, boot.NumFree * boot.ClusterSize / 1024,
boot.NumFree);
if (mod&(FSFATAL|FSERROR))
return 8;
if (mod) {
pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n");
return 4;
}
return 0;
}

760
sbin/fsck_msdos/dir.c Normal file
View File

@ -0,0 +1,760 @@
/* $NetBSD: dir.c,v 1.1 1996/05/14 17:39:30 ws Exp $ */
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank
* Copyright (c) 1995 Martin Husemann
* Some structure declaration borrowed from Paul Popelka
* (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
*
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Martin Husemann
* and Wolfgang Solfrank.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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.
*/
#ifndef lint
static char rcsid[] = "$NetBSD: dir.c,v 1.1 1996/05/14 17:39:30 ws Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include "ext.h"
#define SLOT_EMPTY 0x00 /* slot has never been used */
#define SLOT_E5 0x05 /* the real value is 0xe5 */
#define SLOT_DELETED 0xe5 /* file in this slot deleted */
#define ATTR_NORMAL 0x00 /* normal file */
#define ATTR_READONLY 0x01 /* file is readonly */
#define ATTR_HIDDEN 0x02 /* file is hidden */
#define ATTR_SYSTEM 0x04 /* file is a system file */
#define ATTR_VOLUME 0x08 /* entry is a volume label */
#define ATTR_DIRECTORY 0x10 /* entry is a directory name */
#define ATTR_ARCHIVE 0x20 /* file is new or modified */
#define ATTR_WIN95 0x0f /* long name record */
/*
* This is the format of the contents of the deTime field in the direntry
* structure.
* We don't use bitfields because we don't know how compilers for
* arbitrary machines will lay them out.
*/
#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
#define DT_2SECONDS_SHIFT 0
#define DT_MINUTES_MASK 0x7E0 /* minutes */
#define DT_MINUTES_SHIFT 5
#define DT_HOURS_MASK 0xF800 /* hours */
#define DT_HOURS_SHIFT 11
/*
* This is the format of the contents of the deDate field in the direntry
* structure.
*/
#define DD_DAY_MASK 0x1F /* day of month */
#define DD_DAY_SHIFT 0
#define DD_MONTH_MASK 0x1E0 /* month */
#define DD_MONTH_SHIFT 5
#define DD_YEAR_MASK 0xFE00 /* year - 1980 */
#define DD_YEAR_SHIFT 9
/*
* Calculate a checksum over an 8.3 alias name
*/
static u_char
calcShortSum(p)
u_char *p;
{
u_char sum = 0;
int i;
for (i = 0; i < 11; i++) {
sum = (sum << 7)|(sum >> 1); /* rotate right */
sum += p[i];
}
return sum;
}
/*
* Global variables temporarily used during a directory scan
*/
static char longName[DOSLONGNAMELEN] = "";
static u_char *buffer = NULL;
static u_char *delbuf = NULL;
/*
* Init internal state for a new directory scan.
*/
int
resetDosDirSection(boot)
struct bootblock *boot;
{
int b1, b2;
b1 = boot->RootDirEnts * 32;
b2 = boot->SecPerClust * boot->BytesPerSec;
if (!(buffer = malloc(b1 > b2 ? b1 : b2))
|| !(delbuf = malloc(b2))) {
perror("No space for directory");
return FSFATAL;
}
return FSOK;
}
/*
* Cleanup after a directory scan
*/
void
finishDosDirSection()
{
free(buffer);
free(delbuf);
buffer = NULL;
delbuf = NULL;
}
/*
* Delete directory entries between startcl, startoff and endcl, endoff.
*/
static int
delete(f, boot, fat, startcl, startoff, endcl, endoff, notlast)
int f;
struct bootblock *boot;
struct fatEntry *fat;
cl_t startcl;
int startoff;
cl_t endcl;
int endoff;
int notlast;
{
u_char *s, *e;
off_t off;
int clsz = boot->SecPerClust * boot->BytesPerSec;
s = delbuf + startoff;
e = delbuf + clsz;
while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) {
if (startcl == endcl) {
if (notlast)
break;
e = delbuf + endoff;
}
off = startcl * boot->SecPerClust + boot->ClusterOffset;
off *= boot->BytesPerSec;
if (lseek(f, off, SEEK_SET) != off
|| read(f, delbuf, clsz) != clsz) {
perror("Unable to read directory");
return FSFATAL;
}
while (s < e) {
*s = SLOT_DELETED;
s += 32;
}
if (lseek(f, off, SEEK_SET) != off
|| write(f, delbuf, clsz) != clsz) {
perror("Unable to write directory");
return FSFATAL;
}
if (startcl == endcl)
break;
startcl = fat[startcl].next;
s = delbuf;
}
return FSOK;
}
static int
removede(f, boot, fat, start, end, startcl, endcl, curcl, path, eof)
int f;
struct bootblock *boot;
struct fatEntry *fat;
u_char *start;
u_char *end;
cl_t startcl;
cl_t endcl;
cl_t curcl;
char *path;
int eof;
{
if (!eof)
pwarn("Invalid long filename entry for %s\n", path);
else
pwarn("Invalid long filename entry at end of directory %s\n", path);
if (ask(0, "Remove")) {
if (startcl != curcl) {
if (delete(f, boot, fat,
startcl, start - buffer,
endcl, end - buffer,
endcl == curcl) == FSFATAL)
return FSFATAL;
start = buffer;
}
if (endcl == curcl)
for (; start < end; start += 32)
*start = SLOT_DELETED;
return FSDIRMOD;
}
return FSERROR;
}
/*
* Check an in-memory file entry
*/
static int
checksize(boot, fat, p, dir)
struct bootblock *boot;
struct fatEntry *fat;
u_char *p;
struct dosDirEntry *dir;
{
/*
* Check size on ordinary files
*/
u_int32_t physicalSize;
if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters)
return FSERROR;
physicalSize = fat[dir->head].length * boot->ClusterSize;
if (physicalSize < dir->size) {
pwarn("size of %s is %lu, should at most be %lu\n",
dir->fullpath, dir->size, physicalSize);
if (ask(1, "Truncate")) {
dir->size = physicalSize;
p[28] = (u_char)physicalSize;
p[29] = (u_char)(physicalSize >> 8);
p[30] = (u_char)(physicalSize >> 16);
p[31] = (u_char)(physicalSize >> 24);
return FSDIRMOD;
} else
return FSERROR;
} else if (physicalSize - dir->size >= boot->ClusterSize) {
pwarn("%s has too many clusters allocated\n",
dir->fullpath);
if (ask(1, "Drop superfluous clusters")) {
cl_t cl;
u_int32_t sz = 0;
for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;)
cl = fat[cl].next;
clearchain(boot, fat, fat[cl].next);
fat[cl].next = CLUST_EOF;
return FSFATMOD;
} else
return FSERROR;
}
return FSOK;
}
/*
* The stack of unread directories
*/
struct dirTodoNode *pendingDirectories = NULL;
/*
* Read a directory and
* - resolve long name records
* - enter file and directory records into the parent's list
* - push directories onto the todo-stack
*/
int
readDosDirSection(f, boot, fat, dir)
int f;
struct bootblock *boot;
struct fatEntry *fat;
struct dosDirEntry *dir;
{
struct dosDirEntry dirent, *d;
u_char *p, *vallfn, *invlfn, *empty;
off_t off;
int i, j, k, last;
cl_t cl, valcl, invcl, empcl;
char *t;
u_int lidx = 0;
int shortSum;
int mod = FSOK;
#define THISMOD 0x8000 /* Only used within this routine */
cl = dir->head;
if (dir->fullpath[1] && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
/*
* Already handled somewhere else.
*/
return FSOK;
}
shortSum = -1;
vallfn = invlfn = empty = NULL;
do {
if (!dir->fullpath[1]) {
last = boot->RootDirEnts * 32;
off = boot->ResSectors + boot->FATs * boot->FATsecs;
} else {
last = boot->SecPerClust * boot->BytesPerSec;
off = cl * boot->SecPerClust + boot->ClusterOffset;
}
off *= boot->BytesPerSec;
if (lseek(f, off, SEEK_SET) != off
|| read(f, buffer, last) != last) {
perror("Unable to read directory");
return FSFATAL;
}
last /= 32;
/*
* Check `.' and `..' entries here? XXX
*/
for (p = buffer, i = 0; i < last; i++, p += 32) {
if (dir->fsckflags & DIREMPWARN) {
*p = SLOT_EMPTY;
continue;
}
if (*p == SLOT_EMPTY || *p == SLOT_DELETED) {
if (*p == SLOT_EMPTY) {
dir->fsckflags |= DIREMPTY;
empty = p;
empcl = cl;
}
continue;
}
if (dir->fsckflags & DIREMPTY) {
if (!(dir->fsckflags & DIREMPWARN)) {
pwarn("%s has entries after end of directory\n",
dir->fullpath);
if (ask(1, "Extend")) {
dir->fsckflags &= ~DIREMPTY;
if (delete(f, boot, fat,
empcl, empty - buffer,
cl, p - buffer) == FSFATAL)
return FSFATAL;
} else if (ask(0, "Truncate"))
dir->fsckflags |= DIREMPWARN;
}
if (dir->fsckflags & DIREMPWARN) {
*p = SLOT_DELETED;
mod |= THISMOD|FSDIRMOD;
continue;
} else if (dir->fsckflags & DIREMPTY)
mod |= FSERROR;
empty = NULL;
}
if (p[11] == ATTR_WIN95) {
if (*p & LRFIRST) {
if (shortSum != -1) {
if (!invlfn) {
invlfn = vallfn;
invcl = valcl;
}
}
memset(longName, 0, sizeof longName);
shortSum = p[13];
vallfn = p;
valcl = cl;
} else if (shortSum != p[13]
|| lidx != *p & LRNOMASK) {
if (!invlfn) {
invlfn = vallfn;
invcl = valcl;
}
if (!invlfn) {
invlfn = p;
invcl = cl;
}
vallfn = NULL;
}
lidx = *p & LRNOMASK;
t = longName + --lidx * 13;
for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) {
if (!p[k] && !p[k + 1])
break;
*t++ = p[k];
/*
* Warn about those unusable chars in msdosfs here? XXX
*/
if (p[k + 1])
t[-1] = '?';
}
if (k >= 11)
for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) {
if (!p[k] && !p[k + 1])
break;
*t++ = p[k];
if (p[k + 1])
t[-1] = '?';
}
if (k >= 26)
for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) {
if (!p[k] && !p[k + 1])
break;
*t++ = p[k];
if (p[k + 1])
t[-1] = '?';
}
if (t >= longName + sizeof(longName)) {
pwarn("long filename too long\n");
if (!invlfn) {
invlfn = vallfn;
invcl = valcl;
}
vallfn = NULL;
}
if (p[26] | (p[27] << 8)) {
pwarn("long filename record cluster start != 0\n");
if (!invlfn) {
invlfn = vallfn;
invcl = cl;
}
vallfn = NULL;
}
continue; /* long records don't carry further
* information */
}
/*
* This is a standard msdosfs directory entry.
*/
memset(&dirent, 0, sizeof dirent);
/*
* it's a short name record, but we need to know
* more, so get the flags first.
*/
dirent.flags = p[11];
/*
* Translate from 850 to ISO here XXX
*/
for (j = 0; j < 8; j++)
dirent.name[j] = p[j];
dirent.name[8] = '\0';
for (k = 7; k >= 0 && dirent.name[k] == ' '; k--)
dirent.name[k] = '\0';
if (dirent.name[k] != '\0')
k++;
if (dirent.name[0] == SLOT_E5)
dirent.name[0] = 0xe5;
/*
* What about volume names with extensions? XXX
*/
if ((dirent.flags & ATTR_VOLUME) == 0 && p[8] != ' ')
dirent.name[k++] = '.';
for (j = 0; j < 3; j++)
dirent.name[k++] = p[j+8];
dirent.name[k] = '\0';
for (k--; k >= 0 && dirent.name[k] == ' '; k--)
dirent.name[k] = '\0';
if (vallfn && shortSum != calcShortSum(p)) {
if (!invlfn) {
invlfn = vallfn;
invcl = valcl;
}
vallfn = NULL;
}
dirent.head = p[26] | (p[27] << 8);
dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24);
if (vallfn) {
strcpy(dirent.lname, longName);
longName[0] = '\0';
shortSum = -1;
}
k = strlen(dirent.lname[0] ? dirent.lname : dirent.name);
k += strlen(dir->fullpath) + 2;
dirent.fullpath = malloc(k);
strcpy(dirent.fullpath, dir->fullpath);
if (dir->fullpath[1])
strcat(dirent.fullpath, "/");
strcat(dirent.fullpath,
dirent.lname[0] ? dirent.lname : dirent.name);
if (invlfn) {
mod |= k = removede(f, boot, fat,
invlfn, vallfn ? vallfn : p,
invcl, vallfn ? valcl : cl, cl,
dirent.fullpath, 0);
if (mod & FSFATAL)
return FSFATAL;
if (vallfn
? (valcl == cl && vallfn != buffer)
: p != buffer)
if (k & FSDIRMOD)
mod |= THISMOD;
}
vallfn = NULL; /* not used any longer */
invlfn = NULL;
if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) {
if (dirent.head != 0) {
pwarn("%s has clusters, but size 0\n",
dirent.fullpath);
if (ask(1, "Drop allocated clusters")) {
p[26] = p[27] = 0;
clearchain(boot, fat, dirent.head);
dirent.head = 0;
mod |= THISMOD|FSDIRMOD|FSFATMOD;
} else
mod |= FSERROR;
}
} else if (dirent.head == 0
&& !strcmp(dirent.name, "..")
&& !strcmp(dir->parent->fullpath, "/")) {
/*
* Do nothing, the parent is the root
*/
} else if (dirent.head < CLUST_FIRST
|| dirent.head >= boot->NumClusters
|| fat[dirent.head].next == CLUST_FREE
|| (fat[dirent.head].next >= CLUST_RSRVD
&& fat[dirent.head].next < CLUST_EOFS)
|| fat[dirent.head].head != dirent.head) {
if (dirent.head == 0)
pwarn("%s has no clusters\n",
dirent.fullpath);
else if (dirent.head < CLUST_FIRST
|| dirent.head >= boot->NumClusters)
pwarn("%s starts with cluster out of range(%d)\n",
dirent.fullpath,
dirent.head);
else if (fat[dirent.head].next == CLUST_FREE)
pwarn("%s starts with free cluster\n",
dirent.fullpath);
else if (fat[dirent.head].next >= CLUST_RSRVD)
pwarn("%s starts with %s cluster\n",
dirent.fullpath,
rsrvdcltype(fat[dirent.head].next));
else
pwarn("%s doesn't start a cluster chain\n",
dirent.fullpath);
if (dirent.flags & ATTR_DIRECTORY) {
if (ask(0, "Remove")) {
*p = SLOT_DELETED;
mod |= THISMOD|FSDIRMOD;
} else
mod |= FSERROR;
continue;
} else {
if (ask(1, "Truncate")) {
p[28] = p[29] = p[30] = p[31] = 0;
dirent.size = 0;
mod |= THISMOD|FSDIRMOD;
} else
mod |= FSERROR;
}
}
/* create directory tree node */
d = malloc(sizeof(struct dosDirEntry));
memcpy(d, &dirent, sizeof(struct dosDirEntry));
/* link it into the directory tree */
d->parent = dir;
d->next = dir->child;
dir->child = d;
if (d->head >= CLUST_FIRST && d->head < boot->NumClusters)
fat[d->head].dirp = d;
if (d->flags & ATTR_DIRECTORY) {
/*
* gather more info for directories
*/
struct dirTodoNode * n;
if (d->size) {
pwarn("Directory %s has size != 0\n",
d->fullpath);
if (ask(1, "Correct")) {
p[28] = p[29] = p[30] = p[31] = 0;
d->size = 0;
mod |= THISMOD|FSDIRMOD;
} else
mod |= FSERROR;
}
/*
* handle `.' and `..' specially
*/
if (strcmp(d->name, ".") == 0) {
if (d->head != dir->head) {
pwarn("`.' entry in %s has incorrect start cluster\n",
dir->fullpath);
if (ask(1, "Correct")) {
d->head = dir->head;
p[26] = (u_char)d->head;
p[27] = (u_char)(d->head >> 8);
mod |= THISMOD|FSDIRMOD;
} else
mod |= FSERROR;
}
continue;
}
if (strcmp(d->name, "..") == 0) {
if (d->head != dir->parent->head) {
pwarn("`..' entry in %s has incorrect start cluster\n",
dir->fullpath);
if (ask(1, "Correct")) {
d->head = dir->parent->head;
p[26] = (u_char)d->head;
p[27] = (u_char)(d->head >> 8);
mod |= THISMOD|FSDIRMOD;
} else
mod |= FSERROR;
}
continue;
}
boot->NumFiles++;
/* Enter this directory into the todo list */
n = malloc(sizeof(struct dirTodoNode));
n->next = pendingDirectories;
n->dir = d;
pendingDirectories = n;
} else {
mod |= k = checksize(boot, fat, p, d);
if (k & FSDIRMOD)
mod |= THISMOD;
boot->NumFiles++;
}
}
if (mod & THISMOD) {
last *= 32;
if (lseek(f, off, SEEK_SET) != off
|| write(f, buffer, last) != last) {
perror("Unable to write directory");
return FSFATAL;
}
mod &= ~THISMOD;
}
} while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
if (invlfn || vallfn)
mod |= removede(f, boot, fat,
invlfn ? invlfn : vallfn, p,
invlfn ? invcl : valcl, -1, 0,
dir->fullpath, 1);
return mod & ~THISMOD;
}
/*
* Try to reconnect a FAT chain into dir
*/
static u_char *lfbuf;
static cl_t lfcl;
static off_t lfoff;
int
reconnect(dosfs, boot, fat, head, dir)
int dosfs;
struct bootblock *boot;
struct fatEntry *fat;
cl_t head;
struct dosDirEntry *dir;
{
struct dosDirEntry d;
u_char *p;
if (!dir) /* Create lfdir? XXX */
return FSERROR;
if (!lfbuf) {
lfbuf = malloc(boot->ClusterSize);
if (!lfbuf) {
perror("No space for buffer");
return FSFATAL;
}
p = NULL;
} else
p = lfbuf;
while (1) {
if (p)
while (p < lfbuf + boot->ClusterSize)
if (*p == SLOT_EMPTY
|| *p == SLOT_DELETED)
break;
if (p && p < lfbuf + boot->ClusterSize)
break;
lfcl = p ? fat[lfcl].next : dir->head;
if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
/* Extend lfdir? XXX */
pwarn("No space in %s\n", LOSTDIR);
return FSERROR;
}
lfoff = lfcl * boot->ClusterSize
+ boot->ClusterOffset * boot->BytesPerSec;
if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
|| read(dosfs, buffer, boot->ClusterSize) != boot->ClusterSize) {
perror("could not read LOST.DIR");
return FSFATAL;
}
p = lfbuf;
}
if (!ask(0, "Reconnect"))
return FSERROR;
boot->NumFiles++;
/* Ensure uniqueness of entry here! XXX */
memset(&d, 0, sizeof d);
sprintf(d.name, "%d", head);
d.fullpath = malloc(strlen(dir->fullpath) + strlen(d.name) + 2);
sprintf(d.fullpath, "%s/%s", dir->fullpath, d.name);
d.flags = 0;
d.head = head;
d.size = fat[head].length * boot->ClusterSize;
d.parent = dir;
d.next = dir->child;
dir->child = malloc(sizeof(struct dosDirEntry));
memcpy(dir->child, &d, sizeof(struct dosDirEntry));
memset(p, 0, 32);
memset(p, ' ', 11);
memcpy(p, dir->name, strlen(dir->name));
p[26] = (u_char)dir->head;
p[27] = (u_char)(dir->head >> 8);
p[28] = (u_char)dir->size;
p[29] = (u_char)(dir->size >> 8);
p[30] = (u_char)(dir->size >> 16);
p[31] = (u_char)(dir->size >> 24);
fat[head].dirp = dir;
if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
|| write(dosfs, buffer, boot->ClusterSize) != boot->ClusterSize) {
perror("could not write LOST.DIR");
return FSFATAL;
}
return FSDIRMOD;
}
void
finishlf()
{
if (lfbuf)
free(lfbuf);
lfbuf = NULL;
}

123
sbin/fsck_msdos/dosfs.h Normal file
View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank
* Copyright (c) 1995 Martin Husemann
* Some structure declaration borrowed from Paul Popelka
* (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
*
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Martin Husemann
* and Wolfgang Solfrank.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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.
*/
#ifndef DOSFS_H
#define DOSFS_H
#define DOSBOOTBLOCKSIZE 512
#define MAX12BITCLUSTERS 4078
typedef u_int16_t cl_t; /* type holding a cluster number */
/*
* architecture independent description of all the info stored in a
* FAT boot block.
*/
struct bootblock {
u_int BytesPerSec; /* bytes per sector */
u_int SecPerClust; /* sectors per cluster */
u_int ResSectors; /* number of reserved sectors */
u_int FATs; /* number of FATs */
u_int RootDirEnts; /* number of root directory entries */
u_int32_t Sectors; /* total number of sectors */
u_int Media; /* media descriptor */
u_int FATsecs; /* number of sectors per FAT */
u_int SecPerTrack; /* sectors per track */
u_int Heads; /* number of heads */
u_int32_t HiddenSecs; /* # of hidden sectors */
u_int32_t HugeSectors; /* # of sectors if bpbSectors == 0 */
/* and some more calculated values */
int Is16BitFat; /* 0 for 12 bit, 1 for 16 bit */
cl_t NumClusters; /* # of entries in a FAT */
u_int32_t NumSectors; /* how many sectors are there */
u_int32_t NumFatEntries; /* how many entries really are there */
u_int ClusterOffset; /* at what sector would sector 0 start */
u_int ClusterSize; /* Cluster size in bytes */
/* Now some statistics: */
u_int NumFiles; /* # of plain files */
u_int NumFree; /* # of free clusters */
};
struct fatEntry {
cl_t next; /* pointer to next cluster */
cl_t head; /* pointer to start of chain */
u_int32_t length; /* number of clusters on chain */
struct dosDirEntry *dirp; /* corresponding file name */
};
#define CLUST_FREE 0 /* 0 means cluster is free */
#define CLUST_FIRST 2 /* 2 is the minimum valid cluster number */
#define CLUST_RSRVD 0xfff0 /* start of reserved clusters */
#define CLUST_BAD 0xfff7 /* a cluster with a defect */
#define CLUST_EOFS 0xfff8 /* start of EOF indicators */
#define CLUST_EOF 0xffff /* standard value for last cluster */
#define DOSLONGNAMELEN 256 /* long name maximal length */
#define LRFIRST 0x40 /* first long name record */
#define LRNOMASK 0x1f /* mask to extract long record
* sequence number */
/*
* Architecture independent description of a directory entry
*/
struct dosDirEntry {
struct dosDirEntry
*parent, /* previous tree level */
*next, /* next brother */
*child; /* if this is a directory */
char *fullpath; /* path name from root to this directory */
char name[8+1+3+1]; /* alias name first part */
char lname[DOSLONGNAMELEN]; /* real name */
uint flags; /* attributes */
cl_t head; /* cluster no */
u_int32_t size; /* filesize in bytes */
uint fsckflags; /* flags during fsck */
};
/* Flags in fsckflags: */
#define DIREMPTY 1
#define DIREMPWARN 2
/*
* TODO-list of unread directories
*/
struct dirTodoNode {
struct dosDirEntry *dir;
struct dirTodoNode *next;
};
#endif

153
sbin/fsck_msdos/ext.h Normal file
View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 1995, 1996 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Martin Husemann
* and Wolfgang Solfrank.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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.
*/
#ifndef EXT_H
#define EXT_H
#include <sys/types.h>
#if sun
#define __P(a) a
typedef char int8_t;
typedef unsigned char u_int8_t;
typedef short int16_t;
typedef unsigned short u_int16_t;
typedef long int32_t;
typedef unsigned long u_int32_t;
#endif
#include "dosfs.h"
#define LOSTDIR "LOST.DIR"
/*
* Options:
*/
extern int alwaysno; /* assume "no" for all questions */
extern int alwaysyes; /* assume "yes" for all questions */
extern int preen; /* we are preening */
extern int rdonly; /* device is opened read only (supersedes above) */
extern char *fname; /* filesystem currently checked */
/*
* function declarations
*/
void errexit __P((const char *, ...));
void pfatal __P((const char *, ...));
void pwarn __P((const char *, ...));
int ask __P((int, const char *, ...));
void perror __P((const char *));
/*
* Check filesystem given as arg
*/
int checkfilesys __P((const char *));
/*
* Return values of various functions
*/
#define FSOK 0 /* Check was OK */
#define FSDIRMOD 1 /* Some directory was modified */
#define FSFATMOD 2 /* The FAT was modified */
#define FSERROR 4 /* Some unrecovered error remains */
#define FSFATAL 8 /* Some unrecoverable error occured */
/*
* read a boot block in a machine independend fashion and translate
* it into our struct bootblock.
*/
int readboot __P((int, struct bootblock *));
/*
* Read one of the FAT copies and return a pointer to the new
* allocated array holding our description of it.
*/
int readfat __P((int, struct bootblock *, int, struct fatEntry **));
/*
* Check two FAT copies for consistency and merge changes into the
* first if neccessary.
*/
int comparefat __P((struct bootblock *, struct fatEntry *, struct fatEntry *, int));
/*
* Check a FAT
*/
int checkfat __P((struct bootblock *, struct fatEntry *));
/*
* Write back FAT entries
*/
int writefat __P((int, struct bootblock *, struct fatEntry *));
/*
* Read a directory
*/
int resetDosDirSection __P((struct bootblock *));
void finishDosDirSection __P((void));
int readDosDirSection __P((int, struct bootblock *, struct fatEntry *, struct dosDirEntry *));
/*
* A stack of directories which should be examined later
*/
extern struct dirTodoNode *pendingDirectories;
/*
* Cross-check routines run after everything is completely in memory
*/
/*
* Check for lost cluster chains
*/
int checklost __P((int, struct bootblock *, struct fatEntry *, struct dosDirEntry *));
/*
* Try to reconnect a lost cluster chain
*/
int reconnect __P((int, struct bootblock *, struct fatEntry *, cl_t, struct dosDirEntry *));
void finishlf __P((void));
/*
* Small helper functions
*/
/*
* Return the type of a reserved cluster as text
*/
char *rsrvdcltype __P((cl_t));
/*
* Clear a cluster chain in a FAT
*/
void clearchain __P((struct bootblock *, struct fatEntry *, cl_t));
#endif

514
sbin/fsck_msdos/fat.c Normal file
View File

@ -0,0 +1,514 @@
/* $NetBSD: fat.c,v 1.1 1996/05/14 17:39:34 ws Exp $ */
/*
* Copyright (C) 1995, 1996 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Martin Husemann
* and Wolfgang Solfrank.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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.
*/
#ifndef lint
static char rcsid[] = "$NetBSD: fat.c,v 1.1 1996/05/14 17:39:34 ws Exp $";
#endif /* not lint */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include "ext.h"
/*
* Check a cluster number for valid value
*/
static int
checkclnum(boot, fat, cl, next)
struct bootblock *boot;
int fat;
cl_t cl;
cl_t *next;
{
if (!boot->Is16BitFat && *next >= (CLUST_RSRVD&0xfff))
*next |= 0xf000;
if (*next == CLUST_FREE) {
boot->NumFree++;
return FSOK;
}
if (*next < CLUST_FIRST
|| (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
pwarn("Cluster %d in FAT %d continues with %s cluster number %d\n",
cl, fat,
*next < CLUST_RSRVD ? "out of range" : "reserved",
*next);
if (ask(0, "Truncate")) {
*next = CLUST_EOF;
return FSFATMOD;
}
return FSERROR;
}
return FSOK;
}
/*
* Read a FAT and decode it into internal format
*/
int
readfat(fs, boot, no, fp)
int fs;
struct bootblock *boot;
int no;
struct fatEntry **fp;
{
struct fatEntry *fat;
u_char *buffer, *p;
cl_t cl;
off_t off;
int size;
int ret = FSOK;
boot->NumFree = 0;
fat = malloc(sizeof(struct fatEntry) * boot->NumClusters);
buffer = malloc(boot->FATsecs * boot->BytesPerSec);
if (fat == NULL || buffer == NULL) {
perror("No space for FAT");
if (fat)
free(fat);
return FSFATAL;
}
memset(fat, 0, sizeof(struct fatEntry) * boot->NumClusters);
off = boot->ResSectors + no * boot->FATsecs;
off *= boot->BytesPerSec;
if (lseek(fs, off, SEEK_SET) != off) {
perror("Unable to read FAT");
free(buffer);
free(fat);
return FSFATAL;
}
if ((size = read(fs, buffer, boot->FATsecs * boot->BytesPerSec))
!= boot->FATsecs * boot->BytesPerSec) {
if (size < 0)
perror("Unable to read FAT");
else
pfatal("Short FAT?");
free(buffer);
free(fat);
return FSFATAL;
}
/*
* Remember start of FAT to allow keeping it in write_fat.
*/
fat[0].length = buffer[0]|(buffer[1] << 8)|(buffer[2] << 16);
if (boot->Is16BitFat)
fat[0].length |= buffer[3] << 24;
if (buffer[1] != 0xff || buffer[2] != 0xff
|| (boot->Is16BitFat && buffer[3] != 0xff)) {
char *msg = boot->Is16BitFat
? "FAT starts with odd byte sequence (%02x%02x%02x%02x)\n"
: "FAT starts with odd byte sequence (%02x%02x%02x)\n";
pwarn(msg, buffer[0], buffer[1], buffer[2], buffer[3]);
if (ask(1, "Correct")) {
fat[0].length = boot->Media|0xffffff;
ret |= FSFATMOD;
}
}
p = buffer + (boot->Is16BitFat ? 4 : 3);
for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
if (boot->Is16BitFat) {
fat[cl].next = p[0] + (p[1] << 8);
ret |= checkclnum(boot, no, cl, &fat[cl].next);
cl++;
p += 2;
} else {
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;
}
}
free(buffer);
*fp = fat;
return ret;
}
/*
* Get type of reserved cluster
*/
char *
rsrvdcltype(cl)
cl_t cl;
{
if (cl < CLUST_BAD)
return "reserved";
if (cl > CLUST_BAD)
return "as EOF";
return "bad";
}
static int
clustdiffer(cl, cp1, cp2, fatnum)
cl_t cl;
cl_t *cp1;
cl_t *cp2;
int fatnum;
{
if (*cp1 >= CLUST_RSRVD) {
if (*cp2 >= CLUST_RSRVD) {
if ((*cp1 < CLUST_BAD && *cp2 < CLUST_BAD)
|| (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
pwarn("Cluster %d is marked %s with different indicators, ",
cl, rsrvdcltype(*cp1));
if (ask(1, "fix")) {
*cp2 = *cp1;
return FSFATMOD;
}
return FSFATAL;
}
pwarn("Cluster %d is marked %s in FAT 1, %s in FAT %d\n",
cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
if (ask(0, "use FAT #1's entry")) {
*cp2 = *cp1;
return FSFATMOD;
}
if (ask(0, "use FAT #%d's entry", fatnum)) {
*cp1 = *cp2;
return FSFATMOD;
}
return FSFATAL;
}
pwarn("Cluster %d is marked %s in FAT 1, but continues with cluster %d in FAT %d\n",
cl, rsrvdcltype(*cp1), *cp2, fatnum);
if (ask(0, "Use continuation from FAT %d", fatnum)) {
*cp1 = *cp2;
return FSFATMOD;
}
if (ask(0, "Use mark from FAT 1")) {
*cp2 = *cp1;
return FSFATMOD;
}
return FSFATAL;
}
if (*cp2 >= CLUST_RSRVD) {
pwarn("Cluster %d continues with cluster %d in FAT 1, but is marked %s in FAT %d\n",
cl, *cp1, rsrvdcltype(*cp2), fatnum);
if (ask(0, "Use continuation from FAT 1")) {
*cp2 = *cp1;
return FSFATMOD;
}
if (ask(0, "Use mark from FAT %d", fatnum)) {
*cp1 = *cp2;
return FSFATMOD;
}
return FSERROR;
}
pwarn("Cluster %d continues with cluster %d in FAT 1, but with cluster %d in FAT %d\n",
cl, *cp1, *cp2, fatnum);
if (ask(0, "Use continuation from FAT 1")) {
*cp2 = *cp1;
return FSFATMOD;
}
if (ask(0, "Use continuation from FAT %d", fatnum)) {
*cp1 = *cp2;
return FSFATMOD;
}
return FSERROR;
}
/*
* Compare two FAT copies in memory. Resolve any conflicts and merge them
* into the first one.
*/
int
comparefat(boot, first, second, fatnum)
struct bootblock *boot;
struct fatEntry *first;
struct fatEntry *second;
int fatnum;
{
cl_t cl;
int ret = FSOK;
if (first[0].next != second[0].next) {
pwarn("Media bytes in cluster 1(%02x) and %d(%02x) differ\n",
first[0].next, fatnum, second[0].next);
if (ask(1, "Use media byte from FAT 1")) {
second[0].next = first[0].next;
ret |= FSFATMOD;
} else if (ask(0, "Use media byte from FAT %d", fatnum)) {
first[0].next = second[0].next;
ret |= FSFATMOD;
} else
ret |= FSERROR;
}
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
clearchain(boot, fat, head)
struct bootblock *boot;
struct fatEntry *fat;
cl_t head;
{
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;
}
}
/*
* Check a complete FAT in-memory for crosslinks
*/
int
checkfat(boot, fat)
struct bootblock *boot;
struct fatEntry *fat;
{
cl_t head, p, h;
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 untraveled chain */
if (fat[head].head != 0 /* cluster already belongs to some chain*/
|| fat[head].next == CLUST_FREE)
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 = 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 untraveled chain */
if (fat[head].head != head)
continue;
/* follow the chain to its end (hopefully) */
for (p = head;
fat[p].next >= CLUST_FIRST && fat[p].next < boot->NumClusters;
p = fat[p].next)
if (fat[fat[p].next].head != head)
break;
if (fat[p].next >= CLUST_EOFS)
continue;
if (fat[p].next == 0) {
pwarn("Cluster chain starting at %d ends with free cluster\n", head);
if (ask(0, "Clear chain starting at %d", head)) {
clearchain(boot, fat, head);
ret |= FSFATMOD;
} else
ret |= FSERROR;
continue;
}
if (fat[p].next >= CLUST_RSRVD) {
pwarn("Cluster chain starting at %d ends with cluster marked %s\n",
head, rsrvdcltype(fat[p].next));
if (ask(0, "Clear chain starting at %d", head)) {
clearchain(boot, fat, head);
ret |= FSFATMOD;
} else
ret |= FSERROR;
continue;
}
if (fat[p].next < CLUST_FIRST || fat[p].next >= boot->NumClusters) {
pwarn("Cluster chain starting at %d ends with cluster out of range (%d)\n",
head, fat[p].next);
if (ask(0, "Clear chain starting at %d", head)) {
clearchain(boot, fat, head);
ret |= FSFATMOD;
} else
ret |= FSERROR;
}
pwarn("Cluster chains starting at %d and %d are linked at cluster %d\n",
head, fat[p].head, p);
conf = FSERROR;
if (ask(0, "Clear chain starting at %d", head)) {
clearchain(boot, fat, head);
conf = FSFATMOD;
}
if (ask(0, "Clear chain starting at %d", h = fat[p].head)) {
if (conf == FSERROR) {
/*
* Transfer the common chain to the one not cleared above.
*/
for (; 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
writefat(fs, boot, fat)
int fs;
struct bootblock *boot;
struct fatEntry *fat;
{
u_char *buffer, *p;
cl_t cl;
int i;
u_int32_t fatsz;
off_t off;
int ret = FSOK;
buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec);
if (buffer == NULL) {
perror("No space for FAT");
return FSFATAL;
}
memset(buffer, 0, fatsz);
boot->NumFree = 0;
buffer[0] = (u_char)fat[0].length;
buffer[1] = (u_char)(fat[0].length >> 8);
if (boot->Is16BitFat)
buffer[3] = (u_char)(fat[0].length >> 24);
for (cl = CLUST_FIRST, p = buffer; cl < boot->NumClusters;) {
if (boot->Is16BitFat) {
p[0] = (u_char)fat[cl].next;
if (fat[cl].next == CLUST_FREE)
boot->NumFree++;
p[1] = (u_char)(fat[cl++].next >> 8);
p += 2;
} else {
if (fat[cl].next == CLUST_FREE)
boot->NumFree++;
if (cl + 1 < boot->NumClusters
&& fat[cl + 1].next == CLUST_FREE)
boot->NumFree++;
p[0] = (u_char)fat[cl].next;
p[1] = (u_char)((fat[cl].next >> 8) & 0xf)
|(u_char)(fat[cl+1].next << 4);
p[2] = (u_char)(fat[cl++].next >> 8);
p += 3;
}
}
for (i = 0; i < boot->FATs; i++) {
off = boot->ResSectors + i * boot->FATsecs;
off *= boot->BytesPerSec;
if (lseek(fs, off, SEEK_SET) != off
|| write(fs, buffer, fatsz) != fatsz) {
perror("Unable to write FAT");
ret = FSFATAL; /* Return immediately? XXX */
}
}
free(buffer);
return ret;
}
/*
* Check a complete in-memory FAT for lost cluster chains
*/
int
checklost(dosfs, boot, fat, rootDir)
int dosfs;
struct bootblock *boot;
struct fatEntry *fat;
struct dosDirEntry *rootDir;
{
cl_t head;
struct dosDirEntry *lfdir;
int mod = FSOK;
for (lfdir = rootDir->child; lfdir; lfdir = lfdir->next) {
if (!strcmp(lfdir->name, LOSTDIR))
break;
}
for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
/* find next untraveled chain */
if (fat[head].head != head
|| fat[head].next == CLUST_FREE
|| (fat[head].next >= CLUST_RSRVD
&& fat[head].next < CLUST_EOFS))
continue;
if (fat[head].dirp == NULL) {
pwarn("Lost cluster chain at cluster 0x%04x\n%d Cluster(s) lost\n",
head, fat[head].length);
mod |= reconnect(dosfs, boot, fat, head, lfdir);
if (mod&FSFATAL)
break;
}
}
finishlf();
return mod;
}

View File

@ -0,0 +1,76 @@
.\" $NetBSD: fsck_msdos.8,v 1.1 1996/05/14 17:39:35 ws Exp $
.\"
.\" Copyright (C) 1995 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.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by Martin Husemann
.\" and Wolfgang Solfrank.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" 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.
.\"
.\"
.Dd August 13, 1995
.Dt FSCK_MSDOS 8
.Os NetBSD 1.1a
.Sh NAME
.Nm fsck_msdos
.Nd DOS/Windows (FAT) filesystem consistency checker
.Sh SYNOPSIS
.Nm fsck_msdos
.Fl p
.Ar filesystem
.Ar ...
.Nm fsck_msdos
.Op Fl y
.Op Fl n
.Ar filesystem
.Ar ...
.Sh DESCRIPTION
The first form of
.Nm
preens the specified filesystems.
It is normally started by
.Xr fsck 8
run from
.Pa /etc/rc
during automatic reboot, when a FAT filesystem is detected.
.Pp
.Nm fsck_msdos
verifies a FAT filesystem (more commonly known as DOS filesystem) and tries
to recover it from inconsistencies.
.Pp
Options are:
.Bl -hang -offset indent
.It Em -y
assume yes as answer to all questions.
.It Em -n
assume no as answer to all questions.
.El
.Sh SEE ALSO
.Xr fsck 8 ,
.Xr mount_msdos 8
.Sh BUGS
.Nm fsck_msdos
is still under construction.

252
sbin/fsck_msdos/main.c Normal file
View File

@ -0,0 +1,252 @@
/* $NetBSD: main.c,v 1.1 1996/05/14 17:39:36 ws Exp $ */
/*
* Copyright (C) 1995 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Martin Husemann
* and Wolfgang Solfrank.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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.
*/
#ifndef lint
static char rcsid[] = "$NetBSD: main.c,v 1.1 1996/05/14 17:39:36 ws Exp $";
#endif /* not lint */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#if __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include "ext.h"
int alwaysno; /* assume "no" for all questions */
int alwaysyes; /* assume "yes" for all questions */
int preen; /* set when preening */
int rdonly; /* device is opened read only (supersedes above) */
char *fname; /* filesystem currently checked */
static void
usage()
{
errexit("Usage: fsck_msdos [-pny] filesystem ... \n");
}
int
main(argc, argv)
int argc;
char **argv;
{
extern int optind;
int ret = 0, erg;
int ch;
while ((ch = getopt(argc, argv, "vpyn")) != EOF) {
switch (ch) {
case 'n':
alwaysno = 1;
alwaysyes = preen = 0;
break;
case 'y':
alwaysyes = 1;
alwaysno = preen = 0;
break;
case 'p':
preen = 1;
alwaysyes = alwaysno = 0;
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (!argc)
usage();
while (argc-- > 0) {
erg = checkfilesys(fname = *argv++);
if (erg > ret)
ret = erg;
}
exit(ret);
}
/*VARARGS*/
void
#if __STDC__
errexit(const char *fmt, ...)
#else
errexit(fmt, va_alist)
char *fmt;
va_dcl
#endif
{
va_list ap;
#if __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
vprintf(fmt, ap);
va_end(ap);
exit(8);
}
/*VARARGS*/
void
#if __STDC__
pfatal(const char *fmt, ...)
#else
pfatal(fmt, va_alist)
char *fmt;
va_dcl
#endif
{
va_list ap;
if (preen)
printf("%s: ", fname);
#if __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
vprintf(fmt, ap);
va_end(ap);
printf("\n");
if (preen)
exit(8);
}
/*VARARGS*/
void
#if __STDC__
pwarn(const char *fmt, ...)
#else
pwarn(fmt, va_alist)
char *fmt;
va_dcl
#endif
{
va_list ap;
if (preen)
printf("%s: ", fname);
#if __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
vprintf(fmt, ap);
va_end(ap);
}
#if sun
char *
strerror(n)
int n;
{
extern int sys_nerr;
extern char *sys_errlist[];
static char alt[80];
if (n < sys_nerr)
return sys_errlist[n];
sprintf(alt, "Unknown error %d", n);
return alt;
}
#endif
void
perror(s)
const char *s;
{
pfatal("%s (%s)", s, strerror(errno));
}
/*VARARGS*/
int
#if __STDC__
ask(int def, const char *fmt, ...)
#else
ask(def, fmt, va_alist)
int def;
char *fmt;
va_dcl
#endif
{
va_list ap;
char prompt[256];
int c;
if (preen) {
if (rdonly)
def = 0;
if (def)
printf("FIXED\n");
return def;
}
#if __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
#if sun
vsprintf(prompt, fmt, ap);
#else
vsnprintf(prompt, sizeof(prompt), fmt, ap);
#endif
if (alwaysyes || rdonly) {
printf("%s? %s\n", prompt, rdonly ? "no" : "yes");
return !rdonly;
}
do {
printf("%s? [yn] ", prompt);
fflush(stdout);
c = getchar();
while (c != '\n' && getchar() != '\n')
if (feof(stdin))
return 0;
} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
return c == 'y' || c == 'Y';
}