Commit a706c6fb authored by Mike Hibler's avatar Mike Hibler
Browse files

First cut at Windows FAT support. With code from the FreeBSD fsck_msdosfs

program, it only took a couple of hours.  Heavily tested: it didn't core dump
examining my 20GB FAT32 partition, ship it!

Actually, I did imagezip/imageunzip a FAT12 DOS floppy.  Since imagezip
files are a minimum of 1MB (the chunk size), it is probably not practical
for saving 1.4MB floppies :-)

Also, updated the man page.
parent d79ef690
......@@ -15,6 +15,7 @@ DISTFILES = ext2_fs.h imagehdr.h linux_types.h queue.h \
EXPANDCOPYRIGHT = /usr/site/lib/copyright/expand-copyr
WITH_NTFS = @WINSUPPORT@
WITH_FAT = @WINSUPPORT@
include $(OBJDIR)/Makeconf
......@@ -44,12 +45,19 @@ LIBS += -Lntfs -lntfs -llocale
NTFSDIR = ntfs
endif
# with FAT
ifeq ($(WITH_FAT),1)
CFLAGS += -DWITH_FAT
FSOBJS += fat_boot.o fat_fat.o fat_glue.o
MOSTLY_SRCDIRS += $(SRCDIR)/fat
endif
all: imagezip imageunzip imagedump
include $(TESTBED_SRCDIR)/GNUmakerules
imagezip: $(NTFSDIR) imagezip.o version.o
$(CC) $(CFLAGS) imagezip.o version.o $(LIBS) -o imagezip
imagezip: $(NTFSDIR) imagezip.o version.o $(FSOBJS)
$(CC) $(CFLAGS) imagezip.o version.o $(FSOBJS) $(LIBS) -o imagezip
imageunzip: imageunzip.o crc.o version.o
$(CC) $(CFLAGS) imageunzip.o crc.o version.o $(UNZIPLIBS) -o imageunzip
......@@ -60,6 +68,8 @@ imageunzip.o: imageunzip.c
imagedump: imagedump.o version.o
$(CC) $(CFLAGS) imagedump.o version.o $(LIBS) -o imagedump
fat_boot.o fat_fat.o fat_glue.o: $(SRCDIR)/fat/fat_glue.h
ntfs:
@$(MAKE) -C ntfs all
......
/*
* Copyright (C) 1995, 1996, 1997 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.
* $NetBSD: dosfs.h,v 1.4 1997/01/03 14:32:48 ws Exp $
* $FreeBSD: src/sbin/fsck_msdosfs/dosfs.h,v 1.1.2.1 2001/08/01 05:47:56 obrien Exp $
*/
#ifndef DOSFS_H
#define DOSFS_H
#define DOSBOOTBLOCKSIZE 512
typedef u_int32_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_int Media; /* media descriptor */
u_int FATsmall; /* number of sectors per FAT */
u_int SecPerTrack; /* sectors per track */
u_int Heads; /* number of heads */
u_int32_t Sectors; /* total number of sectors */
u_int32_t HiddenSecs; /* # of hidden sectors */
u_int32_t HugeSectors; /* # of sectors if bpbSectors == 0 */
u_int FSInfo; /* FSInfo sector */
u_int Backup; /* Backup of Bootblocks */
cl_t RootCl; /* Start of Root Directory */
cl_t FSFree; /* Number of free clusters acc. FSInfo */
cl_t FSNext; /* Next free cluster acc. FSInfo */
/* and some more calculated values */
u_int flags; /* some flags: */
#define FAT32 1 /* this is a FAT32 filesystem */
/*
* Maybe, we should separate out
* various parts of FAT32? XXX
*/
int ValidFat; /* valid fat if FAT32 non-mirrored */
cl_t ClustMask; /* mask for entries in FAT */
cl_t NumClusters; /* # of entries in a FAT */
u_int32_t NumSectors; /* how many sectors are there */
u_int32_t FATsecs; /* how many sectors are in FAT */
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 */
u_int NumBad; /* # of bad 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 */
int flags; /* see below */
};
#define CLUST_FREE 0 /* 0 means cluster is free */
#define CLUST_FIRST 2 /* 2 is the minimum valid cluster number */
#define CLUST_RSRVD 0xfffffff6 /* start of reserved clusters */
#define CLUST_BAD 0xfffffff7 /* a cluster with a defect */
#define CLUST_EOFS 0xfffffff8 /* start of EOF indicators */
#define CLUST_EOF 0xffffffff /* standard value for last cluster */
/*
* Masks for cluster values
*/
#define CLUST12_MASK 0xfff
#define CLUST16_MASK 0xffff
#define CLUST32_MASK 0xfffffff
#define FAT_USED 1 /* This fat chain is used in a file */
#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 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
/*
* Hacked from...
*/
/*
* Copyright (C) 1995, 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.
* 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.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: boot.c,v 1.5 1997/10/17 11:19:23 ws Exp $");
static const char rcsid[] =
"$FreeBSD: src/sbin/fsck_msdosfs/boot.c,v 1.1.2.1 2001/08/01 05:47:55 obrien Exp $";
#endif /* not lint */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include "fat_glue.h"
int
readboot(dosfs, boot)
int dosfs;
struct bootblock *boot;
{
u_char block[DOSBOOTBLOCKSIZE];
u_char fsinfo[2 * DOSBOOTBLOCKSIZE];
u_char backup[DOSBOOTBLOCKSIZE];
int ret = FSOK;
if (read(dosfs, block, sizeof block) < sizeof block) {
perror("could not read boot block");
return FSFATAL;
}
if (block[510] != 0x55 || block[511] != 0xaa) {
pfatal("Invalid signature in boot block: %02x%02x", block[511], block[510]);
return FSFATAL;
}
memset(boot, 0, sizeof *boot);
boot->ValidFat = -1;
/* 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->FATsmall = 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->FATsecs = boot->FATsmall;
if (!boot->RootDirEnts)
boot->flags |= FAT32;
if (boot->flags & FAT32) {
boot->FATsecs = block[36] + (block[37] << 8)
+ (block[38] << 16) + (block[39] << 24);
if (block[40] & 0x80)
boot->ValidFat = block[40] & 0x0f;
/* check version number: */
if (block[42] || block[43]) {
/* Correct? XXX */
pfatal("Unknown filesystem version: %x.%x",
block[43], block[42]);
return FSFATAL;
}
boot->RootCl = block[44] + (block[45] << 8)
+ (block[46] << 16) + (block[47] << 24);
boot->FSInfo = block[48] + (block[49] << 8);
boot->Backup = block[50] + (block[51] << 8);
if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
!= boot->FSInfo * boot->BytesPerSec
|| read(dosfs, fsinfo, sizeof fsinfo)
!= sizeof fsinfo) {
perror("could not read fsinfo block");
return FSFATAL;
}
if (memcmp(fsinfo, "RRaA", 4)
|| memcmp(fsinfo + 0x1e4, "rrAa", 4)
|| fsinfo[0x1fc]
|| fsinfo[0x1fd]
|| fsinfo[0x1fe] != 0x55
|| fsinfo[0x1ff] != 0xaa
|| fsinfo[0x3fc]
|| fsinfo[0x3fd]
|| fsinfo[0x3fe] != 0x55
|| fsinfo[0x3ff] != 0xaa) {
pwarn("Invalid signature in fsinfo block");
return FSFATAL;
}
if (boot->FSInfo) {
boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8)
+ (fsinfo[0x1ea] << 16)
+ (fsinfo[0x1eb] << 24);
boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8)
+ (fsinfo[0x1ee] << 16)
+ (fsinfo[0x1ef] << 24);
}
if (lseek(dosfs, boot->Backup * boot->BytesPerSec, SEEK_SET)
!= boot->Backup * boot->BytesPerSec
|| read(dosfs, backup, sizeof backup) != sizeof backup) {
perror("could not read backup bootblock");
return FSFATAL;
}
if (memcmp(block, backup, DOSBOOTBLOCKSIZE)) {
/* Correct? XXX */
pfatal("backup doesn't compare to primary bootblock");
return FSFATAL;
}
/* Check backup FSInfo? XXX */
}
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", boot->BytesPerSec);
return FSFATAL;
}
if (boot->SecPerClust == 0) {
pfatal("Invalid cluster size: %u", 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->flags&FAT32)
boot->ClustMask = CLUST32_MASK;
else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK))
boot->ClustMask = CLUST12_MASK;
else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK))
boot->ClustMask = CLUST16_MASK;
else {
pfatal("Filesystem too big (%u clusters) for non-FAT32 partition",
boot->NumClusters);
return FSFATAL;
}
switch (boot->ClustMask) {
case CLUST32_MASK:
boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 4;
break;
case CLUST16_MASK:
boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 2;
break;
default:
boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec * 2) / 3;
break;
}
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;
}
boot->ClusterSize = boot->BytesPerSec * boot->SecPerClust;
boot->NumFiles = 1;
boot->NumFree = 0;
return ret;
}
/*
* Hacked from...
*/
/*
* 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.
* 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.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $");
static const char rcsid[] =
"$FreeBSD: src/sbin/fsck_msdosfs/fat.c,v 1.1.2.1 2001/08/01 05:47:56 obrien Exp $";
#endif /* not lint */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include "fat_glue.h"
static int checkclnum __P((struct bootblock *, int, cl_t, cl_t *));
static int _readfat __P((int, struct bootblock *, int, u_char **));
/*
* 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;
{
static cl_t first = 0;
if (*next >= (CLUST_RSRVD&boot->ClustMask))
*next |= ~boot->ClustMask;
if (*next == CLUST_FREE) {
if (first == 0)
first = cl;
boot->NumFree++;
} else {
if (first != 0) {
fat_addskip(boot, first, cl - first);
first = 0;
}
}
return FSOK;
}
/*
* Read a FAT from disk. Returns 1 if successful, 0 otherwise.
*/
static int
_readfat(fs, boot, no, buffer)
int fs;
struct bootblock *boot;
int no;
u_char **buffer;
{
off_t off;
*buffer = malloc(boot->FATsecs * boot->BytesPerSec);
if (*buffer == NULL) {
perror("No space for FAT");
return 0;
}
off = boot->ResSectors + no * boot->FATsecs;
off *= boot->BytesPerSec;
if (lseek(fs, off, SEEK_SET) != off) {
perror("Unable to read FAT");
goto err;
}
if (read(fs, *buffer, boot->FATsecs * boot->BytesPerSec)
!= boot->FATsecs * boot->BytesPerSec) {
perror("Unable to read FAT");
goto err;
}
return 1;
err:
free(*buffer);
return 0;
}
/*
* 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;
int ret = FSOK;
boot->NumFree = boot->NumBad = 0;
if (!_readfat(fs, boot, no, &buffer))
return FSFATAL;
fat = calloc(boot->NumClusters, sizeof(struct fatEntry));
if (fat == NULL) {
perror("No space for FAT");
free(buffer);
return FSFATAL;
}
if (buffer[0] != boot->Media
|| 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
* filesystem is dirty if it doesn't reboot cleanly.
* Check this special condition before errorring out.
*/
if (buffer[0] == boot->Media && buffer[1] == 0xff
&& 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))) {
pwarn("FAT is not clean");
ret = FSFATAL;
goto done;
}
else {
/* just some odd byte sequence in FAT */
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;
}
ret = FSFATAL;
goto done;
}
}
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);