* Written by Paul Popelka (paulp@uts.amdahl.com)
* You can do anything you want with this software,
* just don't say you wrote it,
* and don't remove this notice.
* This software is provided "as is".
* The author supplies this software to be publicly
* redistributed on the understanding that the author
* is not responsible for the correct functioning of
* this software in any circumstances and is not liable
* for any damages caused by this software.
* $Id: pcfs_fat.c,v 1.2 1993/10/16 19:29:34 rgrimes Exp $
#include "mount.h" /* to define statfs structure */
#include "vnode.h" /* to define vattr structure */
static void fc_lookup(struct denode
*, u_long
, u_long
*, u_long
*);
int fc_fileextends
; /* # of file extends */
int fc_lfcempty
; /* # of time last file cluster cache entry
int fc_bmapcalls
; /* # of times pcbmap was called */
int fc_lmdistance
[LMMAX
]; /* counters for how far off the last cluster
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 (pmp
, ofs
, bnp
, sizep
, bop
)
bn
= ofs
/ pmp
->pm_fatblocksize
* pmp
->pm_fatblocksec
;
size
= min (pmp
->pm_fatblocksec
, pmp
->pm_FATsecs
- bn
)
*bop
= ofs
% pmp
->pm_fatblocksize
;
* Map the logical cluster number of a file into
* a physical disk sector that is filesystem relative.
* dep - address of denode representing the file of interest
* findcn - file relative cluster whose filesystem relative
* cluster number and/or block number are/is to be found
* bnp - address of where to place the file system relative
* block number. If this pointer is null then don't return
* cnp - address of where to place the file system relative
* cluster number. If this pointer is null then don't return
* Either bnp or cnp must be non-null.
* This function has one side effect. If the requested
* file relative cluster is beyond the end of file, then
* the actual number of clusters in the file is returned
* in *cnp. This is useful for determining how long a
* directory is. If cnp is null, nothing is returned.
pcbmap(dep
, findcn
, bnp
, cnp
)
u_long findcn
; /* file relative cluster to get */
daddr_t
*bnp
; /* returned filesys relative blk number */
u_long
*cnp
; /* returned cluster number */
struct pcfsmount
*pmp
= dep
->de_pmp
;
int fat12
= FAT12(pmp
); /* 12 bit fat */
* If they don't give us someplace to return a value
* then don't bother doing anything.
if (bnp
== NULL
&& cnp
== NULL
)
cn
= dep
->de_StartCluster
;
* The "file" that makes up the root directory is contiguous,
* permanently allocated, of fixed size, and is not made up
* of clusters. If the cluster number is beyond the end of
* the root directory, then return the number of clusters in
if (dep
->de_Attributes
& ATTR_DIRECTORY
) {
if (findcn
* pmp
->pm_SectPerClust
> pmp
->pm_rootdirsize
) {
*cnp
= pmp
->pm_rootdirsize
/ pmp
->pm_SectPerClust
;
*bnp
= pmp
->pm_rootdirblk
+ (findcn
* pmp
->pm_SectPerClust
);
else { /* just an empty file */
* Rummage around in the fat cache, maybe we can avoid
* tromping thru every fat entry for the file.
* And, keep track of how far off the cache was from
fc_lookup(dep
, findcn
, &i
, &cn
);
if ((bn
= findcn
- i
) >= LMMAX
)
* Handle all other files or directories the normal way.
for (; i
< findcn
; i
++) {
byteoffset
= FATOFS(pmp
, cn
);
fatblock(pmp
, byteoffset
, &bn
, &bsize
, &bo
);
error
= bread(pmp
->pm_devvp
, bn
, bsize
, NOCRED
, &bp
);
cn
= getushort(&bp
->b_un
.b_addr
[bo
]);
* 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 pcfs think it is always
* dealing with 16 bit fats.
if ((cn
& 0x0ff0) == 0x0ff0)
fc_setcache(dep
, FC_LASTMAP
, i
, cn
);
/* update last file cluster entry in the fat cache */
fc_setcache(dep
, FC_LASTFC
, i
-1, prevcn
);
* Find the closest entry in the fat cache to the
* cluster we are looking for.
fc_lookup(dep
, findcn
, frcnp
, fsrcnp
)
struct fatcache
*closest
= 0;
for (i
= 0; i
< FC_SIZE
; i
++) {
cn
= dep
->de_fc
[i
].fc_frcn
;
if (cn
!= FCE_EMPTY
&& cn
<= findcn
) {
if (closest
== 0 || cn
> closest
->fc_frcn
)
closest
= &dep
->de_fc
[i
];
*frcnp
= closest
->fc_frcn
;
*fsrcnp
= closest
->fc_fsrcn
;
* Purge the fat cache in denode dep of all entries
* relating to file relative cluster frcn and beyond.
for (i
= 0; i
< FC_SIZE
; i
++, fcp
++) {
if (fcp
->fc_frcn
!= FCE_EMPTY
&& fcp
->fc_frcn
>= frcn
)
fcp
->fc_frcn
= FCE_EMPTY
;
* Once the first fat is updated the other copies of
* the fat must also be updated. This function does
* pmp - pcfsmount structure for filesystem to update
* bp - addr of modified fat block
* fatbn - block number relative to begin of filesystem
* of the modified fat block.
updateotherfats(pmp
, bp
, fatbn
)
printf("updateotherfats(pmp %08x, bp %08x, fatbn %d)\n",
#endif /* defined(PCFSDEBUG) */
* 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
);
bcopy(bp
->b_un
.b_addr
, bpn
->b_un
.b_addr
,
* Updating entries in 12 bit fats is a pain in the butt.
* The following picture shows where nibbles go when
* moving from a 12 bit cluster number into the appropriate
* byte m byte m+1 byte m+2
* +----+----+ +----+----+ +----+----+
* | 0 1 | | 2 3 | | 4 5 | FAT bytes
* +----+----+ +----+----+ +----+----+
* +----+----+----+ +----+----+----+
* +----+----+----+ +----+----+----+
* (Function no longer used)
usemap_alloc (struct pcfsmount
*pmp
, u_long cn
)
pmp
->pm_inusemap
[cn
/ 8] |= 1 << (cn
% 8);
pmp
->pm_freeclustercount
--;
/* This assumes that the lowest available cluster was allocated */
pmp
->pm_lookhere
= cn
+ 1;
usemap_free (struct pcfsmount
*pmp
, u_long cn
)
pmp
->pm_freeclustercount
++;
pmp
->pm_inusemap
[cn
/ 8] &= ~(1 << (cn
% 8));
if (pmp
->pm_lookhere
> cn
)
clusterfree(pmp
, cluster
, oldcnp
)
error
= fatentry(FAT_GET_AND_SET
, pmp
, cluster
, &oldcn
, PCFSFREE
);
* 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
);
* Get or Set or 'Get and Set' the cluster'th entry in the
* function - whether to get or set a fat entry
* pmp - address of the pcfsmount structure for the
* filesystem whose fat is to be manipulated.
* cluster - which cluster is of interest
* oldcontents - address of a word that is to receive
* the contents of the cluster'th entry if this is
* newcontents - the new value to be written into the
* cluster'th element of the fat if this is a set
* This function can also be used to free a cluster
* by setting the fat entry for a cluster to 0.
* All copies of the fat are updated if this is a set
* If fatentry() marks a cluster as free it does not
* update the inusemap in the pcfsmount structure.
* This is left to the caller.
fatentry(function
, pmp
, cn
, oldcontents
, newcontents
)
u_long bn
, bo
, bsize
, byteoffset
;
/*printf("fatentry(func %d, pmp %08x, clust %d, oldcon %08x, newcon %d)\n",
function, pmp, cluster, oldcontents, newcontents);*/
* Be sure they asked us to do something.
if ((function
& (FAT_SET
| FAT_GET
)) == 0) {
printf("fatentry(): function code doesn't specify get or set\n");
* If they asked us to return a cluster number
* but didn't tell us where to put it, give them
if ((function
& FAT_GET
) && oldcontents
== NULL
) {
printf("fatentry(): get function with no place to put result\n");
* Be sure the requested cluster is in the filesystem.
if (cn
< CLUST_FIRST
|| cn
> pmp
->pm_maxcluster
)
byteoffset
= FATOFS(pmp
, cn
);
fatblock(pmp
, byteoffset
, &bn
, &bsize
, &bo
);
error
= bread(pmp
->pm_devvp
, bn
, bsize
, NOCRED
, &bp
);
if (function
& FAT_GET
) {
readcn
= getushort(&bp
->b_un
.b_addr
[bo
]);
/* map certain 12 bit fat entries to 16 bit */
if ((readcn
& 0x0ff0) == 0x0ff0)
if (function
& FAT_SET
) {
readcn
= getushort(&bp
->b_un
.b_addr
[bo
]);
readcn
|= (newcontents
<< 4);
readcn
|= (newcontents
<< 0);
putushort(&bp
->b_un
.b_addr
[bo
], readcn
);
putushort(&bp
->b_un
.b_addr
[bo
], newcontents
);
updateotherfats(pmp
, bp
, bn
);
* Write out the first fat last.
* Allocate a free cluster.
* retcluster - put the allocated cluster's number here.
* fillwith - put this value into the fat entry for the
clusteralloc(pmp
, retcluster
, fillwith
)
u_long idx
, max_idx
, bit
, map
;
max_idx
= pmp
->pm_maxcluster
/ 8;
for (idx
= pmp
->pm_lookhere
/ 8; idx
<= max_idx
; idx
++) {
map
= pmp
->pm_inusemap
[idx
];
for (bit
= 0; bit
< 8; bit
++) {
if ((map
& (1 << bit
)) == 0) {
error
= fatentry(FAT_SET
, pmp
, cn
, 0, fillwith
);
printf("clusteralloc(): allocated cluster %d\n", cn
);
#endif /* defined(PCFSDEBUG) */
* Free a chain of clusters.
* pmp - address of the pcfs mount structure for the
* filesystem containing the cluster chain to be freed.
* startcluster - number of the 1st cluster in the chain
* of clusters to be freed.
freeclusterchain(pmp
, startcluster
)
while (startcluster
>= CLUST_FIRST
&& startcluster
<= pmp
->pm_maxcluster
) {
error
= clusterfree(pmp
, startcluster
, &nextcluster
);
printf("freeclusterchain(): free failed, cluster %d\n",
startcluster
= nextcluster
;
* Read in fat blocks looking for free clusters.
* For every free cluster found turn off its
* corresponding bit in the pm_inusemap.
u_long bn
, bo
, bsize
, byteoffset
;
* Mark all clusters in use, we mark the free ones in the
* fat scan loop further down.
for (cn
= 0; cn
< (pmp
->pm_maxcluster
>> 3) + 1; cn
++)
pmp
->pm_inusemap
[cn
] = 0xff;
* Figure how many free clusters are in the filesystem
* by ripping thougth the fat counting the number of
* entries whose content is zero. These represent free
pmp
->pm_freeclustercount
= 0;
pmp
->pm_lookhere
= pmp
->pm_maxcluster
+ 1;
for (cn
= CLUST_FIRST
; cn
<= pmp
->pm_maxcluster
; cn
++) {
byteoffset
= FATOFS(pmp
, cn
);
bo
= byteoffset
% pmp
->pm_fatblocksize
;
fatblock(pmp
, byteoffset
, &bn
, &bsize
, NULL
);
error
= bread(pmp
->pm_devvp
, bn
, bsize
, NOCRED
, &bp
);
readcn
= getushort(&bp
->b_un
.b_addr
[bo
]);
* Allocate a new cluster and chain it onto the end of the
* dep - the file to extend
* bpp - where to return the address of the buf header for the
* ncp - where to put cluster number of the newly allocated file block
* If this pointer is 0, do not return the cluster number.
* This function is not responsible for turning on the DEUPD
* bit if the de_flag field of the denode and it does not
* change the de_FileSize field. This is left for the caller
extendfile(dep
, bpp
, ncp
)
struct pcfsmount
*pmp
= dep
->de_pmp
;
* Don't try to extend the root directory
if (DETOV(dep
)->v_flag
& VROOT
) {
printf("extendfile(): attempt to extend root directory\n");
* If the "file's last cluster" cache entry is empty,
* and the file is not empty,
* then fill the cache entry by calling pcbmap().
if (dep
->de_fc
[FC_LASTFC
].fc_frcn
== FCE_EMPTY
&&
dep
->de_StartCluster
!= 0) {
error
= pcbmap(dep
, 0xffff, 0, &cn
);
/* we expect it to return E2BIG */
* Allocate another cluster and chain onto the end of the file.
* If the file is empty we make de_StartCluster point to the
* new block. Note that de_StartCluster being 0 is sufficient
* to be sure the file is empty since we exclude attempts to
* extend the root directory above, and the root dir is the
* only file with a startcluster of 0 that has blocks allocated
if (error
= clusteralloc(pmp
, &cn
, CLUST_EOFE
))
if (dep
->de_StartCluster
== 0) {
dep
->de_StartCluster
= cn
;
error
= fatentry(FAT_SET
, pmp
, dep
->de_fc
[FC_LASTFC
].fc_fsrcn
,
clusterfree(pmp
, cn
, NULL
);
frcn
= dep
->de_fc
[FC_LASTFC
].fc_frcn
+ 1;
* Update the "last cluster of the file" entry in the denode's
fc_setcache(dep
, FC_LASTFC
, frcn
, cn
);
* Get the buf header for the new block of the file.
if (dep
->de_Attributes
& ATTR_DIRECTORY
) {
*bpp
= getblk(pmp
->pm_devvp
, cntobn(pmp
, cn
),
*bpp
= getblk(DETOV(dep
), frcn
,
* Give them the filesystem relative cluster number