#include "spadfs.h"

void *SPADFS_READ_SECTOR(SPADFS *fs, __d_off secno, struct buffer_head **bhp, char *msg)
{
	cond_resched();
	if (__likely((*bhp = sb_bread(fs->s, secno >> fs->sectors_per_block_bits)) != NULL)) return (char *)(*bhp)->b_data + (((unsigned)secno << 9) & ((512 << fs->sectors_per_block_bits) - 512));
	SPADFS_ERROR(fs, TXFLAGS_IO_READ_ERROR, "READ ERROR, BLOCK %LX AT %s", (unsigned long long)secno, msg);
	return ERR_PTR(-EIO);
}

void *SPADFS_GET_NEW_SECTOR(SPADFS *fs, __d_off secno, struct buffer_head **bhp, char *msg)
{
	cond_resched();
	if (__unlikely((unsigned)secno & ((1 << fs->sectors_per_block_bits) - 1))) {
		SPADFS_ERROR(fs, TXFLAGS_FS_ERROR, "UNALIGNED GET NEW SECTOR %LX AT %s", (unsigned long long)secno, msg);
		return ERR_PTR(-EFSERROR);
	}
	if (__likely((*bhp = sb_getblk(fs->s, secno >> fs->sectors_per_block_bits)) != NULL)) {
		if (!buffer_uptodate(*bhp)) wait_on_buffer(*bhp);
		set_buffer_uptodate(*bhp);
		return (char *)(*bhp)->b_data;
	}
	SPADFS_ERROR(fs, TXFLAGS_IO_READ_ERROR, "CAN'T GET NEW SECTOR %LX AT %s", (unsigned long long)secno, msg);
	return ERR_PTR(-ENOMEM);
}

struct txblock *SPADFS_READ_TX_BLOCK(SPADFS *fs, struct buffer_head **bhp, char *msg)
{
	struct txblock *tb = SPADFS_READ_SECTOR(fs, fs->txb_sec, bhp, msg);
	if (__unlikely(IS_ERR(tb))) return tb;
	if (__unlikely(tb->magic != TXBLOCK_MAGIC)) {
		SPADFS_ERROR(fs, TXFLAGS_FS_ERROR, "BAD MAGIC ON TX BLOCK %LX AT %s", (unsigned long long)fs->txb_sec, msg);
		rel_err:
		__brelse(*bhp);
		return ERR_PTR(-EFSERROR);
	}
	if (__unlikely(check_checksums(fs))) {
		if (__unlikely(__byte_sum(tb, 512) != CHECKSUM_BASE)) {
			SPADFS_ERROR(fs, TXFLAGS_CHECKSUM_ERROR, "BAD CHECKSUM ON TX BLOCK %LX AT %s", (unsigned long long)fs->txb_sec, msg);
			goto rel_err;
		}
	}
	return tb;
}

struct fnode_block *SPADFS_READ_FNODE_BLOCK(SPADFS *fs, __d_off secno, struct buffer_head **bhp, int struct_type, char *msg)
{
	struct fnode_block *fnode_block = SPADFS_READ_SECTOR(fs, secno, bhp, msg);
	if (__unlikely(IS_ERR(fnode_block))) return fnode_block;
	if (__likely(fnode_block->magic == FNODE_BLOCK_MAGIC)) {
		if (__unlikely(!(struct_type & SRFB_FNODE))) goto bad_magic;
		check_fn:
		if (__unlikely(check_checksums(fs)) && __likely(fnode_block->flags & FNODE_BLOCK_CHECKSUM_VALID)) {
			start_concurrent_atomic_buffer_read(*bhp);
			if (__likely(fnode_block->flags & FNODE_BLOCK_CHECKSUM_VALID)) {
				if (__unlikely(__byte_sum(fnode_block, 512) != CHECKSUM_BASE)) {
					end_concurrent_atomic_buffer_read(*bhp);
					SPADFS_ERROR(fs, TXFLAGS_CHECKSUM_ERROR, "BAD CHECKSUM ON FNODE BLOCK %LX AT %s", (unsigned long long)secno, msg);
					goto rel_err;
				}
			}
			end_concurrent_atomic_buffer_read(*bhp);
		}
		return fnode_block;
	}
	if (__likely(fnode_block->magic == DNODE_PAGE_MAGIC)) {
		if (__unlikely(!(struct_type & SRFB_DNODE))) goto bad_magic;
		if (__unlikely((((struct dnode_page *)fnode_block)->gflags & DNODE_GFLAGS_PAGE_SIZE_BITS_MINUS_1) >> DNODE_GFLAGS_PAGE_SIZE_BITS_MINUS_1_SHIFT != fs->sectors_per_page_bits - 1)) {
			SPADFS_ERROR(fs, TXFLAGS_FS_ERROR, "DNODE %LX HAS INVALID PAGE SIZE AT %s", (unsigned long long)secno, msg);
			goto rel_err;
		}
		return fnode_block;
	}
	if (__likely(fnode_block->magic == FIXED_FNODE_BLOCK_MAGIC)) {
		if (__unlikely(!(struct_type & SRFB_FIXED_FNODE))) goto bad_magic;
		goto check_fn;
	}
	bad_magic:
	SPADFS_ERROR(fs, TXFLAGS_FS_ERROR, "BAD MAGIC %08X ON FNODE BLOCK %LX AT %s", fnode_block->magic, (unsigned long long)secno, msg);
	rel_err:
	__brelse(*bhp);
	return ERR_PTR(-EFSERROR);
}

struct anode *SPADFS_READ_ANODE(SPADFS *fs, __d_off secno, struct buffer_head **bhp, unsigned *vx, char *msg)
{
	struct anode *anode = SPADFS_READ_SECTOR(fs, secno, bhp, msg);
	if (__unlikely(IS_ERR(anode))) return anode;
	if (__unlikely(anode->magic != ANODE_MAGIC)) {
		SPADFS_ERROR(fs, TXFLAGS_FS_ERROR, "BAD MAGIC ON ANODE %LX AT %s", (unsigned long long)secno, msg);
		rel_err:
		__brelse(*bhp);
		return ERR_PTR(-EFSERROR);
	}
	if (__unlikely(check_checksums(fs)) && __likely(anode->flags & ANODE_CHECKSUM_VALID)) {
		if (__unlikely(__byte_sum(anode, 512) != CHECKSUM_BASE)) {
			SPADFS_ERROR(fs, TXFLAGS_CHECKSUM_ERROR, "BAD CHECKSUM ON ANODE %LX AT %s", (unsigned long long)secno, msg);
			goto rel_err;
		}
	}
	*vx = anode->valid_extents;
	if (__unlikely((unsigned)(*vx - 1) >= ANODE_N_EXTENTS)) {
		SPADFS_ERROR(fs, TXFLAGS_FS_ERROR, "ANODE %LX HAS %u VALID EXTENTS AT %s", (unsigned long long)secno, (unsigned)anode->valid_extents, msg);
		goto rel_err;
	}
	return anode;
}

