#ifndef __SPADFS_H
#define __SPADFS_H

#define __NOT_FROM_SPAD
#define __FROM_LINUX_KERNEL

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include <linux/parser.h>
#include <linux/statfs.h>
#include <linux/vmalloc.h>
#include <linux/namei.h>
#include <linux/quotaops.h>
#include <linux/version.h>

#include "linux.h"

#include "STRUCT.H"

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
#define NEW_WORKQUEUE
#endif

/* Allocate in other zones if the group has 1/2 avg free. Must be power of 2 */
#define SPADFS_AVG_FREE_DIVIDE_OTHERZONE	2

#define SPADFS_DEFAULT_PREALLOC_PART		8
#define SPADFS_DEFAULT_MIN_PREALLOC		4096
#define SPADFS_DEFAULT_MAX_PREALLOC		1048576
#define SPADFS_DEFAULT_SYNC_TIME		(120 * HZ)

typedef struct __spadfnode SPADFNODE;
typedef struct __spadfs SPADFS;

#define EFSERROR	EPERM

#define MEMORY_SUPER_MAGIC	('S' | ('P' << 8) | ('A' << 16) | ('D' << 24))

/*
 * Internal and external inode numbers:
 *	SpadFS doesn't have limit on number of files, but Linux has only 32-bit
 *	inode numbers. So the inode numbers can't be used to determine files.
 *
 *	We use 64-bit value spadfs_ino_t to denote a file. This value has
 *	embedded sector number and position within sector. The value should be
 *	created with make_spadfs_ino_t and its parts can be extracted with
 *	spadfs_ino_t_sec and spadfs_ino_t_pos. Position must be >= 24, <= 448
 *	and dividable by 8, as is specified by disk layout.
 *
 *	Fixed fnodes have position "0" and sector number is the number of fixed
 *	fnode block. The are created with make_fixed_spadfs_ino_t and tested
 *	with is_pos_fixed or is_fnode_fixed.
 *
 *	spadfs_ino_t_deleted is the special value that is assigned to
 *	deleted-but-open files. They have no on-disk representation.
 *
 *	To keep userspace happy, we must manufacture 32-bit ino_t value somehow.
 *	Use the fact that no two fnodes can be less than 64-bytes apart --- so
 *	use top 3 bits of position and bottom 29 bits of sector number.
 *	spadfs_ino_t_2_ino_t does this. If the disk has at most 256GiB, ino_t
 *	will be collision-free, otherwise collisions are possible but unlikely.
 *
 *	Kernels starting with 2.6.19 can return 64-bit ino, so
 *	spadfs_ino_t_2_ino64_t returns 64-bit value on them.
 */

typedef __u64 spadfs_ino_t;
#define make_spadfs_ino_t(sec, pos)	((sec) | ((__u64)(pos) << 55))
#define make_fixed_spadfs_ino_t(sec)	make_spadfs_ino_t(sec, 0)
#define spadfs_ino_t_sec(ino)		((__d_off)(ino) & (__d_off)(((__u64)1 << 48) - 1))
#define spadfs_ino_t_pos(ino)		((unsigned)((ino) >> 55))
#define spadfs_ino_t_2_ino_t(ino)	((ino_t)((ino) >> 61) | ((ino_t)(ino) << 3))
#define spadfs_ino_t_2_ino64_t(ino)	(((ino) >> 61) | ((ino) << 3))
#define spadfs_ino_t_deleted		((spadfs_ino_t)1)
#define is_pos_fixed(pos)		(!(pos))

#define is_fnode_fixed(fnode)		(is_pos_fixed(spadfs_ino_t_pos(fnode->spadfs_ino)))

/*
 * Encoding directory position into 64-bit off_t. Ideally, the directory
 * position would need 32+48+3 bits (hash+block in chain+position), really we
 * strip block in chain to 29 bits --- for directories that have more than 2^29
 * blocks in hash-collision chain, readdir won't work.
 *
 * Rotate the bits somehow so that if old application accesses directory with
 * only 32-bit offset, it most time works (16 bits --- hash, 3 bits ---
 * position, 13 bits --- chain).
 *
 * Special directory offsets --- used to return entries "." and ".." that do not
 * really exist in the directory. Readdir can use macros is_dir_off_t_special,
 * dir_off_t_special_n and make_special_dir_off_t to implement simple state
 * machine.
 */

