source: mainline/uspace/srv/fs/fat/fat_fat.c@ 944aa24

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 944aa24 was d963742, checked in by Oleg Romanenko <romanenko.oleg@…>, 14 years ago

FAT: replace uint16_t with uint32_t in fat_cluster_walk and _fat_block_get
for better FAT32 support

  • Property mode set to 100644
File size: 25.9 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * Copyright (c) 2011 Oleg Romanenko
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup fs
31 * @{
32 */
33
34/**
35 * @file fat_fat.c
36 * @brief Functions that manipulate the File Allocation Tables.
37 */
38
39#include "fat_fat.h"
40#include "fat_dentry.h"
41#include "fat.h"
42#include "../../vfs/vfs.h"
43#include <libfs.h>
44#include <libblock.h>
45#include <errno.h>
46#include <byteorder.h>
47#include <align.h>
48#include <assert.h>
49#include <fibril_synch.h>
50#include <malloc.h>
51#include <mem.h>
52
53/*
54 * Convenience macros for computing some frequently used values from the
55 * primitive boot sector members.
56 */
57#define CLBN2PBN(bs, cl, bn) \
58 (SSA((bs)) + ((cl) - FAT_CLST_FIRST) * SPC((bs)) + (bn) % SPC((bs)))
59
60#define IS_ODD(number) (number & 0x1)
61
62/**
63 * The fat_alloc_lock mutex protects all copies of the File Allocation Table
64 * during allocation of clusters. The lock does not have to be held durring
65 * deallocation of clusters.
66 */
67static FIBRIL_MUTEX_INITIALIZE(fat_alloc_lock);
68
69/** Walk the cluster chain.
70 *
71 * @param bs Buffer holding the boot sector for the file.
72 * @param devmap_handle Device handle of the device with the file.
73 * @param firstc First cluster to start the walk with.
74 * @param lastc If non-NULL, output argument hodling the last cluster
75 * number visited.
76 * @param numc If non-NULL, output argument holding the number of
77 * clusters seen during the walk.
78 * @param max_clusters Maximum number of clusters to visit.
79 *
80 * @return EOK on success or a negative error code.
81 */
82int
83fat_cluster_walk(fat_bs_t *bs, devmap_handle_t devmap_handle, fat_cluster_t firstc,
84 fat_cluster_t *lastc, uint32_t *numc, uint32_t max_clusters)
85{
86 uint32_t clusters = 0;
87 fat_cluster_t clst = firstc, clst_last1 = FAT_CLST_LAST1(bs);
88 fat_cluster_t clst_bad = FAT_CLST_BAD(bs);
89 int rc;
90
91 if (firstc == FAT_CLST_RES0) {
92 /* No space allocated to the file. */
93 if (lastc)
94 *lastc = firstc;
95 if (numc)
96 *numc = 0;
97 return EOK;
98 }
99
100 while (clst < clst_last1 && clusters < max_clusters) {
101 assert(clst >= FAT_CLST_FIRST);
102 if (lastc)
103 *lastc = clst; /* remember the last cluster number */
104
105 /* read FAT1 */
106 rc = fat_get_cluster(bs, devmap_handle, FAT1, clst, &clst);
107 if (rc != EOK)
108 return rc;
109
110 assert(clst != clst_bad);
111 clusters++;
112 }
113
114 if (lastc && clst < clst_last1)
115 *lastc = clst;
116 if (numc)
117 *numc = clusters;
118
119 return EOK;
120}
121
122/** Read block from file located on a FAT file system.
123 *
124 * @param block Pointer to a block pointer for storing result.
125 * @param bs Buffer holding the boot sector of the file system.
126 * @param nodep FAT node.
127 * @param bn Block number.
128 * @param flags Flags passed to libblock.
129 *
130 * @return EOK on success or a negative error code.
131 */
132int
133fat_block_get(block_t **block, struct fat_bs *bs, fat_node_t *nodep,
134 aoff64_t bn, int flags)
135{
136 fat_cluster_t firstc = nodep->firstc;
137 fat_cluster_t currc;
138 aoff64_t relbn = bn;
139 int rc;
140
141 if (!nodep->size)
142 return ELIMIT;
143
144 if (!FAT_IS_FAT32(bs) && nodep->firstc == FAT_CLST_ROOT)
145 goto fall_through;
146
147 if (((((nodep->size - 1) / BPS(bs)) / SPC(bs)) == bn / SPC(bs)) &&
148 nodep->lastc_cached_valid) {
149 /*
150 * This is a request to read a block within the last cluster
151 * when fortunately we have the last cluster number cached.
152 */
153 return block_get(block, nodep->idx->devmap_handle,
154 CLBN2PBN(bs, nodep->lastc_cached_value, bn), flags);
155 }
156
157 if (nodep->currc_cached_valid && bn >= nodep->currc_cached_bn) {
158 /*
159 * We can start with the cluster cached by the previous call to
160 * fat_block_get().
161 */
162 firstc = nodep->currc_cached_value;
163 relbn -= (nodep->currc_cached_bn / SPC(bs)) * SPC(bs);
164 }
165
166fall_through:
167 rc = _fat_block_get(block, bs, nodep->idx->devmap_handle, firstc,
168 &currc, relbn, flags);
169 if (rc != EOK)
170 return rc;
171
172 /*
173 * Update the "current" cluster cache.
174 */
175 nodep->currc_cached_valid = true;
176 nodep->currc_cached_bn = bn;
177 nodep->currc_cached_value = currc;
178
179 return rc;
180}
181
182/** Read block from file located on a FAT file system.
183 *
184 * @param block Pointer to a block pointer for storing result.
185 * @param bs Buffer holding the boot sector of the file system.
186 * @param devmap_handle Device handle of the file system.
187 * @param fcl First cluster used by the file. Can be zero if the file
188 * is empty.
189 * @param clp If not NULL, address where the cluster containing bn
190 * will be stored.
191 * stored
192 * @param bn Block number.
193 * @param flags Flags passed to libblock.
194 *
195 * @return EOK on success or a negative error code.
196 */
197int
198_fat_block_get(block_t **block, fat_bs_t *bs, devmap_handle_t devmap_handle,
199 fat_cluster_t fcl, fat_cluster_t *clp, aoff64_t bn, int flags)
200{
201 uint32_t clusters;
202 uint32_t max_clusters;
203 fat_cluster_t c;
204 int rc;
205
206 /*
207 * This function can only operate on non-zero length files.
208 */
209 if (fcl == FAT_CLST_RES0)
210 return ELIMIT;
211
212 if (!FAT_IS_FAT32(bs) && fcl == FAT_CLST_ROOT) {
213 /* root directory special case */
214 assert(bn < RDS(bs));
215 rc = block_get(block, devmap_handle,
216 RSCNT(bs) + FATCNT(bs) * SF(bs) + bn, flags);
217 return rc;
218 }
219
220 max_clusters = bn / SPC(bs);
221 rc = fat_cluster_walk(bs, devmap_handle, fcl, &c, &clusters, max_clusters);
222 if (rc != EOK)
223 return rc;
224 assert(clusters == max_clusters);
225
226 rc = block_get(block, devmap_handle, CLBN2PBN(bs, c, bn), flags);
227
228 if (clp)
229 *clp = c;
230
231 return rc;
232}
233
234/** Fill the gap between EOF and a new file position.
235 *
236 * @param bs Buffer holding the boot sector for nodep.
237 * @param nodep FAT node with the gap.
238 * @param mcl First cluster in an independent cluster chain that will
239 * be later appended to the end of the node's own cluster
240 * chain. If pos is still in the last allocated cluster,
241 * this argument is ignored.
242 * @param pos Position in the last node block.
243 *
244 * @return EOK on success or a negative error code.
245 */
246int fat_fill_gap(fat_bs_t *bs, fat_node_t *nodep, fat_cluster_t mcl, aoff64_t pos)
247{
248 block_t *b;
249 aoff64_t o, boundary;
250 int rc;
251
252 boundary = ROUND_UP(nodep->size, BPS(bs) * SPC(bs));
253
254 /* zero out already allocated space */
255 for (o = nodep->size; o < pos && o < boundary;
256 o = ALIGN_DOWN(o + BPS(bs), BPS(bs))) {
257 int flags = (o % BPS(bs) == 0) ?
258 BLOCK_FLAGS_NOREAD : BLOCK_FLAGS_NONE;
259 rc = fat_block_get(&b, bs, nodep, o / BPS(bs), flags);
260 if (rc != EOK)
261 return rc;
262 memset(b->data + o % BPS(bs), 0, BPS(bs) - o % BPS(bs));
263 b->dirty = true; /* need to sync node */
264 rc = block_put(b);
265 if (rc != EOK)
266 return rc;
267 }
268
269 if (o >= pos)
270 return EOK;
271
272 /* zero out the initial part of the new cluster chain */
273 for (o = boundary; o < pos; o += BPS(bs)) {
274 rc = _fat_block_get(&b, bs, nodep->idx->devmap_handle, mcl,
275 NULL, (o - boundary) / BPS(bs), BLOCK_FLAGS_NOREAD);
276 if (rc != EOK)
277 return rc;
278 memset(b->data, 0, min(BPS(bs), pos - o));
279 b->dirty = true; /* need to sync node */
280 rc = block_put(b);
281 if (rc != EOK)
282 return rc;
283 }
284
285 return EOK;
286}
287
288/** Get cluster from the first FAT. FAT12 version
289 *
290 * @param bs Buffer holding the boot sector for the file system.
291 * @param devmap_handle Device handle for the file system.
292 * @param clst Cluster which to get.
293 * @param value Output argument holding the value of the cluster.
294 *
295 * @return EOK or a negative error code.
296 */
297int
298fat_get_cluster_fat12(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned fatno,
299 fat_cluster_t clst, fat_cluster_t *value)
300{
301 block_t *b, *b1;
302 uint16_t byte1, byte2;
303 aoff64_t offset;
304 int rc;
305
306 offset = (clst + clst/2);
307 if (offset / BPS(bs) >= SF(bs))
308 return ERANGE;
309
310 rc = block_get(&b, devmap_handle, RSCNT(bs) + SF(bs) * fatno +
311 offset / BPS(bs), BLOCK_FLAGS_NONE);
312 if (rc != EOK)
313 return rc;
314
315 byte1 = ((uint8_t*) b->data)[offset % BPS(bs)];
316 /* This cluster access spans a sector boundary. Check only for FAT12 */
317 if ((offset % BPS(bs)) + 1 == BPS(bs)) {
318 /* Is it last sector of FAT? */
319 if (offset / BPS(bs) < SF(bs)) {
320 /* No. Reading next sector */
321 rc = block_get(&b1, devmap_handle, 1 + RSCNT(bs) +
322 SF(bs)*fatno + offset / BPS(bs), BLOCK_FLAGS_NONE);
323 if (rc != EOK) {
324 block_put(b);
325 return rc;
326 }
327 /*
328 * Combining value with last byte of current sector and
329 * first byte of next sector
330 */
331 byte2 = ((uint8_t*) b1->data)[0];
332
333 rc = block_put(b1);
334 if (rc != EOK) {
335 block_put(b);
336 return rc;
337 }
338 }
339 else {
340 /* Yes. It is last sector of FAT */
341 block_put(b);
342 return ERANGE;
343 }
344 }
345 else
346 byte2 = ((uint8_t*) b->data)[(offset % BPS(bs))+1];
347
348 *value = uint16_t_le2host(byte1 | (byte2 << 8));
349 if (IS_ODD(clst))
350 *value = (*value) >> 4;
351 else
352 *value = (*value) & FAT12_MASK;
353
354 rc = block_put(b);
355 return rc;
356}
357
358/** Get cluster from the first FAT. FAT16 version
359 *
360 * @param bs Buffer holding the boot sector for the file system.
361 * @param devmap_handle Device handle for the file system.
362 * @param clst Cluster which to get.
363 * @param value Output argument holding the value of the cluster.
364 *
365 * @return EOK or a negative error code.
366 */
367int
368fat_get_cluster_fat16(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned fatno,
369 fat_cluster_t clst, fat_cluster_t *value)
370{
371 block_t *b;
372 aoff64_t offset;
373 int rc;
374
375 offset = (clst * FAT16_CLST_SIZE);
376
377 rc = block_get(&b, devmap_handle, RSCNT(bs) + SF(bs) * fatno +
378 offset / BPS(bs), BLOCK_FLAGS_NONE);
379 if (rc != EOK)
380 return rc;
381
382 *value = uint16_t_le2host(*(uint16_t *)(b->data + offset % BPS(bs)));
383
384 rc = block_put(b);
385
386 return rc;
387}
388
389/** Get cluster from the first FAT. FAT32 version
390 *
391 * @param bs Buffer holding the boot sector for the file system.
392 * @param devmap_handle Device handle for the file system.
393 * @param clst Cluster which to get.
394 * @param value Output argument holding the value of the cluster.
395 *
396 * @return EOK or a negative error code.
397 */
398int
399fat_get_cluster_fat32(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned fatno,
400 fat_cluster_t clst, fat_cluster_t *value)
401{
402 block_t *b;
403 aoff64_t offset;
404 int rc;
405
406 offset = (clst * FAT32_CLST_SIZE);
407
408 rc = block_get(&b, devmap_handle, RSCNT(bs) + SF(bs) * fatno +
409 offset / BPS(bs), BLOCK_FLAGS_NONE);
410 if (rc != EOK)
411 return rc;
412
413 *value = uint32_t_le2host(*(uint32_t *)(b->data + offset % BPS(bs))) & FAT32_MASK;
414
415 rc = block_put(b);
416
417 return rc;
418}
419
420
421/** Get cluster from the first FAT.
422 *
423 * @param bs Buffer holding the boot sector for the file system.
424 * @param devmap_handle Device handle for the file system.
425 * @param clst Cluster which to get.
426 * @param value Output argument holding the value of the cluster.
427 *
428 * @return EOK or a negative error code.
429 */
430int
431fat_get_cluster(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned fatno,
432 fat_cluster_t clst, fat_cluster_t *value)
433{
434 int rc;
435
436 assert(fatno < FATCNT(bs));
437
438 if (FAT_IS_FAT12(bs)) {
439 rc = fat_get_cluster_fat12(bs, devmap_handle, fatno, clst, value);
440 }
441 else {
442 if (FAT_IS_FAT32(bs))
443 rc = fat_get_cluster_fat32(bs, devmap_handle, fatno, clst, value);
444 else
445 rc = fat_get_cluster_fat16(bs, devmap_handle, fatno, clst, value);
446 }
447
448 return rc;
449}
450
451/** Set cluster in one instance of FAT. FAT12 version.
452 *
453 * @param bs Buffer holding the boot sector for the file system.
454 * @param devmap_handle Device handle for the file system.
455 * @param fatno Number of the FAT instance where to make the change.
456 * @param clst Cluster which is to be set.
457 * @param value Value to set the cluster with.
458 *
459 * @return EOK on success or a negative error code.
460 */
461int
462fat_set_cluster_fat12(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned fatno,
463 fat_cluster_t clst, fat_cluster_t value)
464{
465 block_t *b, *b1=NULL;
466 aoff64_t offset;
467 uint16_t byte1, byte2;
468 int rc;
469
470 offset = (clst + clst/2);
471 if (offset / BPS(bs) >= SF(bs))
472 return ERANGE;
473
474 rc = block_get(&b, devmap_handle, RSCNT(bs) + SF(bs) * fatno +
475 offset / BPS(bs), BLOCK_FLAGS_NONE);
476 if (rc != EOK)
477 return rc;
478
479 byte1 = ((uint8_t*) b->data)[offset % BPS(bs)];
480 bool border = false;
481 /* This cluster access spans a sector boundary. Check only for FAT12 */
482 if ((offset % BPS(bs))+1 == BPS(bs)) {
483 /* Is it last sector of FAT? */
484 if (offset / BPS(bs) < SF(bs)) {
485 /* No. Reading next sector */
486 rc = block_get(&b1, devmap_handle, 1 + RSCNT(bs) +
487 SF(bs)*fatno + offset / BPS(bs), BLOCK_FLAGS_NONE);
488 if (rc != EOK) {
489 block_put(b);
490 return rc;
491 }
492 /*
493 * Combining value with last byte of current sector and
494 * first byte of next sector
495 */
496 byte2 = ((uint8_t*) b1->data)[0];
497 border = true;
498 }
499 else {
500 /* Yes. It is last sector of fat */
501 block_put(b);
502 return ERANGE;
503 }
504 }
505 else
506 byte2 = ((uint8_t*) b->data)[(offset % BPS(bs))+1];
507
508 if (IS_ODD(clst)) {
509 byte1 &= 0x0f;
510 byte2 = 0;
511 value = (value << 4);
512 } else {
513 byte1 = 0;
514 byte2 &= 0xf0;
515 value &= FAT12_MASK;
516 }
517
518 byte1 = byte1 | (value & 0xff);
519 byte2 = byte2 | (value >> 8);
520
521 ((uint8_t*) b->data)[(offset % BPS(bs))] = byte1;
522 if (border) {
523 ((uint8_t*) b1->data)[0] = byte2;
524
525 b1->dirty = true;
526 rc = block_put(b1);
527 if (rc != EOK) {
528 block_put(b);
529 return rc;
530 }
531 } else
532 ((uint8_t*) b->data)[(offset % BPS(bs))+1] = byte2;
533
534 b->dirty = true; /* need to sync block */
535 rc = block_put(b);
536 return rc;
537}
538
539/** Set cluster in one instance of FAT. FAT16 version.
540 *
541 * @param bs Buffer holding the boot sector for the file system.
542 * @param devmap_handle Device handle for the file system.
543 * @param fatno Number of the FAT instance where to make the change.
544 * @param clst Cluster which is to be set.
545 * @param value Value to set the cluster with.
546 *
547 * @return EOK on success or a negative error code.
548 */
549int
550fat_set_cluster_fat16(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned fatno,
551 fat_cluster_t clst, fat_cluster_t value)
552{
553 block_t *b;
554 aoff64_t offset;
555 int rc;
556
557 offset = (clst * FAT16_CLST_SIZE);
558
559 rc = block_get(&b, devmap_handle, RSCNT(bs) + SF(bs) * fatno +
560 offset / BPS(bs), BLOCK_FLAGS_NONE);
561 if (rc != EOK)
562 return rc;
563
564 *(uint16_t *)(b->data + offset % BPS(bs)) = host2uint16_t_le(value);
565
566 b->dirty = true; /* need to sync block */
567 rc = block_put(b);
568 return rc;
569}
570
571/** Set cluster in one instance of FAT. FAT32 version.
572 *
573 * @param bs Buffer holding the boot sector for the file system.
574 * @param devmap_handle Device handle for the file system.
575 * @param fatno Number of the FAT instance where to make the change.
576 * @param clst Cluster which is to be set.
577 * @param value Value to set the cluster with.
578 *
579 * @return EOK on success or a negative error code.
580 */
581int
582fat_set_cluster_fat32(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned fatno,
583 fat_cluster_t clst, fat_cluster_t value)
584{
585 block_t *b;
586 aoff64_t offset;
587 int rc;
588 fat_cluster_t temp;
589
590 offset = (clst * FAT32_CLST_SIZE);
591
592 rc = block_get(&b, devmap_handle, RSCNT(bs) + SF(bs) * fatno +
593 offset / BPS(bs), BLOCK_FLAGS_NONE);
594 if (rc != EOK)
595 return rc;
596
597 temp = uint32_t_le2host(*(uint32_t *)(b->data + offset % BPS(bs)));
598 temp &= 0xf0000000;
599 temp |= (value & FAT32_MASK);
600 *(uint32_t *)(b->data + offset % BPS(bs)) = host2uint32_t_le(temp);
601
602 b->dirty = true; /* need to sync block */
603 rc = block_put(b);
604 return rc;
605}
606
607/** Set cluster in one instance of FAT.
608 *
609 * @param bs Buffer holding the boot sector for the file system.
610 * @param devmap_handle Device handle for the file system.
611 * @param fatno Number of the FAT instance where to make the change.
612 * @param clst Cluster which is to be set.
613 * @param value Value to set the cluster with.
614 *
615 * @return EOK on success or a negative error code.
616 */
617int
618fat_set_cluster(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned fatno,
619 fat_cluster_t clst, fat_cluster_t value)
620{
621 int rc;
622
623 assert(fatno < FATCNT(bs));
624
625 if (FAT_IS_FAT12(bs))
626 rc = fat_set_cluster_fat12(bs, devmap_handle, fatno, clst, value);
627 else if (FAT_IS_FAT32(bs))
628 rc = fat_set_cluster_fat32(bs, devmap_handle, fatno, clst, value);
629 else
630 rc = fat_set_cluster_fat16(bs, devmap_handle, fatno, clst, value);
631
632 return rc;
633}
634
635/** Replay the allocatoin of clusters in all shadow instances of FAT.
636 *
637 * @param bs Buffer holding the boot sector of the file system.
638 * @param devmap_handle Device handle of the file system.
639 * @param lifo Chain of allocated clusters.
640 * @param nclsts Number of clusters in the lifo chain.
641 *
642 * @return EOK on success or a negative error code.
643 */
644int fat_alloc_shadow_clusters(fat_bs_t *bs, devmap_handle_t devmap_handle,
645 fat_cluster_t *lifo, unsigned nclsts)
646{
647 uint8_t fatno;
648 unsigned c;
649 fat_cluster_t clst_last1 = FAT_CLST_LAST1(bs);
650 int rc;
651
652 for (fatno = FAT1 + 1; fatno < FATCNT(bs); fatno++) {
653 for (c = 0; c < nclsts; c++) {
654 rc = fat_set_cluster(bs, devmap_handle, fatno, lifo[c],
655 c == 0 ? clst_last1 : lifo[c - 1]);
656 if (rc != EOK)
657 return rc;
658 }
659 }
660
661 return EOK;
662}
663
664/** Allocate clusters in all copies of FAT.
665 *
666 * This function will attempt to allocate the requested number of clusters in
667 * all instances of the FAT. The FAT will be altered so that the allocated
668 * clusters form an independent chain (i.e. a chain which does not belong to any
669 * file yet).
670 *
671 * @param bs Buffer holding the boot sector of the file system.
672 * @param devmap_handle Device handle of the file system.
673 * @param nclsts Number of clusters to allocate.
674 * @param mcl Output parameter where the first cluster in the chain
675 * will be returned.
676 * @param lcl Output parameter where the last cluster in the chain
677 * will be returned.
678 *
679 * @return EOK on success, a negative error code otherwise.
680 */
681int
682fat_alloc_clusters(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned nclsts,
683 fat_cluster_t *mcl, fat_cluster_t *lcl)
684{
685 fat_cluster_t *lifo; /* stack for storing free cluster numbers */
686 unsigned found = 0; /* top of the free cluster number stack */
687 fat_cluster_t clst, value, clst_last1 = FAT_CLST_LAST1(bs);
688 int rc = EOK;
689
690 lifo = (fat_cluster_t *) malloc(nclsts * sizeof(fat_cluster_t));
691 if (!lifo)
692 return ENOMEM;
693 /*
694 * Search FAT1 for unused clusters.
695 */
696 fibril_mutex_lock(&fat_alloc_lock);
697 for (clst=FAT_CLST_FIRST; clst < CC(bs)+2 && found < nclsts; clst++) {
698 rc = fat_get_cluster(bs, devmap_handle, FAT1, clst, &value);
699 if (rc != EOK)
700 break;
701
702 if (value == FAT_CLST_RES0) {
703 /*
704 * The cluster is free. Put it into our stack
705 * of found clusters and mark it as non-free.
706 */
707 lifo[found] = clst;
708 rc = fat_set_cluster(bs, devmap_handle, FAT1, clst,
709 (found == 0) ? clst_last1 : lifo[found - 1]);
710 if (rc != EOK)
711 break;
712
713 found++;
714 }
715 }
716
717 if (rc == EOK && found == nclsts) {
718 rc = fat_alloc_shadow_clusters(bs, devmap_handle, lifo, nclsts);
719 if (rc == EOK) {
720 *mcl = lifo[found - 1];
721 *lcl = lifo[0];
722 free(lifo);
723 fibril_mutex_unlock(&fat_alloc_lock);
724 return EOK;
725 }
726 }
727
728 /* If something wrong - free the clusters */
729 if (found > 0) {
730 while (found--) {
731 rc = fat_set_cluster(bs, devmap_handle, FAT1, lifo[found],
732 FAT_CLST_RES0);
733 }
734 }
735
736 free(lifo);
737 fibril_mutex_unlock(&fat_alloc_lock);
738 return ENOSPC;
739}
740
741/** Free clusters forming a cluster chain in all copies of FAT.
742 *
743 * @param bs Buffer hodling the boot sector of the file system.
744 * @param devmap_handle Device handle of the file system.
745 * @param firstc First cluster in the chain which is to be freed.
746 *
747 * @return EOK on success or a negative return code.
748 */
749int
750fat_free_clusters(fat_bs_t *bs, devmap_handle_t devmap_handle, fat_cluster_t firstc)
751{
752 unsigned fatno;
753 fat_cluster_t nextc, clst_bad = FAT_CLST_BAD(bs);
754 int rc;
755
756 /* Mark all clusters in the chain as free in all copies of FAT. */
757 while (firstc < FAT_CLST_LAST1(bs)) {
758 assert(firstc >= FAT_CLST_FIRST && firstc < clst_bad);
759 rc = fat_get_cluster(bs, devmap_handle, FAT1, firstc, &nextc);
760 if (rc != EOK)
761 return rc;
762 for (fatno = FAT1; fatno < FATCNT(bs); fatno++) {
763 rc = fat_set_cluster(bs, devmap_handle, fatno, firstc,
764 FAT_CLST_RES0);
765 if (rc != EOK)
766 return rc;
767 }
768
769 firstc = nextc;
770 }
771
772 return EOK;
773}
774
775/** Append a cluster chain to the last file cluster in all FATs.
776 *
777 * @param bs Buffer holding the boot sector of the file system.
778 * @param nodep Node representing the file.
779 * @param mcl First cluster of the cluster chain to append.
780 * @param lcl Last cluster of the cluster chain to append.
781 *
782 * @return EOK on success or a negative error code.
783 */
784int
785fat_append_clusters(fat_bs_t *bs, fat_node_t *nodep, fat_cluster_t mcl,
786 fat_cluster_t lcl)
787{
788 devmap_handle_t devmap_handle = nodep->idx->devmap_handle;
789 fat_cluster_t lastc;
790 uint8_t fatno;
791 int rc;
792
793 if (nodep->firstc == FAT_CLST_RES0) {
794 /* No clusters allocated to the node yet. */
795 nodep->firstc = mcl;
796 nodep->dirty = true; /* need to sync node */
797 } else {
798 if (nodep->lastc_cached_valid) {
799 lastc = nodep->lastc_cached_value;
800 nodep->lastc_cached_valid = false;
801 } else {
802 rc = fat_cluster_walk(bs, devmap_handle, nodep->firstc,
803 &lastc, NULL, (uint16_t) -1);
804 if (rc != EOK)
805 return rc;
806 }
807
808 for (fatno = FAT1; fatno < FATCNT(bs); fatno++) {
809 rc = fat_set_cluster(bs, nodep->idx->devmap_handle,
810 fatno, lastc, mcl);
811 if (rc != EOK)
812 return rc;
813 }
814 }
815
816 nodep->lastc_cached_valid = true;
817 nodep->lastc_cached_value = lcl;
818
819 return EOK;
820}
821
822/** Chop off node clusters in all copies of FAT.
823 *
824 * @param bs Buffer holding the boot sector of the file system.
825 * @param nodep FAT node where the chopping will take place.
826 * @param lcl Last cluster which will remain in the node. If this
827 * argument is FAT_CLST_RES0, then all clusters will
828 * be chopped off.
829 *
830 * @return EOK on success or a negative return code.
831 */
832int fat_chop_clusters(fat_bs_t *bs, fat_node_t *nodep, fat_cluster_t lcl)
833{
834 fat_cluster_t clst_last1 = FAT_CLST_LAST1(bs);
835 int rc;
836 devmap_handle_t devmap_handle = nodep->idx->devmap_handle;
837
838 /*
839 * Invalidate cached cluster numbers.
840 */
841 nodep->lastc_cached_valid = false;
842 if (nodep->currc_cached_value != lcl)
843 nodep->currc_cached_valid = false;
844
845 if (lcl == FAT_CLST_RES0) {
846 /* The node will have zero size and no clusters allocated. */
847 rc = fat_free_clusters(bs, devmap_handle, nodep->firstc);
848 if (rc != EOK)
849 return rc;
850 nodep->firstc = FAT_CLST_RES0;
851 nodep->dirty = true; /* need to sync node */
852 } else {
853 fat_cluster_t nextc;
854 unsigned fatno;
855
856 rc = fat_get_cluster(bs, devmap_handle, FAT1, lcl, &nextc);
857 if (rc != EOK)
858 return rc;
859
860 /* Terminate the cluster chain in all copies of FAT. */
861 for (fatno = FAT1; fatno < FATCNT(bs); fatno++) {
862 rc = fat_set_cluster(bs, devmap_handle, fatno, lcl,
863 clst_last1);
864 if (rc != EOK)
865 return rc;
866 }
867
868 /* Free all following clusters. */
869 rc = fat_free_clusters(bs, devmap_handle, nextc);
870 if (rc != EOK)
871 return rc;
872 }
873
874 /*
875 * Update and re-enable the last cluster cache.
876 */
877 nodep->lastc_cached_valid = true;
878 nodep->lastc_cached_value = lcl;
879
880 return EOK;
881}
882
883int
884fat_zero_cluster(struct fat_bs *bs, devmap_handle_t devmap_handle, fat_cluster_t c)
885{
886 int i;
887 block_t *b;
888 int rc;
889
890 for (i = 0; i < SPC(bs); i++) {
891 rc = _fat_block_get(&b, bs, devmap_handle, c, NULL, i,
892 BLOCK_FLAGS_NOREAD);
893 if (rc != EOK)
894 return rc;
895 memset(b->data, 0, BPS(bs));
896 b->dirty = true;
897 rc = block_put(b);
898 if (rc != EOK)
899 return rc;
900 }
901
902 return EOK;
903}
904
905/** Perform basic sanity checks on the file system.
906 *
907 * Verify if values of boot sector fields are sane. Also verify media
908 * descriptor. This is used to rule out cases when a device obviously
909 * does not contain a fat file system.
910 */
911int fat_sanity_check(fat_bs_t *bs, devmap_handle_t devmap_handle)
912{
913 fat_cluster_t e0, e1;
914 unsigned fat_no;
915 int rc;
916
917 /* Check number of FATs. */
918 if (FATCNT(bs) == 0)
919 return ENOTSUP;
920
921 /* Check total number of sectors. */
922 if (TS(bs) == 0)
923 return ENOTSUP;
924
925 if (bs->totsec16 != 0 && bs->totsec32 != 0 &&
926 bs->totsec16 != bs->totsec32)
927 return ENOTSUP;
928
929 /* Check media descriptor. Must be between 0xf0 and 0xff. */
930 if ((bs->mdesc & 0xf0) != 0xf0)
931 return ENOTSUP;
932
933 /* Check number of sectors per FAT. */
934 if (SF(bs) == 0)
935 return ENOTSUP;
936
937 /*
938 * Check that the root directory entries take up whole blocks.
939 * This check is rather strict, but it allows us to treat the root
940 * directory and non-root directories uniformly in some places.
941 * It can be removed provided that functions such as fat_read() are
942 * sanitized to support file systems with this property.
943 */
944 if (!FAT_IS_FAT32(bs) && (RDE(bs) * sizeof(fat_dentry_t)) % BPS(bs) != 0)
945 return ENOTSUP;
946
947 /* Check signature of each FAT. */
948 for (fat_no = 0; fat_no < FATCNT(bs); fat_no++) {
949 rc = fat_get_cluster(bs, devmap_handle, fat_no, 0, &e0);
950 if (rc != EOK)
951 return EIO;
952
953 rc = fat_get_cluster(bs, devmap_handle, fat_no, 1, &e1);
954 if (rc != EOK)
955 return EIO;
956
957 /* Check that first byte of FAT contains the media descriptor. */
958 if ((e0 & 0xff) != bs->mdesc)
959 return ENOTSUP;
960
961 /*
962 * Check that remaining bits of the first two entries are
963 * set to one.
964 */
965 if (!FAT_IS_FAT12(bs) &&
966 ((e0 >> 8) != (FAT_MASK(bs) >> 8) || e1 != FAT_MASK(bs)))
967 return ENOTSUP;
968 }
969
970 return EOK;
971}
972
973/**
974 * @}
975 */
Note: See TracBrowser for help on using the repository browser.