static int fat_node_sync(fat_node_t *node) { block_t *b; fat_bs_t *bs; fat_dentry_t *d; int rc; assert(node->dirty); bs = block_bb_get(node->idx->service_id); /* Read the block that contains the dentry of interest. */ rc = _fat_block_get(&b, bs, node->idx->service_id, node->idx->pfc, NULL, (node->idx->pdi * sizeof(fat_dentry_t)) / BPS(bs), BLOCK_FLAGS_NONE); if (rc != EOK) return rc; d = ((fat_dentry_t *)b->data) + (node->idx->pdi % DPS(bs)); d->firstc = host2uint16_t_le(node->firstc); if (node->type == FAT_FILE) { d->size = host2uint32_t_le(node->size); } else if (node->type == FAT_DIRECTORY) { d->attr = FAT_ATTR_SUBDIR; } /* TODO: update other fields? (e.g time fields) */ b->dirty = true; /* need to sync block */ rc = block_put(b); return rc; }
/** Read block from file located on a FAT file system. * * @param block Pointer to a block pointer for storing result. * @param bs Buffer holding the boot sector of the file system. * @param nodep FAT node. * @param bn Block number. * @param flags Flags passed to libblock. * * @return EOK on success or a negative error code. */ int fat_block_get(block_t **block, struct fat_bs *bs, fat_node_t *nodep, aoff64_t bn, int flags) { fat_cluster_t firstc = nodep->firstc; fat_cluster_t currc; aoff64_t relbn = bn; int rc; if (!nodep->size) return ELIMIT; if (!FAT_IS_FAT32(bs) && nodep->firstc == FAT_CLST_ROOT) goto fall_through; if (((((nodep->size - 1) / BPS(bs)) / SPC(bs)) == bn / SPC(bs)) && nodep->lastc_cached_valid) { /* * This is a request to read a block within the last cluster * when fortunately we have the last cluster number cached. */ return block_get(block, nodep->idx->service_id, CLBN2PBN(bs, nodep->lastc_cached_value, bn), flags); } if (nodep->currc_cached_valid && bn >= nodep->currc_cached_bn) { /* * We can start with the cluster cached by the previous call to * fat_block_get(). */ firstc = nodep->currc_cached_value; relbn -= (nodep->currc_cached_bn / SPC(bs)) * SPC(bs); } fall_through: rc = _fat_block_get(block, bs, nodep->idx->service_id, firstc, &currc, relbn, flags); if (rc != EOK) return rc; /* * Update the "current" cluster cache. */ nodep->currc_cached_valid = true; nodep->currc_cached_bn = bn; nodep->currc_cached_value = currc; return rc; }
/** Fill the gap between EOF and a new file position. * * @param bs Buffer holding the boot sector for nodep. * @param nodep FAT node with the gap. * @param mcl First cluster in an independent cluster chain that will * be later appended to the end of the node's own cluster * chain. If pos is still in the last allocated cluster, * this argument is ignored. * @param pos Position in the last node block. * * @return EOK on success or a negative error code. */ int fat_fill_gap(fat_bs_t *bs, fat_node_t *nodep, fat_cluster_t mcl, aoff64_t pos) { block_t *b; aoff64_t o, boundary; int rc; boundary = ROUND_UP(nodep->size, BPS(bs) * SPC(bs)); /* zero out already allocated space */ for (o = nodep->size; o < pos && o < boundary; o = ALIGN_DOWN(o + BPS(bs), BPS(bs))) { int flags = (o % BPS(bs) == 0) ? BLOCK_FLAGS_NOREAD : BLOCK_FLAGS_NONE; rc = fat_block_get(&b, bs, nodep, o / BPS(bs), flags); if (rc != EOK) return rc; memset(b->data + o % BPS(bs), 0, BPS(bs) - o % BPS(bs)); b->dirty = true; /* need to sync node */ rc = block_put(b); if (rc != EOK) return rc; } if (o >= pos) return EOK; /* zero out the initial part of the new cluster chain */ for (o = boundary; o < pos; o += BPS(bs)) { rc = _fat_block_get(&b, bs, nodep->idx->service_id, mcl, NULL, (o - boundary) / BPS(bs), BLOCK_FLAGS_NOREAD); if (rc != EOK) return rc; memset(b->data, 0, min(BPS(bs), pos - o)); b->dirty = true; /* need to sync node */ rc = block_put(b); if (rc != EOK) return rc; } return EOK; }