Commit | Line | Data |
---|---|---|
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 | */ | |
45 | int fc_fileextends; /* # of file extends */ | |
46 | int fc_lfcempty; /* # of time last file cluster cache entry | |
47 | * was empty */ | |
48 | int fc_bmapcalls; /* # of times pcbmap was called */ | |
49 | #define LMMAX 20 | |
50 | int fc_lmdistance[LMMAX]; /* counters for how far off the last cluster | |
51 | * mapped entry was. */ | |
52 | int 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 | ||
58 | static 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 | */ | |
99 | int | |
100 | pcbmap(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 | ||
213 | hiteof:; | |
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 | */ | |
227 | fc_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 | */ | |
254 | fc_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 | */ | |
277 | void | |
278 | updateotherfats(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) | |
287 | printf("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 | ||
339 | extern inline void | |
340 | usemap_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 | ||
348 | extern inline void | |
349 | usemap_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 | ||
357 | int | |
358 | clusterfree(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 | */ | |
404 | int | |
405 | fatentry(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 | */ | |
498 | int | |
499 | clusteralloc(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 | ||
522 | found_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) | |
529 | printf("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 | */ | |
541 | int | |
542 | freeclusterchain(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 | */ | |
566 | int | |
567 | fillinusemap(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 | */ | |
632 | int | |
633 | extendfile(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 | } |