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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since dbf2ca4 was f73b291, checked in by Martin Decky <martin@…>, 13 years ago

libblock.{c|h} → block.{c|h}

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