#define make_dir_off_t(hash, order, pos)	(((hash) & 0xffff) | ((((pos) + (0x40 - SIZEOF_FNODE_BLOCK)) & 0x1c0) << (16 - 6)) | (((order) & 0x1fff) << (16 + 3)) | (((__u64)((hash) & 0xffff0000) << 16) | ((__u64)((order) & 0x1fffe000) << (32 + 3))))
#define dir_off_t_hash(off)		(((__u32)(off) & 0xffff) | ((__u32)((off) >> 16) & 0xffff0000))
#define dir_off_t_order(off)		((((__u32)(off) >> (16 + 3)) & 0x1fff) | ((__u32)((off) >> (32 + 3)) & 0x1fffe000))
#define dir_off_t_pos(off)		((((__u32)(off) >> (16 - 6)) & 0x1c0) - (0x40 - SIZEOF_FNODE_BLOCK))

#define is_dir_off_t_special(off)	(!((__u32)(off) & (0x1c0 << (16 - 6))))
#define dir_off_t_special_n(off)	((__u32)(off))
#define make_special_dir_off_t(n)	(n)

#define MAX_CHAIN_LENGTH		((1 << 29) - 1)

/*
 * Split 48-bit values to 32+32 bits or 16+16+16 bits and assemble them back.
 * gcc generates very lame code for constructs like ((__u64)(h) << 32) | (l)),
 * inline assembler would likely be better.
 */

#define MAKE_D_OFF(l,h)	(sizeof(__d_off) <= 4 ? (l) : ((__u64)(h) << 32) | (l))
#define MAKE_PART_0(l)	((__u32)(l))
#define MAKE_PART_1(l)	(sizeof(__d_off) <= 4 ? 0 : ((__d_off)(l) >> 32))

#define MAKE_D_OFF3(l,h,hh) (sizeof(__d_off) <= 4 ? (l) | ((h) << 16) : (l) | ((h) << 16) | ((__u64)(hh) << 32))
#define MAKE_PART_30(l)	((__u16)(l))
#define MAKE_PART_31(l)	((__u16)((__u32)(l) >> 16))
#define MAKE_PART_32(l)	(sizeof(__d_off) <= 4 ? 0 : ((__d_off)(l) >> 32))

/*
 * One mapped apage. Really these structures form array of
 * [ pagesize / 2 * blocksize ] APAGE_MAPs.
 * Buffer head, entry is bh->b_data;
 */

typedef struct {
	struct aentry *entry;	/* [ blocksize / 16 ] */
	struct buffer_head *bh;
} APAGE_MAP;			/* [ pagesize / 2 / blocksize ] */

/*
 * Describes a zone. A total 3 zones exist --- metadata, small files, large
 * files.
 * grp_start of next zone must be grp_start+grp_n of previous zone, grp_start of
 * the first zone must be 0 --- a lot of code depends on it.
 */

struct spadfszone {
	__d_off freespace;
	unsigned grp_start;
	unsigned grp_n;
};

/*
 * Describes a group.
 */

struct spadfsgroup_info {
	__d_off freespace;
	struct spadfszone *zone;
};

struct __spadfnode {
	SPADFS *fs;
	unsigned prealloc; /* Number of preallocated block --- disk-size - i_size */
	loff_t disk_size; /* Size of file on disk (including prealloc) */
	loff_t mmu_private;
	spadfs_ino_t spadfs_ino;

	/* Copy of entries in fnode on disk */
	__d_off blk1;
	__d_off blk2;
	unsigned blk1_n;
	unsigned blk2_n;
	__d_off root;

	/* This fnode's and parent fnode's block and position. For fixed fnodes,
	 * parent values are 0. */
	__d_off fnode_block;
	__d_off parent_fnode_block;
	unsigned fnode_pos;
	unsigned parent_fnode_pos;

