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

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

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

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