2000-05-06 21:34:20 +02:00
|
|
|
/****************************************************************/
|
|
|
|
/* */
|
|
|
|
/* fattab.c */
|
|
|
|
/* */
|
|
|
|
/* FAT File System Table Functions */
|
|
|
|
/* */
|
|
|
|
/* Copyright (c) 1995 */
|
|
|
|
/* Pasquale J. Villani */
|
|
|
|
/* All Rights Reserved */
|
|
|
|
/* */
|
|
|
|
/* This file is part of DOS-C. */
|
|
|
|
/* */
|
|
|
|
/* DOS-C is free software; you can redistribute it and/or */
|
|
|
|
/* modify it under the terms of the GNU General Public License */
|
|
|
|
/* as published by the Free Software Foundation; either version */
|
|
|
|
/* 2, or (at your option) any later version. */
|
|
|
|
/* */
|
|
|
|
/* DOS-C is distributed in the hope that it will be useful, but */
|
|
|
|
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
|
|
|
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
|
|
|
|
/* the GNU General Public License for more details. */
|
|
|
|
/* */
|
|
|
|
/* You should have received a copy of the GNU General Public */
|
|
|
|
/* License along with DOS-C; see the file COPYING. If not, */
|
|
|
|
/* write to the Free Software Foundation, 675 Mass Ave, */
|
|
|
|
/* Cambridge, MA 02139, USA. */
|
|
|
|
/****************************************************************/
|
|
|
|
|
|
|
|
#include "portab.h"
|
|
|
|
#include "globals.h"
|
|
|
|
|
|
|
|
#ifdef VERSION_STRINGS
|
2001-11-18 15:01:12 +01:00
|
|
|
static BYTE *RcsId =
|
|
|
|
"$Id$";
|
2000-05-06 21:34:20 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* cluster/sector routines */
|
|
|
|
/* */
|
|
|
|
/************************************************************************/
|
|
|
|
|
2004-04-30 15:07:26 +02:00
|
|
|
/* special "impossible" "Cluster2" value of 1 denotes reading the
|
|
|
|
cluster number rather than overwriting it */
|
|
|
|
#define READ_CLUSTER 1
|
|
|
|
|
2001-09-23 22:39:44 +02:00
|
|
|
#ifndef ISFAT32
|
2001-11-18 15:01:12 +01:00
|
|
|
int ISFAT32(struct dpb FAR * dpbp)
|
2001-09-23 22:39:44 +02:00
|
|
|
{
|
2001-11-18 15:01:12 +01:00
|
|
|
return _ISFAT32(dpbp);
|
2001-09-23 22:39:44 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-04-30 16:05:14 +02:00
|
|
|
struct buffer FAR *getFATblock(struct dpb FAR * dpbp, CLUSTER clussec)
|
2001-06-03 16:16:18 +02:00
|
|
|
{
|
2004-04-30 16:05:14 +02:00
|
|
|
struct buffer FAR *bp = getblock(clussec, dpbp->dpb_unit);
|
2001-11-18 15:01:12 +01:00
|
|
|
|
2001-06-03 16:16:18 +02:00
|
|
|
if (bp)
|
2001-11-04 20:47:39 +01:00
|
|
|
{
|
|
|
|
bp->b_flag &= ~(BFR_DATA | BFR_DIR);
|
|
|
|
bp->b_flag |= BFR_FAT | BFR_VALID;
|
2002-08-03 04:25:17 +02:00
|
|
|
bp->b_dpbp = dpbp;
|
2002-12-09 01:17:15 +01:00
|
|
|
bp->b_copies = dpbp->dpb_fats;
|
|
|
|
bp->b_offset = dpbp->dpb_fatsize;
|
|
|
|
#ifdef WITHFAT32
|
|
|
|
if (ISFAT32(dpbp))
|
|
|
|
{
|
|
|
|
if (dpbp->dpb_xflags & FAT_NO_MIRRORING)
|
|
|
|
bp->b_copies = 1;
|
|
|
|
}
|
|
|
|
#endif
|
2001-11-04 20:47:39 +01:00
|
|
|
}
|
2001-11-18 15:01:12 +01:00
|
|
|
return bp;
|
2001-06-03 16:16:18 +02:00
|
|
|
}
|
2001-09-23 22:39:44 +02:00
|
|
|
|
|
|
|
#ifdef WITHFAT32
|
2001-11-18 15:01:12 +01:00
|
|
|
void read_fsinfo(struct dpb FAR * dpbp)
|
2001-09-23 22:39:44 +02:00
|
|
|
{
|
|
|
|
struct buffer FAR *bp;
|
|
|
|
struct fsinfo FAR *fip;
|
|
|
|
|
|
|
|
bp = getblock(dpbp->dpb_xfsinfosec, dpbp->dpb_unit);
|
|
|
|
bp->b_flag &= ~(BFR_DATA | BFR_DIR | BFR_FAT | BFR_DIRTY);
|
|
|
|
bp->b_flag |= BFR_VALID;
|
|
|
|
|
2001-11-18 15:01:12 +01:00
|
|
|
fip = (struct fsinfo FAR *)&bp->b_buffer[0x1e4];
|
2001-11-04 20:47:39 +01:00
|
|
|
dpbp->dpb_xnfreeclst = fip->fi_nfreeclst;
|
|
|
|
dpbp->dpb_xcluster = fip->fi_cluster;
|
2001-09-23 22:39:44 +02:00
|
|
|
}
|
|
|
|
|
2001-11-18 15:01:12 +01:00
|
|
|
void write_fsinfo(struct dpb FAR * dpbp)
|
2001-09-23 22:39:44 +02:00
|
|
|
{
|
|
|
|
struct buffer FAR *bp;
|
|
|
|
struct fsinfo FAR *fip;
|
|
|
|
|
|
|
|
bp = getblock(dpbp->dpb_xfsinfosec, dpbp->dpb_unit);
|
|
|
|
bp->b_flag &= ~(BFR_DATA | BFR_DIR | BFR_FAT);
|
|
|
|
bp->b_flag |= BFR_VALID | BFR_DIRTY;
|
|
|
|
|
2001-11-18 15:01:12 +01:00
|
|
|
fip = (struct fsinfo FAR *)&bp->b_buffer[0x1e4];
|
2001-11-04 20:47:39 +01:00
|
|
|
fip->fi_nfreeclst = dpbp->dpb_xnfreeclst;
|
|
|
|
fip->fi_cluster = dpbp->dpb_xcluster;
|
2001-09-23 22:39:44 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2000-05-06 21:34:20 +02:00
|
|
|
/* */
|
|
|
|
/* The FAT file system is difficult to trace through FAT table. */
|
|
|
|
/* There are two kinds of FAT's, 12 bit and 16 bit. The 16 bit */
|
2004-04-24 17:53:21 +02:00
|
|
|
/* FAT is the easiest, since it is nothing more than a series */
|
|
|
|
/* of UWORD's. The 12 bit FAT is difficult, because it packs 3 */
|
|
|
|
/* FAT entries into two BYTE's. These are packed as follows: */
|
2000-05-06 21:34:20 +02:00
|
|
|
/* */
|
|
|
|
/* 0x0003 0x0004 0x0005 0x0006 0x0007 0x0008 0x0009 ... */
|
|
|
|
/* */
|
|
|
|
/* are packed as */
|
|
|
|
/* */
|
|
|
|
/* 0x03 0x40 0x00 0x05 0x60 0x00 0x07 0x80 0x00 0x09 ... */
|
|
|
|
/* */
|
|
|
|
/* 12 bytes are compressed to 9 bytes */
|
|
|
|
/* */
|
|
|
|
|
2004-04-30 15:07:26 +02:00
|
|
|
CLUSTER link_fat(struct dpb FAR * dpbp, CLUSTER Cluster1,
|
|
|
|
REG CLUSTER Cluster2)
|
2000-05-06 21:34:20 +02:00
|
|
|
{
|
2002-08-03 04:25:17 +02:00
|
|
|
struct buffer FAR *bp;
|
2004-04-29 14:48:18 +02:00
|
|
|
unsigned idx;
|
2004-04-30 16:05:14 +02:00
|
|
|
unsigned secdiv;
|
|
|
|
CLUSTER clussec = Cluster1;
|
|
|
|
CLUSTER max_cluster = dpbp->dpb_size;
|
|
|
|
|
|
|
|
#ifdef WITHFAT32
|
|
|
|
if (ISFAT32(dpbp))
|
|
|
|
max_cluster = dpbp->dpb_xsize;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (clussec <= 1 || clussec > max_cluster)
|
|
|
|
{
|
|
|
|
put_string("run CHKDSK: trying to access invalid cluster 0x");
|
|
|
|
#ifdef WITHFAT32
|
|
|
|
put_unsigned((unsigned)(clussec >> 16), 16, 4);
|
|
|
|
#endif
|
|
|
|
put_unsigned((unsigned)(clussec & 0xffffu), 16, 4);
|
|
|
|
put_console('\n');
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
secdiv = dpbp->dpb_secsize;
|
|
|
|
if (ISFAT12(dpbp))
|
|
|
|
{
|
|
|
|
clussec = (unsigned)clussec * 3;
|
|
|
|
secdiv *= 2;
|
|
|
|
}
|
|
|
|
else /* FAT16 or FAT32 */
|
|
|
|
{
|
|
|
|
secdiv /= 2;
|
|
|
|
#ifdef WITHFAT32
|
|
|
|
if (ISFAT32(dpbp))
|
|
|
|
secdiv /= 2;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* idx is a pointer to an index which is the nibble offset of the FAT
|
|
|
|
entry within the sector for FAT12, or word offset for FAT16, or
|
|
|
|
dword offset for FAT32 */
|
|
|
|
idx = (unsigned)(clussec % secdiv);
|
|
|
|
clussec /= secdiv;
|
|
|
|
clussec += dpbp->dpb_fatstrt;
|
|
|
|
#ifdef WITHFAT32
|
|
|
|
if (ISFAT32(dpbp) && (dpbp->dpb_xflags & FAT_NO_MIRRORING))
|
|
|
|
{
|
|
|
|
/* we must modify the active fat,
|
|
|
|
it's number is in the 0-3 bits of dpb_xflags */
|
|
|
|
clussec += (dpbp->dpb_xflags & 0xf) * dpbp->dpb_xfatsize;
|
|
|
|
}
|
|
|
|
#endif
|
2002-08-03 04:25:17 +02:00
|
|
|
|
|
|
|
/* Get the block that this cluster is in */
|
2004-04-30 16:05:14 +02:00
|
|
|
bp = getFATblock(dpbp, clussec);
|
2002-08-03 04:25:17 +02:00
|
|
|
|
|
|
|
if (bp == NULL)
|
2004-04-30 15:07:26 +02:00
|
|
|
return 1; /* the only error code possible here */
|
2001-11-18 15:01:12 +01:00
|
|
|
|
2000-05-06 21:34:20 +02:00
|
|
|
if (ISFAT12(dpbp))
|
2002-08-03 04:25:17 +02:00
|
|
|
{
|
|
|
|
REG UBYTE FAR *fbp0, FAR * fbp1;
|
|
|
|
struct buffer FAR * bp1;
|
2004-04-30 17:28:18 +02:00
|
|
|
unsigned cluster;
|
2002-08-03 04:25:17 +02:00
|
|
|
|
|
|
|
/* form an index so that we can read the block as a */
|
|
|
|
/* byte array */
|
2004-04-29 14:48:18 +02:00
|
|
|
idx /= 2;
|
2004-04-24 17:53:21 +02:00
|
|
|
|
2002-08-03 04:25:17 +02:00
|
|
|
/* Test to see if the cluster straddles the block. If */
|
|
|
|
/* it does, get the next block and use both to form the */
|
|
|
|
/* the FAT word. Otherwise, just point to the next */
|
|
|
|
/* block. */
|
|
|
|
fbp0 = &bp->b_buffer[idx];
|
2004-04-30 17:28:18 +02:00
|
|
|
|
|
|
|
/* pointer to next byte, will be overwritten, if not valid */
|
2002-08-03 04:25:17 +02:00
|
|
|
fbp1 = fbp0 + 1;
|
2004-04-30 17:28:18 +02:00
|
|
|
|
2002-08-03 04:25:17 +02:00
|
|
|
if (idx >= (unsigned)dpbp->dpb_secsize - 1)
|
|
|
|
{
|
2004-04-30 17:28:18 +02:00
|
|
|
/* blockio.c LRU logic ensures that bp != bp1 */
|
2004-04-30 16:05:14 +02:00
|
|
|
bp1 = getFATblock(dpbp, (unsigned)clussec + 1);
|
2002-08-03 04:25:17 +02:00
|
|
|
if (bp1 == 0)
|
2004-04-30 15:07:26 +02:00
|
|
|
return 1; /* the only error code possible here */
|
2002-08-03 04:25:17 +02:00
|
|
|
|
2004-04-30 15:07:26 +02:00
|
|
|
if (Cluster2 != READ_CLUSTER)
|
|
|
|
bp1->b_flag |= BFR_DIRTY | BFR_VALID;
|
2002-08-03 04:25:17 +02:00
|
|
|
|
|
|
|
fbp1 = &bp1->b_buffer[0];
|
2004-04-30 15:07:26 +02:00
|
|
|
}
|
|
|
|
|
2004-04-30 17:28:18 +02:00
|
|
|
idx = *fbp0 | (*fbp1 << 8);
|
2004-04-30 15:07:26 +02:00
|
|
|
if (Cluster2 == READ_CLUSTER)
|
|
|
|
{
|
|
|
|
/* Now to unpack the contents of the FAT entry. Odd and */
|
|
|
|
/* even bytes are packed differently. */
|
|
|
|
|
|
|
|
if (Cluster1 & 0x01)
|
2004-04-30 17:28:18 +02:00
|
|
|
idx >>= 4;
|
|
|
|
idx &= 0x0fff;
|
2004-04-30 15:07:26 +02:00
|
|
|
|
|
|
|
if (idx >= MASK12)
|
|
|
|
return LONG_LAST_CLUSTER;
|
|
|
|
if (idx == BAD12)
|
|
|
|
return LONG_BAD;
|
|
|
|
return idx;
|
2002-08-03 04:25:17 +02:00
|
|
|
}
|
|
|
|
|
2004-04-30 17:28:18 +02:00
|
|
|
/* Cluster2 may be set to LONG_LAST_CLUSTER == 0x0FFFFFFFUL or 0xFFFF */
|
|
|
|
/* -- please don't remove this mask! */
|
|
|
|
cluster = (unsigned)Cluster2 & 0x0fff;
|
|
|
|
|
2002-08-03 04:25:17 +02:00
|
|
|
/* Now pack the value in */
|
2004-02-07 19:04:50 +01:00
|
|
|
if ((unsigned)Cluster1 & 0x01)
|
2002-08-03 04:25:17 +02:00
|
|
|
{
|
2004-04-30 17:28:18 +02:00
|
|
|
idx &= 0x000f;
|
|
|
|
cluster <<= 4;
|
2002-08-03 04:25:17 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-04-30 17:28:18 +02:00
|
|
|
idx &= 0xf000;
|
2002-08-03 04:25:17 +02:00
|
|
|
}
|
2004-04-30 17:28:18 +02:00
|
|
|
cluster |= idx;
|
|
|
|
*fbp0 = (UBYTE)cluster;
|
|
|
|
*fbp1 = (UBYTE)(cluster >> 8);
|
2002-08-03 04:25:17 +02:00
|
|
|
}
|
2003-02-20 21:51:42 +01:00
|
|
|
else if (ISFAT16(dpbp))
|
2002-08-03 04:25:17 +02:00
|
|
|
{
|
|
|
|
/* form an index so that we can read the block as a */
|
|
|
|
/* byte array */
|
2004-04-30 15:07:26 +02:00
|
|
|
if (Cluster2 == READ_CLUSTER)
|
|
|
|
{
|
|
|
|
/* and get the cluster number */
|
|
|
|
UWORD res = fgetword(&bp->b_buffer[idx * 2]);
|
|
|
|
if (res >= MASK16)
|
|
|
|
return LONG_LAST_CLUSTER;
|
|
|
|
if (res == BAD16)
|
|
|
|
return LONG_BAD;
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
2002-08-03 04:25:17 +02:00
|
|
|
/* Finally, put the word into the buffer and mark the */
|
|
|
|
/* buffer as dirty. */
|
2004-04-29 14:48:18 +02:00
|
|
|
fputword(&bp->b_buffer[idx * 2], (UWORD)Cluster2);
|
2002-08-03 04:25:17 +02:00
|
|
|
}
|
2001-09-23 22:39:44 +02:00
|
|
|
#ifdef WITHFAT32
|
|
|
|
else if (ISFAT32(dpbp))
|
2002-08-03 04:25:17 +02:00
|
|
|
{
|
|
|
|
/* form an index so that we can read the block as a */
|
|
|
|
/* byte array */
|
2004-04-30 15:07:26 +02:00
|
|
|
if (Cluster2 == READ_CLUSTER)
|
|
|
|
{
|
|
|
|
UDWORD res = fgetlong(&bp->b_buffer[idx * 4]);
|
|
|
|
if (res > LONG_BAD)
|
|
|
|
return LONG_LAST_CLUSTER;
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
2002-08-03 04:25:17 +02:00
|
|
|
/* Finally, put the word into the buffer and mark the */
|
|
|
|
/* buffer as dirty. */
|
2004-04-29 14:48:18 +02:00
|
|
|
fputlong(&bp->b_buffer[idx * 4], Cluster2);
|
2002-08-03 04:25:17 +02:00
|
|
|
}
|
2001-09-23 22:39:44 +02:00
|
|
|
#endif
|
2000-05-06 21:34:20 +02:00
|
|
|
else
|
2004-04-30 15:07:26 +02:00
|
|
|
return 1;
|
2001-07-10 00:19:33 +02:00
|
|
|
|
2001-09-23 22:39:44 +02:00
|
|
|
/* update the free space count */
|
2002-08-03 04:25:17 +02:00
|
|
|
bp->b_flag |= BFR_DIRTY | BFR_VALID;
|
|
|
|
if (Cluster2 == FREE)
|
2001-11-04 20:47:39 +01:00
|
|
|
{
|
|
|
|
#ifdef WITHFAT32
|
|
|
|
if (ISFAT32(dpbp) && dpbp->dpb_xnfreeclst != XUNKNCLSTFREE)
|
|
|
|
{
|
|
|
|
/* update the free space count for returned */
|
2002-08-03 04:25:17 +02:00
|
|
|
/* cluster */
|
2001-11-04 20:47:39 +01:00
|
|
|
++dpbp->dpb_xnfreeclst;
|
|
|
|
write_fsinfo(dpbp);
|
2001-11-18 15:01:12 +01:00
|
|
|
}
|
|
|
|
else
|
2001-11-04 20:47:39 +01:00
|
|
|
#endif
|
2001-07-10 00:19:33 +02:00
|
|
|
if (dpbp->dpb_nfreeclst != UNKNCLSTFREE)
|
2001-11-04 20:47:39 +01:00
|
|
|
++dpbp->dpb_nfreeclst;
|
|
|
|
}
|
|
|
|
|
2001-11-18 15:01:12 +01:00
|
|
|
/*if (Cluster2 == FREE)
|
|
|
|
{ */
|
|
|
|
/* update the free space count for returned */
|
|
|
|
/* cluster */
|
|
|
|
/* ++dpbp->dpb_nfreeclst;
|
|
|
|
} */
|
|
|
|
|
|
|
|
/* update the free space count for removed */
|
|
|
|
/* cluster */
|
|
|
|
/* BUG: was counted twice for 2nd,.. cluster. moved to find_fat_free() */
|
|
|
|
/* BO: don't completely understand this yet - leave here for now as
|
|
|
|
a comment */
|
|
|
|
/* else
|
|
|
|
{
|
|
|
|
--dpbp->dpb_nfreeclst;
|
|
|
|
} */
|
2000-05-06 21:34:20 +02:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Given the disk parameters, and a cluster number, this function
|
|
|
|
looks at the FAT, and returns the next cluster in the clain. */
|
2001-11-18 15:01:12 +01:00
|
|
|
CLUSTER next_cluster(struct dpb FAR * dpbp, CLUSTER ClusterNum)
|
2001-09-23 22:39:44 +02:00
|
|
|
{
|
2004-04-30 15:07:26 +02:00
|
|
|
return link_fat(dpbp, ClusterNum, READ_CLUSTER);
|
2001-11-04 20:47:39 +01:00
|
|
|
}
|
|
|
|
|