This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / sys / pcfs / pcfs_fat.c
CommitLineData
15637ed4
RG
1/*
2 * Written by Paul Popelka (paulp@uts.amdahl.com)
3 *
4 * You can do anything you want with this software,
5 * just don't say you wrote it,
6 * and don't remove this notice.
7 *
8 * This software is provided "as is".
9 *
10 * The author supplies this software to be publicly
11 * redistributed on the understanding that the author
12 * is not responsible for the correct functioning of
13 * this software in any circumstances and is not liable
14 * for any damages caused by this software.
15 *
16 * October 1992
17 *
78ed81a3 18 * $Id$
15637ed4
RG
19 */
20
21/*
22 * kernel include files.
23 */
24#include "param.h"
25#include "systm.h"
26#include "buf.h"
27#include "file.h"
28#include "namei.h"
29#include "mount.h" /* to define statfs structure */
30#include "vnode.h" /* to define vattr structure */
31#include "errno.h"
32
33/*
34 * pcfs include files.
35 */
36#include "bpb.h"
37#include "pcfsmount.h"
38#include "direntry.h"
39#include "denode.h"
40#include "fat.h"
41
42/*
43 * Fat cache stats.
44 */
45int fc_fileextends; /* # of file extends */
46int fc_lfcempty; /* # of time last file cluster cache entry
47 * was empty */
48int fc_bmapcalls; /* # of times pcbmap was called */
49#define LMMAX 20
50int fc_lmdistance[LMMAX]; /* counters for how far off the last cluster
51 * mapped entry was. */
52int fc_largedistance; /* off by more than LMMAX */
53
54/* Byte offset in FAT on filesystem pmp, cluster cn */
55#define FATOFS(pmp, cn) (FAT12(pmp) ? (cn) * 3 / 2 : (cn) * 2)
56
57
58static void fatblock (pmp, ofs, bnp, sizep, bop)
59 struct pcfsmount *pmp;
60 u_long ofs;
61 u_long *bnp;
62 u_long *sizep;
63 u_long *bop;
64{
65 u_long bn, size;
66
67 bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec;
68 size = min (pmp->pm_fatblocksec, pmp->pm_FATsecs - bn)
69 * pmp->pm_BytesPerSec;
70 bn += pmp->pm_fatblk;
71 if (bnp)
72 *bnp = bn;
73 if (sizep)
74 *sizep = size;
75 if (bop)
76 *bop = ofs % pmp->pm_fatblocksize;
77}
78
79/*
80 * Map the logical cluster number of a file into
81 * a physical disk sector that is filesystem relative.
82 * dep - address of denode representing the file of interest
83 * findcn - file relative cluster whose filesystem relative
84 * cluster number and/or block number are/is to be found
85 * bnp - address of where to place the file system relative
86 * block number. If this pointer is null then don't return
87 * this quantity.
88 * cnp - address of where to place the file system relative
89 * cluster number. If this pointer is null then don't return
90 * this quantity.
91 * NOTE:
92 * Either bnp or cnp must be non-null.
93 * This function has one side effect. If the requested
94 * file relative cluster is beyond the end of file, then
95 * the actual number of clusters in the file is returned
96 * in *cnp. This is useful for determining how long a
97 * directory is. If cnp is null, nothing is returned.
98 */
99int
100pcbmap(dep, findcn, bnp, cnp)
101 struct denode *dep;
102 u_long findcn; /* file relative cluster to get */
103 daddr_t *bnp; /* returned filesys relative blk number */
104 u_long *cnp; /* returned cluster number */
105{
106 int error;
107 u_long i;
108 u_long cn;
109 u_long prevcn;
110 u_long byteoffset;
111 u_long bn;
112 u_long bo;
113 struct buf *bp = NULL;
114 u_long bp_bn = -1;
115 struct pcfsmount *pmp = dep->de_pmp;
116 u_long bsize;
117 int fat12 = FAT12(pmp); /* 12 bit fat */
118
119 fc_bmapcalls++;
120
121/*
122 * If they don't give us someplace to return a value
123 * then don't bother doing anything.
124 */
125 if (bnp == NULL && cnp == NULL)
126 return 0;
127
128 cn = dep->de_StartCluster;
129/*
130 * The "file" that makes up the root directory is contiguous,
131 * permanently allocated, of fixed size, and is not made up
132 * of clusters. If the cluster number is beyond the end of
133 * the root directory, then return the number of clusters in
134 * the file.
135 */
136 if (cn == PCFSROOT) {
137 if (dep->de_Attributes & ATTR_DIRECTORY) {
138 if (findcn * pmp->pm_SectPerClust > pmp->pm_rootdirsize) {
139 if (cnp)
140 *cnp = pmp->pm_rootdirsize / pmp->pm_SectPerClust;
141 return E2BIG;
142 }
143 if (bnp)
144 *bnp = pmp->pm_rootdirblk + (findcn * pmp->pm_SectPerClust);
145 if (cnp)
146 *cnp = PCFSROOT;
147 return 0;
148 }
149 else { /* just an empty file */
150 if (cnp)
151 *cnp = 0;
152 return E2BIG;
153 }
154 }
155
156/*
157 * Rummage around in the fat cache, maybe we can avoid
158 * tromping thru every fat entry for the file.
159 * And, keep track of how far off the cache was from
160 * where we wanted to be.
161 */
162 i = 0;
163 fc_lookup(dep, findcn, &i, &cn);
164 if ((bn = findcn - i) >= LMMAX)
165 fc_largedistance++;
166 else
167 fc_lmdistance[bn]++;
168
169/*
170 * Handle all other files or directories the normal way.
171 */
172 for (; i < findcn; i++) {
173 if (PCFSEOF(cn))
174 goto hiteof;
175 byteoffset = FATOFS(pmp, cn);
176 fatblock(pmp, byteoffset, &bn, &bsize, &bo);
177 if (bn != bp_bn) {
178 if (bp)
179 brelse(bp);
180 error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
181 if (error)
182 return error;
183 bp_bn = bn;
184 }
185 prevcn = cn;
186 cn = getushort(&bp->b_un.b_addr[bo]);
187 if (fat12) {
188 if (prevcn & 1)
189 cn >>= 4;
190 cn &= 0x0fff;
191/*
192 * Force the special cluster numbers in the range
193 * 0x0ff0-0x0fff to be the same as for 16 bit cluster
194 * numbers to let the rest of pcfs think it is always
195 * dealing with 16 bit fats.
196 */
197 if ((cn & 0x0ff0) == 0x0ff0)
198 cn |= 0xf000;
199 }
200 }
201
202 if (!PCFSEOF(cn)) {
203 if (bp)
204 brelse(bp);
205 if (bnp)
206 *bnp = cntobn(pmp, cn);
207 if (cnp)
208 *cnp = cn;
209 fc_setcache(dep, FC_LASTMAP, i, cn);
210 return 0;
211 }
212
213hiteof:;
214 if (cnp)
215 *cnp = i;
216 if (bp)
217 brelse(bp);
218 /* update last file cluster entry in the fat cache */
219 fc_setcache(dep, FC_LASTFC, i-1, prevcn);
220 return E2BIG;
221}
222
223/*
224 * Find the closest entry in the fat cache to the
225 * cluster we are looking for.
226 */
227fc_lookup(dep, findcn, frcnp, fsrcnp)
228 struct denode *dep;
229 u_long findcn;
230 u_long *frcnp;
231 u_long *fsrcnp;
232{
233 int i;
234 u_long cn;
235 struct fatcache *closest = 0;
236
237 for (i = 0; i < FC_SIZE; i++) {
238 cn = dep->de_fc[i].fc_frcn;
239 if (cn != FCE_EMPTY && cn <= findcn) {
240 if (closest == 0 || cn > closest->fc_frcn)
241 closest = &dep->de_fc[i];
242 }
243 }
244 if (closest) {
245 *frcnp = closest->fc_frcn;
246 *fsrcnp = closest->fc_fsrcn;
247 }
248}
249
250/*
251 * Purge the fat cache in denode dep of all entries
252 * relating to file relative cluster frcn and beyond.
253 */
254fc_purge(dep, frcn)
255 struct denode *dep;
256 u_int frcn;
257{
258 int i;
259 struct fatcache *fcp;
260
261 fcp = dep->de_fc;
262 for (i = 0; i < FC_SIZE; i++, fcp++) {
263 if (fcp->fc_frcn != FCE_EMPTY && fcp->fc_frcn >= frcn)
264 fcp->fc_frcn = FCE_EMPTY;
265 }
266}
267
268/*
269 * Once the first fat is updated the other copies of
270 * the fat must also be updated. This function does
271 * this.
272 * pmp - pcfsmount structure for filesystem to update
273 * bp - addr of modified fat block
274 * fatbn - block number relative to begin of filesystem
275 * of the modified fat block.
276 */
277void
278updateotherfats(pmp, bp, fatbn)
279 struct pcfsmount *pmp;
280 struct buf *bp;
281 u_long fatbn;
282{
283 int i;
284 struct buf *bpn;
285
286#if defined(PCFSDEBUG)
287printf("updateotherfats(pmp %08x, bp %08x, fatbn %d)\n",
288 pmp, bp, fatbn);
289#endif /* defined(PCFSDEBUG) */
290
291/*
292 * Now copy the block(s) of the modified fat to the other
293 * copies of the fat and write them out. This is faster
294 * than reading in the other fats and then writing them
295 * back out. This could tie up the fat for quite a while.
296 * Preventing others from accessing it. To prevent us
297 * from going after the fat quite so much we use delayed
298 * writes, unless they specfied "synchronous" when the
299 * filesystem was mounted. If synch is asked for then
300 * use bwrite()'s and really slow things down.
301 */
302 for (i = 1; i < pmp->pm_FATs; i++) {
303 fatbn += pmp->pm_FATsecs;
304 /* getblk() never fails */
305 bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount);
306 bcopy(bp->b_un.b_addr, bpn->b_un.b_addr,
307 bp->b_bcount);
308 if (pmp->pm_waitonfat)
309 bwrite(bpn);
310 else
311 bdwrite(bpn);
312 }
313}
314
315/*
316 * Updating entries in 12 bit fats is a pain in the butt.
317 *
318 * The following picture shows where nibbles go when
319 * moving from a 12 bit cluster number into the appropriate
320 * bytes in the FAT.
321 *
322 * byte m byte m+1 byte m+2
323 * +----+----+ +----+----+ +----+----+
324 * | 0 1 | | 2 3 | | 4 5 | FAT bytes
325 * +----+----+ +----+----+ +----+----+
326 *
327 * +----+----+----+ +----+----+----+
328 * | 3 0 1 | | 4 5 2 |
329 * +----+----+----+ +----+----+----+
330 * cluster n cluster n+1
331 *
332 * Where n is even.
333 * m = n + (n >> 2)
334 *
335 * (Function no longer used)
336 */
337
338
339extern inline void
340usemap_alloc (struct pcfsmount *pmp, u_long cn)
341{
342 pmp->pm_inusemap[cn / 8] |= 1 << (cn % 8);
343 pmp->pm_freeclustercount--;
344 /* This assumes that the lowest available cluster was allocated */
345 pmp->pm_lookhere = cn + 1;
346}
347
348extern inline void
349usemap_free (struct pcfsmount *pmp, u_long cn)
350{
351 pmp->pm_freeclustercount++;
352 pmp->pm_inusemap[cn / 8] &= ~(1 << (cn % 8));
353 if (pmp->pm_lookhere > cn)
354 pmp->pm_lookhere = cn;
355}
356
357int
358clusterfree(pmp, cluster, oldcnp)
359 struct pcfsmount *pmp;
360 u_long cluster;
361 u_long *oldcnp;
362{
363 int error;
364 u_long oldcn;
365
366 error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, PCFSFREE);
367 if (error == 0) {
368/*
369 * If the cluster was successfully marked free, then update the count of
370 * free clusters, and turn off the "allocated" bit in the
371 * "in use" cluster bit map.
372 */
373 usemap_free(pmp, cluster);
374 if (oldcnp)
375 *oldcnp = oldcn;
376 }
377 return error;
378}
379
380/*
381 * Get or Set or 'Get and Set' the cluster'th entry in the
382 * fat.
383 * function - whether to get or set a fat entry
384 * pmp - address of the pcfsmount structure for the
385 * filesystem whose fat is to be manipulated.
386 * cluster - which cluster is of interest
387 * oldcontents - address of a word that is to receive
388 * the contents of the cluster'th entry if this is
389 * a get function
390 * newcontents - the new value to be written into the
391 * cluster'th element of the fat if this is a set
392 * function.
393 *
394 * This function can also be used to free a cluster
395 * by setting the fat entry for a cluster to 0.
396 *
397 * All copies of the fat are updated if this is a set
398 * function.
399 * NOTE:
400 * If fatentry() marks a cluster as free it does not
401 * update the inusemap in the pcfsmount structure.
402 * This is left to the caller.
403 */
404int
405fatentry(function, pmp, cn, oldcontents, newcontents)
406 int function;
407 struct pcfsmount *pmp;
408 u_long cn;
409 u_long *oldcontents;
410 u_long newcontents;
411{
412 int error;
413 u_long readcn;
414 u_long bn, bo, bsize, byteoffset;
415 struct buf *bp;
416/*printf("fatentry(func %d, pmp %08x, clust %d, oldcon %08x, newcon %d)\n",
417 function, pmp, cluster, oldcontents, newcontents);*/
418
419#ifdef DIAGNOSTIC
420/*
421 * Be sure they asked us to do something.
422 */
423 if ((function & (FAT_SET | FAT_GET)) == 0) {
424 printf("fatentry(): function code doesn't specify get or set\n");
425 return EINVAL;
426 }
427
428/*
429 * If they asked us to return a cluster number
430 * but didn't tell us where to put it, give them
431 * an error.
432 */
433 if ((function & FAT_GET) && oldcontents == NULL) {
434 printf("fatentry(): get function with no place to put result\n");
435 return EINVAL;
436 }
437#endif
438
439/*
440 * Be sure the requested cluster is in the filesystem.
441 */
442 if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster)
443 return EINVAL;
444
445 byteoffset = FATOFS(pmp, cn);
446 fatblock(pmp, byteoffset, &bn, &bsize, &bo);
447 error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
448 if (function & FAT_GET) {
449 readcn = getushort(&bp->b_un.b_addr[bo]);
450 if (FAT12(pmp)) {
451 if (cn & 1)
452 readcn >>= 4;
453 readcn &= 0x0fff;
454 /* map certain 12 bit fat entries to 16 bit */
455 if ((readcn & 0x0ff0) == 0x0ff0)
456 readcn |= 0xf000;
457 }
458 *oldcontents = readcn;
459 }
460 if (function & FAT_SET) {
461 if (FAT12(pmp)) {
462 readcn = getushort(&bp->b_un.b_addr[bo]);
463 if (cn & 1) {
464 readcn &= 0x000f;
465 readcn |= (newcontents << 4);
466 }
467 else {
468 readcn &= 0xf000;
469 readcn |= (newcontents << 0);
470 }
471 putushort(&bp->b_un.b_addr[bo], readcn);
472 }
473 else
474 putushort(&bp->b_un.b_addr[bo], newcontents);
475 updateotherfats(pmp, bp, bn);
476/*
477 * Write out the first fat last.
478 */
479 if (pmp->pm_waitonfat)
480 bwrite(bp);
481 else
482 bdwrite(bp);
483 bp = NULL;
484 pmp->pm_fmod++;
485 }
486 if (bp)
487 brelse(bp);
488 return 0;
489}
490
491/*
492 * Allocate a free cluster.
493 * pmp -
494 * retcluster - put the allocated cluster's number here.
495 * fillwith - put this value into the fat entry for the
496 * allocated cluster.
497 */
498int
499clusteralloc(pmp, retcluster, fillwith)
500 struct pcfsmount *pmp;
501 u_long *retcluster;
502 u_long fillwith;
503{
504 int error;
505 u_long cn;
506 u_long idx, max_idx, bit, map;
507
508 max_idx = pmp->pm_maxcluster / 8;
509 for (idx = pmp->pm_lookhere / 8; idx <= max_idx; idx++) {
510 map = pmp->pm_inusemap[idx];
511 if (map != 0xff) {
512 for (bit = 0; bit < 8; bit++) {
513 if ((map & (1 << bit)) == 0) {
514 cn = idx * 8 + bit;
515 goto found_one;
516 }
517 }
518 }
519 }
520 return ENOSPC;
521
522found_one:;
523 error = fatentry(FAT_SET, pmp, cn, 0, fillwith);
524 if (error == 0) {
525 usemap_alloc(pmp, cn);
526 *retcluster = cn;
527 }
528#if defined(PCFSDEBUG)
529printf("clusteralloc(): allocated cluster %d\n", cn);
530#endif /* defined(PCFSDEBUG) */
531 return error;
532}
533
534/*
535 * Free a chain of clusters.
536 * pmp - address of the pcfs mount structure for the
537 * filesystem containing the cluster chain to be freed.
538 * startcluster - number of the 1st cluster in the chain
539 * of clusters to be freed.
540 */
541int
542freeclusterchain(pmp, startcluster)
543 struct pcfsmount *pmp;
544 u_long startcluster;
545{
546 u_long nextcluster;
547 int error = 0;
548
549 while (startcluster >= CLUST_FIRST && startcluster <= pmp->pm_maxcluster) {
550 error = clusterfree(pmp, startcluster, &nextcluster);
551 if (error) {
552 printf("freeclusterchain(): free failed, cluster %d\n",
553 startcluster);
554 break;
555 }
556 startcluster = nextcluster;
557 }
558 return error;
559}
560
561/*
562 * Read in fat blocks looking for free clusters.
563 * For every free cluster found turn off its
564 * corresponding bit in the pm_inusemap.
565 */
566int
567fillinusemap(pmp)
568 struct pcfsmount *pmp;
569{
570 struct buf *bp = NULL;
571 u_long cn, readcn;
572 int error;
573 int fat12 = FAT12(pmp);
574 u_long bn, bo, bsize, byteoffset;
575
576/*
577 * Mark all clusters in use, we mark the free ones in the
578 * fat scan loop further down.
579 */
580 for (cn = 0; cn < (pmp->pm_maxcluster >> 3) + 1; cn++)
581 pmp->pm_inusemap[cn] = 0xff;
582
583/*
584 * Figure how many free clusters are in the filesystem
585 * by ripping thougth the fat counting the number of
586 * entries whose content is zero. These represent free
587 * clusters.
588 */
589 pmp->pm_freeclustercount = 0;
590 pmp->pm_lookhere = pmp->pm_maxcluster + 1;
591 for (cn = CLUST_FIRST; cn <= pmp->pm_maxcluster; cn++) {
592 byteoffset = FATOFS(pmp, cn);
593 bo = byteoffset % pmp->pm_fatblocksize;
594 if (!bo || !bp) {
595 /* Read new FAT block */
596 if (bp)
597 brelse(bp);
598 fatblock(pmp, byteoffset, &bn, &bsize, NULL);
599 error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
600 if (error)
601 return error;
602 }
603 readcn = getushort(&bp->b_un.b_addr[bo]);
604 if (fat12) {
605 if (cn & 1)
606 readcn >>= 4;
607 readcn &= 0x0fff;
608 }
609
610 if (readcn == 0)
611 usemap_free(pmp, cn);
612 }
613 brelse(bp);
614 return 0;
615}
616
617/*
618 * Allocate a new cluster and chain it onto the end of the
619 * file.
620 * dep - the file to extend
621 * bpp - where to return the address of the buf header for the
622 * new file block
623 * ncp - where to put cluster number of the newly allocated file block
624 * If this pointer is 0, do not return the cluster number.
625 *
626 * NOTE:
627 * This function is not responsible for turning on the DEUPD
628 * bit if the de_flag field of the denode and it does not
629 * change the de_FileSize field. This is left for the caller
630 * to do.
631 */
632int
633extendfile(dep, bpp, ncp)
634 struct denode *dep;
635 struct buf **bpp;
636 u_int *ncp;
637{
638 int error = 0;
639 u_long frcn;
640 u_long cn;
641 struct pcfsmount *pmp = dep->de_pmp;
642
643/*
644 * Don't try to extend the root directory
645 */
646 if (DETOV(dep)->v_flag & VROOT) {
647 printf("extendfile(): attempt to extend root directory\n");
648 return ENOSPC;
649 }
650
651/*
652 * If the "file's last cluster" cache entry is empty,
653 * and the file is not empty,
654 * then fill the cache entry by calling pcbmap().
655 */
656 fc_fileextends++;
657 if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY &&
658 dep->de_StartCluster != 0) {
659 fc_lfcempty++;
660 error = pcbmap(dep, 0xffff, 0, &cn);
661 /* we expect it to return E2BIG */
662 if (error != E2BIG)
663 return error;
664 error = 0;
665 }
666
667/*
668 * Allocate another cluster and chain onto the end of the file.
669 * If the file is empty we make de_StartCluster point to the
670 * new block. Note that de_StartCluster being 0 is sufficient
671 * to be sure the file is empty since we exclude attempts to
672 * extend the root directory above, and the root dir is the
673 * only file with a startcluster of 0 that has blocks allocated
674 * (sort of).
675 */
676 if (error = clusteralloc(pmp, &cn, CLUST_EOFE))
677 return error;
678 if (dep->de_StartCluster == 0) {
679 dep->de_StartCluster = cn;
680 frcn = 0;
681 } else {
682 error = fatentry(FAT_SET, pmp, dep->de_fc[FC_LASTFC].fc_fsrcn,
683 0, cn);
684 if (error) {
685 clusterfree(pmp, cn, NULL);
686 return error;
687 }
688
689 frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1;
690 }
691
692/*
693 * Update the "last cluster of the file" entry in the denode's
694 * fat cache.
695 */
696 fc_setcache(dep, FC_LASTFC, frcn, cn);
697
698/*
699 * Get the buf header for the new block of the file.
700 */
701 if (dep->de_Attributes & ATTR_DIRECTORY) {
702 *bpp = getblk(pmp->pm_devvp, cntobn(pmp, cn),
703 pmp->pm_bpcluster);
704 } else {
705 *bpp = getblk(DETOV(dep), frcn,
706 pmp->pm_bpcluster);
707 }
708 clrbuf(*bpp);
709
710/*
711 * Give them the filesystem relative cluster number
712 * if they want it.
713 */
714 if (ncp)
715 *ncp = cn;
716 return 0;
717}