void REALLY_DO_FNODE_BLOCK_CHECKSUM(struct fnode_block *fnode_block);

static __finline__ void DO_FNODE_BLOCK_CHECKSUM(SPADFS *fs, struct fnode_block *fnode_block)
{
	if (__unlikely(make_checksums(fs))) REALLY_DO_FNODE_BLOCK_CHECKSUM(fnode_block);
	else fnode_block->flags &= ~FNODE_BLOCK_CHECKSUM_VALID;
}

#define VALIDATE_FNODE(fs, fnode_block, fnode, size, namelen, ok, skip_free, bad_fnode)\
do {									\
	if (__unlikely((((unsigned long)(fnode) + (size) - 1) & ~(unsigned long)(FNODE_BLOCK_SIZE - 1)) != ((unsigned long)(fnode_block) & ~(unsigned long)(FNODE_BLOCK_SIZE - 1)))) goto bad_fnode;\
	if (((fnode)->next & FNODE_NEXT_FREE) && CC_VALID((fs), (fnode)->cc, (fnode)->txc)) {\
		if (__unlikely((size) < FNODE_HEAD_SIZE)) goto bad_fnode;\
		goto skip_free;						\
	}								\
	if (__unlikely((size) <= FNODE_NAME_POS) || __unlikely((unsigned)((size) - FNODE_EA_POS(namelen)) > FNODE_MAX_EA_SIZE)) goto bad_fnode;\
	goto ok;							\
} while (0)

static __finline__ void SET_SPADFS_FILE(SPADFNODE *f, struct fnode *fnode, int part)
{
	loff_t pa;
	fnode->ctime = inode(f)->i_ctime.tv_sec;
	fnode->mtime = inode(f)->i_mtime.tv_sec;
	fnode->flags = 0;
	fnode->run10 = MAKE_PART_0(f->blk1);
	fnode->run11 = MAKE_PART_1(f->blk1);
	fnode->run1n = f->blk1_n;
	fnode->run20 = MAKE_PART_0(f->blk2);
	fnode->run21 = MAKE_PART_1(f->blk2);
	fnode->run2n = f->blk2_n;
	fnode->anode0 = MAKE_PART_0(f->root);
	fnode->anode1 = MAKE_PART_1(f->root);
	pa = f->disk_size - i_size_read(inode(f));
	if (__unlikely((pa & ~0xffffffffULL) != 0)) pa = 0;
	if (!part) {
		fnode->size0 = f->disk_size;
		if (__likely(f->ea_unx != NULL)) f->ea_unx->prealloc0 = pa;
	} else {
		fnode->size1 = f->disk_size;
		if (__likely(f->ea_unx != NULL)) f->ea_unx->prealloc1 = pa;
	}
}

static __finline__ void SET_SPADFS_DIRECTORY(SPADFNODE *f, struct fnode *fnode, int part)
{
	fnode->ctime = inode(f)->i_ctime.tv_sec;
	fnode->mtime = inode(f)->i_mtime.tv_sec;
	fnode->flags = FNODE_FLAGS_DIR;
	if (!part) {
		fnode->run10 = MAKE_PART_0(f->root);
		fnode->run11 = MAKE_PART_1(f->root);
		fnode->size0 = 0;
	} else {
		fnode->run20 = MAKE_PART_0(f->root);
		fnode->run21 = MAKE_PART_1(f->root);
		fnode->size1 = 0;
	}
}

static __finline__ void WRITE_EA(SPADFNODE *f, struct fnode *fnode)
{
	unsigned long ea = (unsigned long)fnode + FNODE_EA_POS(fnode->namelen);
	if (__unlikely(((ea + f->ea_size - 1) & ~(unsigned long)(FNODE_BLOCK_SIZE - 1)) != ((unsigned long)fnode & ~(unsigned long)(FNODE_BLOCK_SIZE - 1)))) {
		SPADFS_ERROR(f->fs, TXFLAGS_FS_ERROR, "CAN'T WRITE EXTENDED ATTRIBUTES ON FNODE %LX/%X", (unsigned long long)f->fnode_block, f->fnode_pos);
		return;
	}
	memcpy((void *)ea, f->ea, f->ea_size);
}

static __finline__ void SPADFS_INIT_UNX(struct inode *dir, struct ea_unx *ea, int mode)
{	
	ea->magic = EA_UNX_MAGIC;
	ea->mode = mode;
	ea->flags = 0;
	ea->uid = current->fsuid;
	ea->gid = current->fsgid;
	if (__unlikely(dir->i_mode & S_ISGID)) {
		ea->gid = dir->i_gid;
		if (__unlikely(S_ISDIR(mode))) {
			mode |= S_ISGID;
		}
	}
	ea->prealloc0 = 0;
	ea->prealloc1 = 0;
}

static __finline__ void MAKE_FIXED_FNODE(struct fnode *fnode, __d_off blk)
{
	fnode->size0 = fnode->size1 = fnode->ctime = fnode->mtime = fnode->run10 = fnode->run11 = fnode->run1n = fnode->run20 = fnode->run21 = fnode->run2n = 0;
	fnode->anode0 = MAKE_PART_0(blk);
	fnode->anode1 = MAKE_PART_1(blk);
	fnode->flags = FNODE_FLAGS_HARDLINK;
}

static __finline__ void ND_LOCK(SPADFS *fs, int wlock)
{
	if (__likely(!wlock)) down_read(&fs->sync_lock);
	else down_write(&fs->sync_lock);
}

static __finline__ void ND_UNLOCK(SPADFS *fs, int wlock)
{
	if (__likely(!wlock)) up_read(&fs->sync_lock);
	else up_write(&fs->sync_lock);
}

int SPADFS_ALLOC_FIXED_FNODE_BLOCK(SPADFNODE *fn, __d_off hint, unsigned size, __u16 hint_small, __u16 hint_big, __d_off *result);
int SPADFS_ALLOC_LEAF_PAGE(SPADFNODE *fn, __d_off hint, unsigned sectors, __d_off prev, __d_off *result);
struct fnode_block *SPADFS_FIND_HASH_BLOCK(SPADFNODE *f, hash_t hash, struct buffer_head **bh, __d_off *secno, hash_t *next_hash);
int SPADFS_LOOKUP_INO(SPADFNODE *f, struct qstr *qstr, spadfs_ino_t *ino, int for_delete);
int SPADFS_CHECK_DIR_EMPTY(SPADFNODE *f);
int SPADFS_REMOVE_RECURSIVE(SPADFNODE *fn, __d_off root, int depth);
#define NEED_SYNC	((void *)1)
#define NEED_WLOCK	((void *)2)
struct fnode *SPADFS_ADD_FNODE_TO_DIRECTORY(SPADFNODE *dir, const char *name, unsigned namelen, unsigned ea_size, struct buffer_head **bh, __d_off *fnode_address, unsigned *fnode_off, struct fnode_block **pfnode_block, int wlock);
int SPADFS_REMOVE_FNODE_FROM_DIRECTORY(SPADFNODE *dir, SPADFNODE *file, struct qstr *name);
int SPADFS_REFILE_FNODE(SPADFNODE *dir, SPADFNODE *file);
