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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ed19497 was 0182e5cc, checked in by Oleg Romanenko <romanenko.oleg@…>, 14 years ago
  1. Rewrite and clean fat_get_cluster_fat12
  2. Support MIPS and other arch with only alligned memory access.
  3. Fix incompatibilities with BigEndian.
  4. Tested on IA32 and on MIPS32 EndianBig (GXEmul):

FAT12,16 reading and writing. FAT32 only reading. FAT32 writing - not tested!

  1. Some fixes for FAT32: reading hi and lo part of node cluster number from fat_dentry_t,

but its need more fixes to change size of cluster number from uint16_t to uint32_t etc.

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