File: [local] / prex / usr / server / fs / fatfs / fatfs_fat.c (download)
Revision 1.1.1.1 (vendor branch), Tue Aug 19 12:47:01 2008 UTC (15 years, 10 months ago) by nbrk
Branch: MAIN, KOHSUKE
CVS Tags: PREX_0_8_BASE, HEAD Changes since 1.1: +0 -0 lines
Initial import of Prex, Portable Real-time Embedded POSIX microkernel system.
I have totally new directions in my development (more focused on real hardware, not virtual one).
Old hacks are available in prex-old module. They will be carefully re-designed and merged soon.
|
/*
* Copyright (c) 2005-2008, Kohsuke Ohtani
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <prex/prex.h>
#include <sys/buf.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "fatfs.h"
/*
* Read the FAT entry for specified cluster.
*/
static int
read_fat_entry(struct fatfsmount *fmp, u_long cl)
{
u_long sec;
char *buf = fmp->fat_buf;
int err, border = 0;
struct buf *bp;
/* Get the sector number in FAT entry. */
if (FAT16(fmp))
sec = (cl * 2) / SEC_SIZE;
else {
sec = (cl * 3 / 2) / SEC_SIZE;
/*
* Check if the entry data is placed at the
* end of sector. If so, we have to read one
* more sector to get complete FAT12 entry.
*/
if ((cl * 3 / 2) % SEC_SIZE == SEC_SIZE - 1)
border = 1;
}
sec += fmp->fat_start;
/* Read first sector. */
if ((err = bread(fmp->dev, sec, &bp)) != 0)
return err;
memcpy(buf, bp->b_data, SEC_SIZE);
brelse(bp);
if (!FAT12(fmp) || border == 0)
return 0;
/* Read second sector for the border entry of FAT12. */
if ((err = bread(fmp->dev, sec + 1, &bp)) != 0)
return err;
memcpy(buf + SEC_SIZE, bp->b_data, SEC_SIZE);
brelse(bp);
return 0;
}
/*
* Write fat entry from buffer.
*/
static int
write_fat_entry(struct fatfsmount *fmp, u_long cl)
{
u_long sec;
char *buf = fmp->fat_buf;
int err, border = 0;
struct buf *bp;
/* Get the sector number in FAT entry. */
if (FAT16(fmp))
sec = (cl * 2) / SEC_SIZE;
else {
sec = (cl * 3 / 2) / SEC_SIZE;
/* Check if border entry for FAT12 */
if ((cl * 3 / 2) % SEC_SIZE == SEC_SIZE - 1)
border = 1;
}
sec += fmp->fat_start;
/* Write first sector. */
bp = getblk(fmp->dev, sec);
memcpy(bp->b_data, buf, SEC_SIZE);
if ((err = bwrite(bp)) != 0)
return err;
if (!FAT12(fmp) || border == 0)
return 0;
/* Write second sector for the border entry of FAT12. */
bp = getblk(fmp->dev, sec + 1);
memcpy(bp->b_data, buf + SEC_SIZE, SEC_SIZE);
err = bwrite(bp);
return err;
}
/*
* Get next cluster number of FAT chain.
* @fmp: fat mount data
* @cl: previous cluster#
* @next: next cluster# to return
*/
int
fat_next_cluster(struct fatfsmount *fmp, u_long cl, u_long *next)
{
u_int offset;
uint16_t val;
int err;
/* Read FAT entry */
err = read_fat_entry(fmp, cl);
if (err)
return err;
/* Get offset in buffer. */
if (FAT16(fmp))
offset = (cl * 2) % SEC_SIZE;
else
offset = (cl * 3 / 2) % SEC_SIZE;
/* Pick up cluster# */
val = *((uint16_t *)(fmp->fat_buf + offset));
/* Adjust data for FAT12 entry */
if (FAT12(fmp)) {
if (cl & 1)
val >>= 4;
else
val &= 0xfff;
}
*next = (u_long)val;
DPRINTF(("fat_next_cluster: %d => %d\n", cl, *next));
return 0;
}
/*
* Set next cluster number in FAT chain.
* @fmp: fat mount data
* @cl: previous cluster#
* @next: cluster# to set (can be eof)
*/
int
fat_set_cluster(struct fatfsmount *fmp, u_long cl, u_long next)
{
u_int offset;
char *buf = fmp->fat_buf;
int err;
uint16_t val, tmp;
/* Read FAT entry */
err = read_fat_entry(fmp, cl);
if (err)
return err;
/* Get offset in buffer. */
if (FAT16(fmp))
offset = (cl * 2) % SEC_SIZE;
else
offset = (cl * 3 / 2) % SEC_SIZE;
/* Modify FAT entry for target cluster. */
val = (uint16_t)(next & fmp->fat_mask);
if (FAT12(fmp)) {
tmp = *((uint16_t *)(buf + offset));
if (cl & 1) {
val <<= 4;
val |= (tmp & 0xf);
} else {
tmp &= 0xf000;
val |= tmp;
}
}
*((uint16_t *)(buf + offset)) = val;
/* Write FAT entry */
err = write_fat_entry(fmp, cl);
return err;
}
/*
* Allocate free cluster in FAT chain.
*
* @fmp: fat mount data
* @scan_start: cluster# to scan first. If 0, use the previous used value.
* @free: allocated cluster# to return
*/
int
fat_alloc_cluster(struct fatfsmount *fmp, u_long scan_start, u_long *free)
{
u_long cl, next;
int err;
if (scan_start == 0)
scan_start = fmp->free_scan;
DPRINTF(("fat_alloc_cluster: start=%d\n", scan_start));
cl = scan_start + 1;
while (cl != scan_start) {
err = fat_next_cluster(fmp, cl, &next);
if (err)
return err;
if (next == CL_FREE) { /* free ? */
DPRINTF(("fat_alloc_cluster: free cluster=%d\n", cl));
*free = cl;
return 0;
}
if (++cl >= fmp->last_cluster)
cl = CL_FIRST;
}
return ENOSPC; /* no space */
}
/*
* Deallocate needless cluster.
* @fmp: fat mount data
* @start: first cluster# of FAT chain
*/
int
fat_free_clusters(struct fatfsmount *fmp, u_long start)
{
int err;
u_long cl, next;
cl = start;
if (cl < CL_FIRST)
return EINVAL;
while (!IS_EOFCL(fmp, cl)) {
err = fat_next_cluster(fmp, cl, &next);
if (err)
return err;
err = fat_set_cluster(fmp, cl, CL_FREE);
if (err)
return err;
cl = next;
}
/* Clear eof */
err = fat_set_cluster(fmp, cl, CL_FREE);
if (err)
return err;
return 0;
}
/*
* Get the cluster# for the specific file offset.
*
* @fmp: fat mount data
* @start: start cluster# of file.
* @offset: file offset
* @cl: cluster# to return
*/
int
fat_seek_cluster(struct fatfsmount *fmp, u_long start, u_long offset,
u_long *cl)
{
int err, i;
u_long c, target;
if (start > fmp->last_cluster)
return EIO;
c = start;
target = offset / fmp->cluster_size;
for (i = 0; i < target; i++) {
err = fat_next_cluster(fmp, c, &c);
if (err)
return err;
if (IS_EOFCL(fmp, c))
return EIO;
}
*cl = c;
return 0;
}
/*
* Expand file size.
*
* @fmp: fat mount data
* @cl: cluster# of target file.
* @size: new size of file in bytes.
*/
int
fat_expand_file(struct fatfsmount *fmp, u_long cl, int size)
{
int i, cl_len, alloc, err;
u_long next;
alloc = 0;
cl_len = size / fmp->cluster_size + 1;
for (i = 0; i < cl_len; i++) {
err = fat_next_cluster(fmp, cl, &next);
if (err)
return err;
if (alloc || next >= fmp->fat_eof) {
err = fat_alloc_cluster(fmp, cl, &next);
if (err)
return err;
alloc = 1;
}
if (alloc) {
err = fat_set_cluster(fmp, cl, next);
if (err)
return err;
}
cl = next;
}
if (alloc)
fat_set_cluster(fmp, cl, fmp->fat_eof); /* add eof */
DPRINTF(("fat_expand_file: new size=%d\n", size));
return 0;
}
/*
* Expand directory size.
*
* @fmp: fat mount data
* @cl: cluster# of target directory
* @new_cl: cluster# for new directory to return
*
* Note: The root directory can not be expanded.
*/
int
fat_expand_dir(struct fatfsmount *fmp, u_long cl, u_long *new_cl)
{
int err;
u_long next;
/* Find last cluster number of FAT chain. */
while (!IS_EOFCL(fmp, cl)) {
err = fat_next_cluster(fmp, cl, &next);
if (err)
return err;
cl = next;
}
err = fat_alloc_cluster(fmp, cl, &next);
if (err)
return err;
err = fat_set_cluster(fmp, cl, next);
if (err)
return err;
err = fat_set_cluster(fmp, next, fmp->fat_eof);
if (err)
return err;
*new_cl = next;
return 0;
}