	/* Pointer to UNX attribute within "ea" array to speed-up access.
	 * Or NULL if there is no UNX attribute. */
	struct ea_unx *ea_unx;

	/* List of all inodes. Maybe it is superfluous and kernel inode list
	 * could be used */
	struct hlist_node inode_list;
	/* Only for files. Directories are reasonably protected by the kernel */
	struct semaphore lock;

	/* Cache for one extent --- physical block, logical block, number of
	 * blocks */
	__d_off extent_cache_pb;
	__d_off extent_cache_lb;
	__d_off extent_cache_size;

	struct inode vfs_inode;

	/* Real number of links, see comment on set_nlink */
	__u64 spadfs_nlink;

	/* Extended attributes */
	unsigned ea_size;
	__u8 ea[FNODE_MAX_EA_SIZE];
};

struct __spadfs {
	__s32 *cct; /* Crash count table --- an array of 65536 32-bit values */
	__u16 cc; /* Current crash count */
	__s32 txc; /* cct[cc] */
	__s32 a_txc; /* txc/cc pair selecting one of two apage indices */
	__u16 a_cc;
	/* Values selected with mkspadfs. "_bits" suffix means log2 of the value */
	unsigned char sectors_per_block_bits;
	unsigned char sectors_per_fnodepage_bits;
	unsigned char sectors_per_page_bits;
	unsigned char pagesize_bits;
	unsigned char dnode_hash_bits;
	unsigned char sectors_per_cluster_bits;
	unsigned char sectors_per_group_bits;
	unsigned apage_size;
	unsigned blocksize;
	unsigned dnode_data_size;
	unsigned dnode_page_sectors;
	unsigned n_apage_mappings;
	__u32 cluster_threshold;
	/* Mount options */
	uid_t uid;
	gid_t gid;
	umode_t mode;
	unsigned prealloc_part;
	unsigned min_prealloc;
	unsigned max_prealloc;
	unsigned xfer_size;
	/* Values read from superblock */
	__d_off txb_sec;
	__d_off apage_index0_sec;
	__d_off apage_index1_sec;
	__d_off cct_sec;
	__d_off root_sec;
	__d_off size;
	__d_off freespace;
	__d_off group_mask;
	__u32 flags_compat_fsck;
	__u32 flags_compat_rw;
	__u32 flags_compat_ro;
	__u32 flags_compat_none;
	unsigned n_apages;		/* Total apages */
	unsigned n_active_apages;	/* Used apages */
	struct apage_index_entry *apage_index;
	struct apage_info *apage_info;
	unsigned n_groups;		/* Total groups */
	struct spadfsgroup_info *group_info;	/* Group descriptors */
	struct spadfszone zones[3];		/* Zone descriptors */
	APAGE_MAP *tmp_map;
	struct semaphore alloc_lock;
	struct rw_semaphore sync_lock;
	struct hlist_head inode_list; /* List of all inodes. Maybe superfluous */
	struct semaphore inode_list_lock; /* List's lock */
	struct super_block *s;	/* VFS part of superblock */
	struct workqueue_struct *spadfs_syncer;	/* Syncer thread */
	unsigned long spadfs_sync_time;		/* Sync time */
#ifndef NEW_WORKQUEUE
	struct work_struct spadfs_sync_work;
#else
	struct delayed_work spadfs_sync_work;
#endif
	unsigned mount_flags;
	__u32 txflags;		/* Error flags */
	__u32 txflags_new;	/* New error flags (if txflags_new & ~txflags,
				   error flags need to be written) */
};

#define MOUNT_FLAGS_CHECKSUMS_OVERRIDE	1
#define MOUNT_FLAGS_CHECKSUMS		2

struct apage_info {
	APAGE_MAP *map[2];
};

#define spadfs(s)	((SPADFS *)((s)->s_fs_info))
#define spadfnode(i)	list_entry(i, SPADFNODE, vfs_inode)
#define inode(i)	(&(i)->vfs_inode)

