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

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

Merge mainline changes.

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