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

Last change on this file since 1db4e2ae was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

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