#define CC_VALID(fs_, cc_, txc_)	((__s32)((__u32)(fs_)->cct[(__u16)(cc_)] - (__u32)(txc_)) >= 0)

#define CC_CURRENT(fs_, cc_, txc_)	(!(((__u16)(cc_) ^ (fs_)->cc) | (((txc_) ^ (fs_)->txc) & 0x7fffffff)))

static __finline__ void CC_SET_CURRENT(SPADFS *fs, __u16 *cc, __s32 *txc)
{
	*txc = (~((__u32)fs->cct[*cc] - (__u32)*txc) & 0x80000000) | fs->txc;
	*cc = fs->cc;
}

static __finline__ void CC_SET_CURRENT_INVALID(SPADFS *fs, __u16 *cc, __s32 *txc)
{
	*txc = fs->txc ^ 0x80000000;
	*cc = fs->cc;
}

/*
 * Use this if you don't want partially modified buffer be written to disk.
 */

static __finline__ void start_atomic_buffer_modify(struct buffer_head *bh)
{
	clear_buffer_dirty(bh);
	wait_on_buffer(bh);
}

static __finline__ void end_atomic_buffer_modify(struct buffer_head *bh)
{
	mark_buffer_dirty(bh);
}

/*
 * The same functionality, but can be called simultaneously for the same buffer.
 * Need to serialize.
 */

static __finline__ void start_concurrent_atomic_buffer_modify(struct buffer_head *bh)
{
	lock_buffer(bh);
}

static __finline__ void end_concurrent_atomic_buffer_modify(struct buffer_head *bh)
{
	unlock_buffer(bh);
	mark_buffer_dirty(bh);
}

/*
 * The same as previous, but read-only access. Used when checking checksums.
 */

static __finline__ void start_concurrent_atomic_buffer_read(struct buffer_head *bh)
{
	lock_buffer(bh);
}

static __finline__ void end_concurrent_atomic_buffer_read(struct buffer_head *bh)
{
	unlock_buffer(bh);
}

#define check_checksums(fs)	(!((fs)->flags_compat_fsck & FLAG_COMPAT_FSCK_NO_CHECKSUMS))
#define make_checksums(fs)	(!((fs)->flags_compat_fsck & FLAG_COMPAT_FSCK_NO_CHECKSUMS))

/*
 * SpadFS allows more than 2^32 hardlinks to a file, however i_nlink is only
 * 32-bit. So keep internal 64-bit value "spadfs_nlink" and external value
 * i_nlink exposed to stat().
 */

static __finline__ void set_nlink(struct inode *inode)
{
	inode->i_nlink = spadfnode(inode)->spadfs_nlink;
	if (__unlikely(inode->i_nlink != spadfnode(inode)->spadfs_nlink)) inode->i_nlink = -1;
}

/* alloc.c */

#define ALLOC_METADATA		0x00010000
#define ALLOC_SMALL_FILE	0x00020000
#define ALLOC_BIG_FILE		0x00040000
#define ALLOC_PARTIAL_AT_GOAL	0x00080000
#define ALLOC_NEW_GROUP_HINT	0x00100000

#define ALLOC_FREE_FROM		0x80000000

#define ALLOC_MASK_MASK		0x0000FFFF
#define ALLOC_MASK_1		0x00000001
#define ALLOC_MASK(flags)	(((flags) / ALLOC_MASK_1) & ALLOC_MASK_MASK)

struct alloc {
	__d_off sector;
	__d_off top;
	unsigned n_sectors;
	unsigned flags;
};

int SPADFS_COUNT_FREE_SPACE(SPADFS *fs);
int SPADFS_ALLOC_BLOCKS(SPADFNODE *f, struct alloc *al);
int SPADFS_FREE_BLOCKS_(SPADFNODE *f, __d_off start, __d_off n_blocks, int account);
static __finline__ int SPADFS_FREE_BLOCKS(SPADFNODE *f, __d_off start, __d_off n_blocks)
{
	int r;
	down(&f->fs->alloc_lock);
	r = SPADFS_FREE_BLOCKS_(f, start, n_blocks, 1);
	up(&f->fs->alloc_lock);
	return r;
}

