/*
	Copyright 1999-2001, Be Incorporated.   All Rights Reserved.
	This file may be used under the terms of the Be Sample Code License.
*/
 
 
#include "iter.h"
 
#include <string.h>
 
#include <fs_cache.h>
#include <fs_info.h>
#include <KernelExport.h>
 
#include "dosfs.h"
#include "fat.h"
#include "util.h"
 
 
#define DPRINTF(a,b) if (debug_iter > (a)) dprintf b
 
 
static int
_validate_cs_(nspace *vol, uint32 cluster, uint32 sector)
{
	if (sector < 0) return -1;
 
	if ((vol->fat_bits != 32) && IS_FIXED_ROOT(cluster)) { // fat12 or fat16 root
		if (sector >= vol->root_sectors)
			return -1;
		return 0;
	}
 
	if (sector >= vol->sectors_per_cluster) return -1;
 
	if (!IS_DATA_CLUSTER(cluster)) return -1;
 
	return 0;
}
 
 
off_t
csi_to_block(struct csi *csi)
{
	// presumes the caller has already called _validate_cs_ on the argument
	ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
		return EINVAL;
 
	if (IS_FIXED_ROOT(csi->cluster))
		return csi->vol->root_start + csi->sector;
 
	return csi->vol->data_start +
		(off_t)(csi->cluster - 2)* csi->vol->sectors_per_cluster +
		csi->sector;
}
 
 
int
init_csi(nspace *vol, uint32 cluster, uint32 sector, struct csi *csi)
{
	int ret;
	if ((ret = _validate_cs_(vol,cluster,sector)) != 0)
		return ret;
 
	csi->vol = vol; csi->cluster = cluster; csi->sector = sector;
 
	return 0;
}
 
 
int
iter_csi(struct csi *csi, int sectors)
{
	if (csi->sector == 0xffff) // check if already at end of chain
		return -1;
 
	if (sectors < 0)
		return EINVAL;
 
	if (sectors == 0)
		return 0;
 
	if (IS_FIXED_ROOT(csi->cluster)) {
		csi->sector += sectors;
		if (csi->sector < csi->vol->root_sectors)
			return 0;
	} else {
		csi->sector += sectors;
		if (csi->sector < csi->vol->sectors_per_cluster)
			return 0;
		csi->cluster = get_nth_fat_entry(csi->vol, csi->cluster,
			csi->sector / csi->vol->sectors_per_cluster);
 
		if ((int32)csi->cluster < 0) {
			csi->sector = 0xffff;
			return csi->cluster;
		}
 
		if (vIS_DATA_CLUSTER(csi->vol,csi->cluster)) {
			csi->sector %= csi->vol->sectors_per_cluster;
			return 0;
		}
	}
 
	csi->sector = 0xffff;
 
	return -1;
}
 
 
uint8 *
csi_get_block(struct csi *csi)
{
	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
		return NULL;
 
	return (uint8 *)block_cache_get_etc(csi->vol->fBlockCache,
		csi_to_block(csi), 1, csi->vol->bytes_per_sector);
}
 
 
status_t
csi_release_block(struct csi *csi)
{
	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
		return EINVAL;
 
	block_cache_put(csi->vol->fBlockCache, csi_to_block(csi));
	return B_OK;
}
 
 
status_t
csi_make_writable(struct csi *csi)
{
	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
		return EINVAL;
 
	return block_cache_make_writable(csi->vol->fBlockCache, csi_to_block(csi),
		-1);
}
 
 
/* XXX: not the most efficient implementation, but it gets the job done */
status_t
csi_read_blocks(struct csi *csi, uint8 *buffer, ssize_t len)
{
	struct csi old_csi;
	uint32 sectors;
	off_t block;
	status_t err;
	uint8 *buf = buffer;
	int32 i;
 
	ASSERT(len >= csi->vol->bytes_per_sector);
 
	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
		return EINVAL;
 
	sectors = 1;
	block = csi_to_block(csi);
 
	while (1) {
		old_csi = *csi;
		err = iter_csi(csi, 1);
		if (len < (sectors + 1) * csi->vol->bytes_per_sector)
			break;
		if ((err < B_OK) || (block + sectors != csi_to_block(csi)))
			break;
		sectors++;
	}
 
	for (i = block; i < block + sectors; i++) {
		char *blockData = (char *)block_cache_get(csi->vol->fBlockCache, i);
		memcpy(buf, blockData, csi->vol->bytes_per_sector);
		buf += csi->vol->bytes_per_sector;
		block_cache_put(csi->vol->fBlockCache, i);
	}
 
	*csi = old_csi;
 
	return sectors * csi->vol->bytes_per_sector;
}
 
 
status_t
csi_write_blocks(struct csi *csi, uint8 *buffer, ssize_t len)
{
	struct csi old_csi;
	uint32 sectors;
	off_t block;
	status_t err;
	uint8 *buf = buffer;
	int32 i;
 
	ASSERT(len >= csi->vol->bytes_per_sector);
 
	ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
		return EINVAL;
 
	sectors = 1;
	block = csi_to_block(csi);
 
	while (1) {
		old_csi = *csi;
		err = iter_csi(csi, 1);
		if (len < (sectors + 1) * csi->vol->bytes_per_sector)
			break;
		if ((err < B_OK) || (block + sectors != csi_to_block(csi)))
			break;
		sectors++;
	}
 
	for (i = block; i < block + sectors; i++) {
		char *blockData = block_cache_get_writable_etc(csi->vol->fBlockCache, i,
			0, 1, -1);
		memcpy(blockData, buf, csi->vol->bytes_per_sector);
		buf += csi->vol->bytes_per_sector;
		block_cache_put(csi->vol->fBlockCache, i);
	}
 
	/* return the last state of the iterator because that's what dosfs_write
	 * expects. this lets it meaningfully cache the state even when it's
	 * writing to the end of the file. */
	*csi = old_csi;
 
	return sectors * csi->vol->bytes_per_sector;
}
 
 
status_t
csi_write_block(struct csi *csi, uint8 *buffer)
{
	off_t block;
	char *blockData;
 
	block = csi_to_block(csi);
 
	ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
		return EINVAL;
 
	blockData = block_cache_get_writable_etc(csi->vol->fBlockCache, block, 0, 1,
		-1);
	memcpy(blockData, buffer, csi->vol->bytes_per_sector);
	block_cache_put(csi->vol->fBlockCache, block);
 
	return B_OK;
}
 
 
//	#pragma mark -
 
 
static void
_diri_release_current_block_(struct diri *diri)
{
	ASSERT(diri->current_block);
	if (diri->current_block == NULL)
		return;
	csi_release_block(&(diri->csi));
	diri->current_block = NULL;
}
 
 
uint8 *
diri_init(nspace *vol, uint32 cluster, uint32 index, struct diri *diri)
{
	diri->current_block = NULL;
 
	if (cluster >= vol->total_clusters + 2)
		return NULL;
 
	if (init_csi(vol,cluster,0,&(diri->csi)) != 0)
		return NULL;
 
	diri->starting_cluster = cluster;
	diri->current_index = index;
	if (index >= vol->bytes_per_sector / 0x20
		&& iter_csi(&(diri->csi), diri->current_index
				/ (vol->bytes_per_sector / 0x20)) != 0)
		return NULL;
 
	// get current sector
	diri->current_block = csi_get_block(&diri->csi);
 
	if (diri->current_block == NULL)
		return NULL;
 
	// now the diri is valid
	return diri->current_block
		+ (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
}
 
 
int
diri_free(struct diri *diri)
{
	if (diri->current_block)
		_diri_release_current_block_(diri);
 
	return 0;
}
 
 
uint8 *
diri_current_entry(struct diri *diri)
{
	if (diri->current_block == NULL)
		return NULL;
 
	return diri->current_block
		+ (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
}
 
 
uint8 *
diri_next_entry(struct diri *diri)
{
	if (diri->current_block == NULL)
		return NULL;
 
	if ((++diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20)) == 0) {
		_diri_release_current_block_(diri);
		if (iter_csi(&(diri->csi), 1) != 0)
			return NULL;
		diri->current_block = csi_get_block(&(diri->csi));
		if (diri->current_block == NULL)
			return NULL;
	}
 
	return diri->current_block
		+ (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
}
 
 
uint8 *
diri_rewind(struct diri *diri)
{
	if (diri->current_index > (diri->csi.vol->bytes_per_sector / 0x20 - 1)) {
		if (diri->current_block)
			_diri_release_current_block_(diri);
		if (init_csi(diri->csi.vol, diri->starting_cluster, 0, &(diri->csi)) != 0)
			return NULL;
		diri->current_block = csi_get_block(&diri->csi);
	}
	diri->current_index = 0;
	return diri->current_block;
}
 
 
void
diri_make_writable(struct diri *diri)
{
	csi_make_writable(&diri->csi);
}

V547 Expression 'sector < 0' is always false. Unsigned type value is never < 0.