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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 25.5 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 <mem.h>
51#include <stdlib.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 an error code.
74 */
75errno_t
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 errno_t 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 an error code.
124 */
125errno_t
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 = 0;
131 aoff64_t relbn = bn;
132 errno_t 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 an error code.
189 */
190errno_t
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 = 0;
197 errno_t 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 an error code.
238 */
239errno_t
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 errno_t 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 an error code.
290 */
291static errno_t
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 errno_t 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 an error code.
360 */
361static errno_t
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 errno_t 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 an error code.
391 */
392static errno_t
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 errno_t 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 an error code.
424 */
425errno_t
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 errno_t 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 an error code.
452 */
453static errno_t
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 errno_t 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 an error code.
540 */
541static errno_t
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 errno_t 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 an error code.
573 */
574static errno_t
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 errno_t 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 an error code.
610 */
611errno_t
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 errno_t 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 an error code.
637 */
638errno_t 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 errno_t 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, an error code otherwise.
674 */
675errno_t
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;
682 fat_cluster_t value = 0;
683 fat_cluster_t clst_last1 = FAT_CLST_LAST1(bs);
684 errno_t rc = EOK;
685
686 lifo = (fat_cluster_t *) malloc(nclsts * sizeof(fat_cluster_t));
687 if (!lifo)
688 return ENOMEM;
689
690 /*
691 * Search FAT1 for unused clusters.
692 */
693 fibril_mutex_lock(&fat_alloc_lock);
694 for (clst = FAT_CLST_FIRST; clst < CC(bs) + 2 && found < nclsts;
695 clst++) {
696 rc = fat_get_cluster(bs, service_id, FAT1, clst, &value);
697 if (rc != EOK)
698 break;
699
700 if (value == FAT_CLST_RES0) {
701 /*
702 * The cluster is free. Put it into our stack
703 * of found clusters and mark it as non-free.
704 */
705 lifo[found] = clst;
706 rc = fat_set_cluster(bs, service_id, FAT1, clst,
707 (found == 0) ? clst_last1 : lifo[found - 1]);
708 if (rc != EOK)
709 break;
710
711 found++;
712 }
713 }
714
715 if (rc == EOK && found == nclsts) {
716 rc = fat_alloc_shadow_clusters(bs, service_id, lifo, nclsts);
717 if (rc == EOK) {
718 *mcl = lifo[found - 1];
719 *lcl = lifo[0];
720 free(lifo);
721 fibril_mutex_unlock(&fat_alloc_lock);
722 return EOK;
723 }
724 }
725
726 /* If something wrong - free the clusters */
727 while (found--) {
728 (void) fat_set_cluster(bs, service_id, FAT1, lifo[found],
729 FAT_CLST_RES0);
730 }
731
732 free(lifo);
733 fibril_mutex_unlock(&fat_alloc_lock);
734
735 return ENOSPC;
736}
737
738/** Free clusters forming a cluster chain in all copies of FAT.
739 *
740 * @param bs Buffer hodling the boot sector of the file system.
741 * @param service_id Device service ID of the file system.
742 * @param firstc First cluster in the chain which is to be freed.
743 *
744 * @return EOK on success or an error code.
745 */
746errno_t
747fat_free_clusters(fat_bs_t *bs, service_id_t service_id, fat_cluster_t firstc)
748{
749 unsigned fatno;
750 fat_cluster_t nextc = 0;
751 fat_cluster_t clst_bad = FAT_CLST_BAD(bs);
752 errno_t rc;
753
754 /* Mark all clusters in the chain as free in all copies of FAT. */
755 while (firstc < FAT_CLST_LAST1(bs)) {
756 assert(firstc >= FAT_CLST_FIRST && firstc < clst_bad);
757
758 rc = fat_get_cluster(bs, service_id, FAT1, firstc, &nextc);
759 if (rc != EOK)
760 return rc;
761
762 for (fatno = FAT1; fatno < FATCNT(bs); fatno++) {
763 rc = fat_set_cluster(bs, service_id, 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 an error code.
783 */
784errno_t fat_append_clusters(fat_bs_t *bs, fat_node_t *nodep, fat_cluster_t mcl,
785 fat_cluster_t lcl)
786{
787 service_id_t service_id = nodep->idx->service_id;
788 fat_cluster_t lastc = 0;
789 uint8_t fatno;
790 errno_t 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, service_id, nodep->firstc,
802 &lastc, NULL, (uint32_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->service_id,
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 an error code.
830 */
831errno_t 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 errno_t rc;
835 service_id_t service_id = nodep->idx->service_id;
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, service_id, 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, service_id, 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, service_id, 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, service_id, 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
882errno_t
883fat_zero_cluster(struct fat_bs *bs, service_id_t service_id, fat_cluster_t c)
884{
885 int i;
886 block_t *b;
887 errno_t rc;
888
889 for (i = 0; i < SPC(bs); i++) {
890 rc = _fat_block_get(&b, bs, service_id, 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 */
910errno_t fat_sanity_check(fat_bs_t *bs, service_id_t service_id)
911{
912 fat_cluster_t e0 = 0;
913 fat_cluster_t e1 = 0;
914 unsigned fat_no;
915 errno_t 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) &&
945 (RDE(bs) * sizeof(fat_dentry_t)) % BPS(bs) != 0)
946 return ENOTSUP;
947
948 /* Check signature of each FAT. */
949 for (fat_no = 0; fat_no < FATCNT(bs); fat_no++) {
950 rc = fat_get_cluster(bs, service_id, fat_no, 0, &e0);
951 if (rc != EOK)
952 return EIO;
953
954 rc = fat_get_cluster(bs, service_id, fat_no, 1, &e1);
955 if (rc != EOK)
956 return EIO;
957
958 /*
959 * Check that first byte of FAT contains the media descriptor.
960 */
961 if ((e0 & 0xff) != bs->mdesc)
962 return ENOTSUP;
963
964 /*
965 * Check that remaining bits of the first two entries are
966 * set to one.
967 */
968 if (!FAT_IS_FAT12(bs) &&
969 ((e0 >> 8) != (FAT_MASK(bs) >> 8) || e1 != FAT_MASK(bs)))
970 return ENOTSUP;
971 }
972
973 return EOK;
974}
975
976/**
977 * @}
978 */
Note: See TracBrowser for help on using the repository browser.