/* buffer.c */

void *SPADFS_READ_SECTOR(SPADFS *fs, __d_off secno, struct buffer_head **bhp, char *msg);
void *SPADFS_GET_NEW_SECTOR(SPADFS *fs, __d_off secno, struct buffer_head **bhp, char *msg);
struct txblock *SPADFS_READ_TX_BLOCK(SPADFS *fs, struct buffer_head **bhp, char *msg);
#define SRFB_FNODE		1
#define SRFB_FIXED_FNODE	2
#define SRFB_DNODE		4
struct fnode_block *SPADFS_READ_FNODE_BLOCK(SPADFS *fs, __d_off secno, struct buffer_head **bhp, int struct_type, char *msg);
struct anode *SPADFS_READ_ANODE(SPADFS *fs, __d_off secno, struct buffer_head **bhp, unsigned *vx, char *msg);

/* dir.c */

#define HINT_META	0
#define HINT_SMALL	1
#define HINT_BIG	2

__d_off SPADFS_ALLOC_HINT(SPADFNODE *f, int hint);
void SPADFS_SET_NEW_HINT(SPADFNODE *f, struct alloc *al);
void SPADFS_GET_DIR_HINT(SPADFNODE *f, __u16 *small, __u16 *big);
int SPADFS_WRITE_FILE(SPADFNODE *f);
int SPADFS_WRITE_DIRECTORY(SPADFNODE *f);

/* file.c */

__d_off SPADFS_SIZE_2_BLKS(SPADFS *fs, loff_t size);

void SPADFS_DELETE_FILE_CONTENT(SPADFNODE *f);
int SPADFS_FILE_SETATTR(struct dentry *dentry, struct iattr *iattr);

extern struct inode_operations spadfs_file_iops;
extern struct file_operations spadfs_file_fops;
extern struct address_space_operations spadfs_file_aops;

/* inode.c */

struct inode *SPADFS_IGET(struct super_block *s, spadfs_ino_t ino, __d_off parent_block, unsigned parent_pos);
struct inode *SPADFS_NEW_INODE(struct inode *dir, mode_t mode, time_t ctime, void *ea, unsigned ea_size, __d_off fnode_block, unsigned fnode_pos, __d_off dir_root);
void SPADFS_DELETE_INODE(struct inode *i);
int SPADFS_FSYNC(struct file *file, struct dentry *dentry, int datasync);
void SPADFS_MOVE_FNODE_PTR(SPADFS *sfs, __d_off src_sec, unsigned src_pos, __d_off dst_sec, unsigned dst_pos, int is_dir);
void SPADFS_UPDATE_EA(struct inode *inode);
int SPADFS_SETATTR_COMMON(struct inode *inode, struct iattr *iattr);
int SPADFS_GETATTR(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);

/* link.c */

extern struct inode_operations spadfs_symlink_iops;

/* name.c */

int SPADFS_COMPARE_NAMES(SPADFS *fs, const char *n1, unsigned l1, const char *n2, unsigned l2);
void SPADFS_SET_NAME(SPADFS *fs, char *dest, const char *src, unsigned len);

extern struct dentry_operations spadfs_dops;

static inline void SPADFS_SET_DENTRY_OPERATIONS(SPADFS *fs, struct dentry *dentry)
{
	if (__unlikely(!(fs->flags_compat_none & FLAG_COMPAT_NONE_UNIX_NAMES)))
		dentry->d_op = &spadfs_dops;
}

/* namei.c */

extern struct inode_operations spadfs_dir_iops;
extern struct file_operations spadfs_dir_fops;

/* super.c */

void __attribute__ ((__format__ (__printf__, 3, 4))) SPADFS_ERROR(SPADFS *fs, unsigned flags, char *msg, ...);
int SPADFS_STOP_CYCLES(SPADFS *fs, __d_off key, __d_off (*c)[2], char *msg);
void SPADFS_TX_BLOCK_CHECKSUM(struct txblock *tb);
int SPADFS_COMMIT(SPADFS *fs);

#endif
