summaryrefslogtreecommitdiff
path: root/sys/msdosfs/msdosfs_fat.c
diff options
context:
space:
mode:
authorNiels Provos <provos@cvs.openbsd.org>1998-01-11 20:39:12 +0000
committerNiels Provos <provos@cvs.openbsd.org>1998-01-11 20:39:12 +0000
commit487fe4efe824e828cdefbee41c1d03ef04a885f3 (patch)
tree49663eae55a517c8ad3c2220a6ede523fa5dc975 /sys/msdosfs/msdosfs_fat.c
parent6607731225347e583be10d914e5758d7a9726fc9 (diff)
FAT32 support from NetBSD by Wolfgang Solfrank.
Diffstat (limited to 'sys/msdosfs/msdosfs_fat.c')
-rw-r--r--sys/msdosfs/msdosfs_fat.c241
1 files changed, 163 insertions, 78 deletions
diff --git a/sys/msdosfs/msdosfs_fat.c b/sys/msdosfs/msdosfs_fat.c
index fe044d1a6e3..a215bdc98bc 100644
--- a/sys/msdosfs/msdosfs_fat.c
+++ b/sys/msdosfs/msdosfs_fat.c
@@ -1,9 +1,9 @@
-/* $OpenBSD: msdosfs_fat.c,v 1.4 1997/03/02 18:01:56 millert Exp $ */
-/* $NetBSD: msdosfs_fat.c,v 1.24 1996/10/13 04:16:32 christos Exp $ */
+/* $OpenBSD: msdosfs_fat.c,v 1.5 1998/01/11 20:39:08 provos Exp $ */
+/* $NetBSD: msdosfs_fat.c,v 1.26 1997/10/17 11:24:02 ws Exp $ */
/*-
- * Copyright (C) 1994, 1995 Wolfgang Solfrank.
- * Copyright (C) 1994, 1995 TooLs GmbH.
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
* All rights reserved.
* Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
*
@@ -83,9 +83,6 @@ int fc_lmdistance[LMMAX]; /* counters for how far off the last
* cluster mapped entry was. */
int fc_largedistance; /* off by more than LMMAX */
-/* Byte offset in FAT on filesystem pmp, cluster cn */
-#define FATOFS(pmp, cn) (FAT12(pmp) ? (cn) * 3 / 2 : (cn) * 2)
-
static void fatblock __P((struct msdosfsmount *, u_long, u_long *, u_long *,
u_long *));
void updatefats __P((struct msdosfsmount *, struct buf *, u_long));
@@ -109,7 +106,8 @@ fatblock(pmp, ofs, bnp, sizep, bop)
bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec;
size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn)
* pmp->pm_BytesPerSec;
- bn += pmp->pm_fatblk;
+ bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs;
+
if (bnp)
*bnp = bn;
if (sizep)
@@ -155,7 +153,6 @@ pcbmap(dep, findcn, bnp, cnp, sp)
u_long bp_bn = -1;
struct msdosfsmount *pmp = dep->de_pmp;
u_long bsize;
- int fat12 = FAT12(pmp); /* 12 bit fat */
fc_bmapcalls++;
@@ -217,7 +214,10 @@ pcbmap(dep, findcn, bnp, cnp, sp)
* Handle all other files or directories the normal way.
*/
for (; i < findcn; i++) {
- if (MSDOSFSEOF(cn))
+ /*
+ * Stop with all reserved clusters, not just with EOF.
+ */
+ if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
goto hiteof;
byteoffset = FATOFS(pmp, cn);
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
@@ -232,23 +232,25 @@ pcbmap(dep, findcn, bnp, cnp, sp)
bp_bn = bn;
}
prevcn = cn;
- cn = getushort(&bp->b_data[bo]);
- if (fat12) {
- if (prevcn & 1)
- cn >>= 4;
- cn &= 0x0fff;
- /*
- * Force the special cluster numbers in the range
- * 0x0ff0-0x0fff to be the same as for 16 bit
- * cluster numbers to let the rest of msdosfs think
- * it is always dealing with 16 bit fats.
- */
- if ((cn & 0x0ff0) == 0x0ff0)
- cn |= 0xf000;
- }
+ if (FAT32(pmp))
+ cn = getulong(&bp->b_data[bo]);
+ else
+ cn = getushort(&bp->b_data[bo]);
+ if (FAT12(pmp) && (prevcn & 1))
+ cn >>= 4;
+ cn &= pmp->pm_fatmask;
+
+ /*
+ * Force the special cluster numbers
+ * to be the same for all cluster sizes
+ * to let the rest of msdosfs handle
+ * all cases the same.
+ */
+ if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
+ cn |= ~pmp->pm_fatmask;
}
- if (!MSDOSFSEOF(cn)) {
+ if (!MSDOSFSEOF(pmp, cn)) {
if (bp)
brelse(bp);
if (bnp)
@@ -317,7 +319,9 @@ fc_purge(dep, frcn)
}
/*
- * Update all copies of the fat. The first copy is updated last.
+ * Update the fat.
+ * If mirroring the fat, update all copies, with the first copy as last.
+ * Else update only the current fat (ignoring the others).
*
* pmp - msdosfsmount structure for filesystem to update
* bp - addr of modified fat block
@@ -338,32 +342,76 @@ updatefats(pmp, bp, fatbn)
#endif
/*
- * Now copy the block(s) of the modified fat to the other copies of
- * the fat and write them out. This is faster than reading in the
- * other fats and then writing them back out. This could tie up
- * the fat for quite a while. Preventing others from accessing it.
- * To prevent us from going after the fat quite so much we use
- * delayed writes, unless they specfied "synchronous" when the
- * filesystem was mounted. If synch is asked for then use
- * bwrite()'s and really slow things down.
+ * If we have an FSInfo block, update it.
*/
- for (i = 1; i < pmp->pm_FATs; i++) {
- fatbn += pmp->pm_FATsecs;
- /* getblk() never fails */
- bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount, 0, 0);
- bcopy(bp->b_data, bpn->b_data, bp->b_bcount);
- if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT)
- bwrite(bpn);
- else
- bdwrite(bpn);
+ if (pmp->pm_fsinfo) {
+ u_long cn = pmp->pm_nxtfree;
+
+ if (pmp->pm_freeclustercount
+ && (pmp->pm_inusemap[cn / N_INUSEBITS]
+ & (1 << (cn % N_INUSEBITS)))) {
+ /*
+ * The cluster indicated in FSInfo isn't free
+ * any longer. Got get a new free one.
+ */
+ for (cn = 0; cn < pmp->pm_maxcluster;)
+ if (pmp->pm_inusemap[cn / N_INUSEBITS] != (u_int)-1)
+ break;
+ pmp->pm_nxtfree = cn
+ + ffs(pmp->pm_inusemap[cn / N_INUSEBITS]
+ ^ (u_int)-1) - 1;
+ }
+ if (bread(pmp->pm_devvp, pmp->pm_fsinfo, 1024, NOCRED, &bpn) != 0) {
+ /*
+ * Ignore the error, but turn off FSInfo update for the future.
+ */
+ pmp->pm_fsinfo = 0;
+ brelse(bpn);
+ } else {
+ struct fsinfo *fp = (struct fsinfo *)bpn->b_data;
+
+ putulong(fp->fsinfree, pmp->pm_freeclustercount);
+ putulong(fp->fsinxtfree, pmp->pm_nxtfree);
+ if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT)
+ bwrite(bpn);
+ else
+ bdwrite(bpn);
+ }
}
+
+ if (pmp->pm_flags & MSDOSFS_FATMIRROR) {
+ /*
+ * Now copy the block(s) of the modified fat to the other copies of
+ * the fat and write them out. This is faster than reading in the
+ * other fats and then writing them back out. This could tie up
+ * the fat for quite a while. Preventing others from accessing it.
+ * To prevent us from going after the fat quite so much we use
+ * delayed writes, unless they specfied "synchronous" when the
+ * filesystem was mounted. If synch is asked for then use
+ * bwrite()'s and really slow things down.
+ */
+ for (i = 1; i < pmp->pm_FATs; i++) {
+ fatbn += pmp->pm_FATsecs;
+ /* getblk() never fails */
+ bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount, 0, 0);
+ bcopy(bp->b_data, bpn->b_data, bp->b_bcount);
+ if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT)
+ bwrite(bpn);
+ else
+ bdwrite(bpn);
+ }
+ }
+
/*
- * Write out the first fat last.
+ * Write out the first (or current) fat last.
*/
if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT)
bwrite(bp);
else
bdwrite(bp);
+ /*
+ * Maybe update fsinfo sector here?
+ */
}
/*
@@ -414,15 +462,17 @@ clusterfree(pmp, cluster, oldcnp)
int error;
u_long oldcn;
+ usemap_free(pmp, cluster);
error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE);
- if (error)
+ if (error) {
+ usemap_alloc(pmp, cluster);
return (error);
+ }
/*
* If the cluster was successfully marked free, then update
* the count of free clusters, and turn off the "allocated"
* bit in the "in use" cluster bit map.
*/
- usemap_free(pmp, cluster);
if (oldcnp)
*oldcnp = oldcn;
return (0);
@@ -460,7 +510,7 @@ fatentry(function, pmp, cn, oldcontents, newcontents)
u_long bn, bo, bsize, byteoffset;
struct buf *bp;
-#if 0
+#ifdef MSDOSFS_DEBUG
printf("fatentry(func %d, pmp %08x, clust %d, oldcon %08x, newcon %d)\n",
function, pmp, cluster, oldcontents, newcontents);
#endif
@@ -498,19 +548,21 @@ fatentry(function, pmp, cn, oldcontents, newcontents)
}
if (function & FAT_GET) {
- readcn = getushort(&bp->b_data[bo]);
- if (FAT12(pmp)) {
- if (cn & 1)
- readcn >>= 4;
- readcn &= 0x0fff;
- /* map certain 12 bit fat entries to 16 bit */
- if ((readcn & 0x0ff0) == 0x0ff0)
- readcn |= 0xf000;
- }
+ if (FAT32(pmp))
+ readcn = getulong(&bp->b_data[bo]);
+ else
+ readcn = getushort(&bp->b_data[bo]);
+ if (FAT12(pmp) & (cn & 1))
+ readcn >>= 4;
+ readcn &= pmp->pm_fatmask;
+ /* map reserved fat entries to same values for all fats */
+ if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
+ readcn |= ~pmp->pm_fatmask;
*oldcontents = readcn;
}
if (function & FAT_SET) {
- if (FAT12(pmp)) {
+ switch (pmp->pm_fatmask) {
+ case FAT12_MASK:
readcn = getushort(&bp->b_data[bo]);
if (cn & 1) {
readcn &= 0x000f;
@@ -520,8 +572,21 @@ fatentry(function, pmp, cn, oldcontents, newcontents)
readcn |= newcontents & 0xfff;
}
putushort(&bp->b_data[bo], readcn);
- } else
+ break;
+ case FAT16_MASK:
putushort(&bp->b_data[bo], newcontents);
+ break;
+ case FAT32_MASK:
+ /*
+ * According to spec we have to retain the
+ * high order bits of the fat entry.
+ */
+ readcn = getulong(&bp->b_data[bo]);
+ readcn &= ~FAT32_MASK;
+ readcn |= newcontents & FAT32_MASK;
+ putulong(&bp->b_data[bo], readcn);
+ break;
+ }
updatefats(pmp, bp, bn);
bp = NULL;
pmp->pm_fmod = 1;
@@ -571,7 +636,8 @@ fatchain(pmp, start, count, fillwith)
while (count > 0) {
start++;
newc = --count > 0 ? start : fillwith;
- if (FAT12(pmp)) {
+ switch (pmp->pm_fatmask) {
+ case FAT12_MASK:
readcn = getushort(&bp->b_data[bo]);
if (start & 1) {
readcn &= 0xf000;
@@ -584,9 +650,18 @@ fatchain(pmp, start, count, fillwith)
bo++;
if (!(start & 1))
bo++;
- } else {
+ break;
+ case FAT16_MASK:
putushort(&bp->b_data[bo], newc);
bo += 2;
+ break;
+ case FAT32_MASK:
+ readcn = getulong(&bp->b_data[bo]);
+ readcn &= ~pmp->pm_fatmask;
+ readcn |= newc & pmp->pm_fatmask;
+ putulong(&bp->b_data[bo], readcn);
+ bo += 4;
+ break;
}
if (bo >= bsize)
break;
@@ -659,7 +734,10 @@ chainalloc(pmp, start, count, fillwith, retcluster, got)
u_long *got;
{
int error;
-
+ u_long cl, n;
+
+ for (cl = start, n = count; n-- > 0;)
+ usemap_alloc(pmp, cl++);
if ((error = fatchain(pmp, start, count, fillwith)) != 0)
return (error);
#ifdef MSDOSFS_DEBUG
@@ -670,8 +748,6 @@ chainalloc(pmp, start, count, fillwith, retcluster, got)
*retcluster = start;
if (got)
*got = count;
- while (count-- > 0)
- usemap_alloc(pmp, start++);
return (0);
}
@@ -801,8 +877,9 @@ freeclusterchain(pmp, cluster)
lbn = bn;
}
usemap_free(pmp, cluster);
- readcn = getushort(&bp->b_data[bo]);
- if (FAT12(pmp)) {
+ switch (pmp->pm_fatmask) {
+ case FAT12_MASK:
+ readcn = getushort(&bp->b_data[bo]);
if (cluster & 1) {
cluster = readcn >> 4;
readcn &= 0x000f;
@@ -813,13 +890,20 @@ freeclusterchain(pmp, cluster)
readcn |= MSDOSFSFREE & 0xfff;
}
putushort(&bp->b_data[bo], readcn);
- cluster &= 0x0fff;
- if ((cluster&0x0ff0) == 0x0ff0)
- cluster |= 0xf000;
- } else {
- cluster = readcn;
+ break;
+ case FAT16_MASK:
+ cluster = getushort(&bp->b_data[bo]);
putushort(&bp->b_data[bo], MSDOSFSFREE);
+ break;
+ case FAT32_MASK:
+ cluster = getulong(&bp->b_data[bo]);
+ putulong(&bp->b_data[bo],
+ (MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK));
+ break;
}
+ cluster &= pmp->pm_fatmask;
+ if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD)
+ cluster |= pmp->pm_fatmask;
}
if (bp)
updatefats(pmp, bp, bn);
@@ -837,7 +921,6 @@ fillinusemap(pmp)
struct buf *bp = NULL;
u_long cn, readcn;
int error;
- int fat12 = FAT12(pmp);
u_long bn, bo, bsize, byteoffset;
/*
@@ -867,12 +950,13 @@ fillinusemap(pmp)
return (error);
}
}
- readcn = getushort(&bp->b_data[bo]);
- if (fat12) {
- if (cn & 1)
- readcn >>= 4;
- readcn &= 0x0fff;
- }
+ if (FAT32(pmp))
+ readcn = getulong(&bp->b_data[bo]);
+ else
+ readcn = getushort(&bp->b_data[bo]);
+ if (FAT12(pmp) && (cn & 1))
+ readcn >>= 4;
+ readcn &= pmp->pm_fatmask;
if (readcn == 0)
usemap_free(pmp, cn);
@@ -913,7 +997,8 @@ extendfile(dep, count, bpp, ncp, flags)
/*
* Don't try to extend the root directory
*/
- if (DETOV(dep)->v_flag & VROOT) {
+ if (dep->de_StartCluster == MSDOSFSROOT
+ && (dep->de_Attributes & ATTR_DIRECTORY)) {
printf("extendfile(): attempt to extend root directory\n");
return (ENOSPC);
}