diff options
author | Niels Provos <provos@cvs.openbsd.org> | 1998-01-11 20:39:12 +0000 |
---|---|---|
committer | Niels Provos <provos@cvs.openbsd.org> | 1998-01-11 20:39:12 +0000 |
commit | 487fe4efe824e828cdefbee41c1d03ef04a885f3 (patch) | |
tree | 49663eae55a517c8ad3c2220a6ede523fa5dc975 /sys/msdosfs/msdosfs_fat.c | |
parent | 6607731225347e583be10d914e5758d7a9726fc9 (diff) |
FAT32 support from NetBSD by Wolfgang Solfrank.
Diffstat (limited to 'sys/msdosfs/msdosfs_fat.c')
-rw-r--r-- | sys/msdosfs/msdosfs_fat.c | 241 |
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); } |