/*
 * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#ifndef __NETFS_H
#define __NETFS_H

#include <linux/types.h>
#include <linux/connector.h>
#include <linux/backing-dev.h>

#define POHMELFS_CN_IDX			5
#define POHMELFS_CN_VAL			0

#define POHMELFS_CTLINFO_ACK		1
#define POHMELFS_NOINFO_ACK		2

#define POHMELFS_NULL_IDX		65535

/*
 * Network command structure.
 * Will be extended.
 */
struct netfs_cmd {
	__u16			cmd;	/* Command number */
	__u16			csize;	/* Attached crypto information size */
	__u16			cpad;	/* Attached padding size */
	__u16			ext;	/* External flags */
	__u32			size;	/* Size of the attached data */
	__u32			trans;	/* Transaction id */
	__u64			id;	/* Object ID to operate on. Used for feedback.*/
	__u64			start;	/* Start of the object. */
	__u64			iv;	/* IV sequence */
	__u8			data[0];
};

static inline void netfs_convert_cmd(struct netfs_cmd *cmd)
{
	cmd->id = __be64_to_cpu(cmd->id);
	cmd->start = __be64_to_cpu(cmd->start);
	cmd->iv = __be64_to_cpu(cmd->iv);
	cmd->cmd = __be16_to_cpu(cmd->cmd);
	cmd->ext = __be16_to_cpu(cmd->ext);
	cmd->csize = __be16_to_cpu(cmd->csize);
	cmd->cpad = __be16_to_cpu(cmd->cpad);
	cmd->size = __be32_to_cpu(cmd->size);
}

#define NETFS_TRANS_SINGLE_DST		(1<<0)

enum {
	NETFS_READDIR	= 1,	/* Read directory for given inode number */
	NETFS_READ_PAGE,	/* Read data page from the server */
	NETFS_WRITE_PAGE,	/* Write data page to the server */
	NETFS_CREATE,		/* Create directory entry */
	NETFS_REMOVE,		/* Remove directory entry */

	NETFS_LOOKUP,		/* Lookup single object */
	NETFS_LINK,		/* Create a link */
	NETFS_TRANS,		/* Transaction */
	NETFS_OPEN,		/* Open intent */
	NETFS_INODE_INFO,	/* Metadata cache coherency synchronization message */

	NETFS_PAGE_CACHE,	/* Page cache invalidation message */
	NETFS_READ_PAGES,	/* Read multiple contiguous pages in one go */
	NETFS_RENAME,		/* Rename object */
	NETFS_CAPABILITIES,	/* Capabilities of the client, for example supported crypto */
	NETFS_LOCK,		/* Distributed lock message */

	NETFS_XATTR_SET,	/* Set extended attribute */
	NETFS_XATTR_GET,	/* Get extended attribute */
	NETFS_CMD_MAX
};

enum {
	POHMELFS_FLAGS_ADD = 0, /* Network state control message for ADD */
	POHMELFS_FLAGS_DEL,     /* Network state control message for DEL */
	POHMELFS_FLAGS_SHOW,    /* Network state control message for SHOW */
	POHMELFS_FLAGS_CRYPTO,	/* Crypto data control message */
	POHMELFS_FLAGS_MODIFY,	/* Network state modification message */
	POHMELFS_FLAGS_DUMP,	/* Network state control message for SHOW ALL */
	POHMELFS_FLAGS_FLUSH,	/* Network state control message for FLUSH */
};

/*
 * Always wanted to copy it from socket headers into public one,
 * since they are __KERNEL__ protected there.
 */
#define _K_SS_MAXSIZE	128

struct saddr {
	unsigned short		sa_family;
	char			addr[_K_SS_MAXSIZE];
};

enum {
	POHMELFS_CRYPTO_HASH = 0,
	POHMELFS_CRYPTO_CIPHER,
};

struct pohmelfs_crypto {
	unsigned int		idx;		/* Config index */
	unsigned short		strlen;		/* Size of the attached crypto string including 0-byte
						 * "cbc(aes)" for example */
	unsigned short		type;		/* HMAC, cipher, both */
	unsigned int		keysize;	/* Key size */
	unsigned char		data[0];	/* Algorithm string, key and IV */
};

#define POHMELFS_IO_PERM_READ		(1<<0)
#define POHMELFS_IO_PERM_WRITE		(1<<1)

/*
 * Configuration command used to create table of different remote servers.
 */
struct pohmelfs_ctl {
	__u32			idx;		/* Config index */
	__u32			type;		/* Socket type */
	__u32			proto;		/* Socket protocol */
	__u16			addrlen;	/* Size of the address */
	__u16			perm;		/* IO permission */
	__u16			prio;		/* IO priority */
	struct saddr		addr;		/* Remote server address */
};

/*
 * Ack for userspace about requested command.
 */
struct pohmelfs_cn_ack {
	struct cn_msg		msg;
	int			error;
	int			msg_num;
	int			unused[3];
	struct pohmelfs_ctl	ctl;
};

/*
 * Inode info structure used to sync with server.
 * Check what stat() returns.
 */
struct netfs_inode_info {
	unsigned int		mode;
	unsigned int		nlink;
	unsigned int		uid;
	unsigned int		gid;
	unsigned int		blocksize;
	unsigned int		padding;
	__u64			ino;
	__u64			blocks;
	__u64			rdev;
	__u64			size;
	__u64			version;
};

static inline void netfs_convert_inode_info(struct netfs_inode_info *info)
{
	info->mode = __cpu_to_be32(info->mode);
	info->nlink = __cpu_to_be32(info->nlink);
	info->uid = __cpu_to_be32(info->uid);
	info->gid = __cpu_to_be32(info->gid);
	info->blocksize = __cpu_to_be32(info->blocksize);
	info->blocks = __cpu_to_be64(info->blocks);
	info->rdev = __cpu_to_be64(info->rdev);
	info->size = __cpu_to_be64(info->size);
	info->version = __cpu_to_be64(info->version);
	info->ino = __cpu_to_be64(info->ino);
}

/*
 * Cache state machine.
 */
enum {
	NETFS_COMMAND_PENDING = 0,	/* Command is being executed */
	NETFS_INODE_REMOTE_SYNCED,	/* Inode was synced to server */
	NETFS_INODE_REMOTE_DIR_SYNCED,	/* Inode (directory) was synced from the server */
	NETFS_INODE_OWNED,		/* Inode is owned by given host */
	NETFS_INODE_NEED_FLUSH,		/* Inode has to be flushed to the server */
};

/*
 * POHMELFS capabilities: information about supported
 * crypto operations (hash/cipher, modes, key sizes and so on),
 * root informaion (used/available size, number of objects, permissions)
 */
enum pohmelfs_capabilities {
	POHMELFS_CRYPTO_CAPABILITIES = 0,
	POHMELFS_ROOT_CAPABILITIES,
};

/* Read-only mount */
#define POHMELFS_FLAGS_RO		(1<<0)
/* Extended attributes support on/off */
#define POHMELFS_FLAGS_XATTR		(1<<1)

struct netfs_root_capabilities {
	__u64			nr_files;
	__u64			used, avail;
	__u64			flags;
};

static inline void netfs_convert_root_capabilities(struct netfs_root_capabilities *cap)
{
	cap->nr_files = __cpu_to_be64(cap->nr_files);
	cap->used = __cpu_to_be64(cap->used);
	cap->avail = __cpu_to_be64(cap->avail);
	cap->flags = __cpu_to_be64(cap->flags);
}

struct netfs_crypto_capabilities {
	unsigned short		hash_strlen;	/* Hash string length, like "hmac(sha1) including 0 byte "*/
	unsigned short		cipher_strlen;	/* Cipher string length with the same format */
	unsigned int		cipher_keysize;	/* Cipher key size */
};

static inline void netfs_convert_crypto_capabilities(struct netfs_crypto_capabilities *cap)
{
	cap->hash_strlen = __cpu_to_be16(cap->hash_strlen);
	cap->cipher_strlen = __cpu_to_be16(cap->cipher_strlen);
	cap->cipher_keysize = __cpu_to_be32(cap->cipher_keysize);
}

enum pohmelfs_lock_type {
	POHMELFS_LOCK_GRAB	= (1<<15),

	POHMELFS_READ_LOCK	= 0,
	POHMELFS_WRITE_LOCK,
};

struct netfs_lock {
	__u64			start;
	__u64			ino;
	__u32			size;
	__u32			type;
};

static inline void netfs_convert_lock(struct netfs_lock *lock)
{
	lock->start = __cpu_to_be64(lock->start);
	lock->ino = __cpu_to_be64(lock->ino);
	lock->size = __cpu_to_be32(lock->size);
	lock->type = __cpu_to_be32(lock->type);
}

#ifdef __KERNEL__

#include <linux/kernel.h>
#include <linux/completion.h>
#include <linux/rbtree.h>
#include <linux/net.h>
#include <linux/poll.h>

/*
 * Private POHMELFS cache of objects in directory.
 */
struct pohmelfs_name {
	struct rb_node		hash_node;

	struct list_head	sync_create_entry;

	u64			ino;

	u32			hash;
	u32			mode;
	u32			len;

	char			*data;
};

/*
 * POHMELFS inode. Main object.
 */
struct pohmelfs_inode {
	struct list_head	inode_entry;		/* Entry in superblock list.
							 * Objects which are not bound to dentry require to be dropped
							 * in ->put_super()
							 */
	struct rb_root		hash_root;		/* The same, but indexed by name hash and len */
	struct mutex		offset_lock;		/* Protect both above trees */

	struct list_head	sync_create_list;	/* List of created but not yet synced to the server children */

	unsigned int		drop_count;

	int			lock_type;		/* How this inode is locked: read or write */

	int			error;			/* Transaction error for given inode */

	long			state;			/* State machine above */

	u64			ino;			/* Inode number */
	u64			total_len;		/* Total length of all children names, used to create offsets */

	struct inode		vfs_inode;
};

struct netfs_trans;
typedef int (*netfs_trans_complete_t)(struct page **pages, unsigned int page_num,
		void *private, int err);

struct netfs_state;
struct pohmelfs_sb;

struct netfs_trans {
	/*
	 * Transaction header and attached contiguous data live here.
	 */
	struct iovec			iovec;

	/*
	 * Pages attached to transaction.
	 */
	struct page			**pages;

	/*
	 * List and protecting lock for transaction destination
	 * network states.
	 */
	spinlock_t			dst_lock;
	struct list_head		dst_list;

	/*
	 * Number of users for given transaction.
	 * For example each network state attached to transaction
	 * via dst_list increases it.
	 */
	atomic_t			refcnt;

	/*
	 * Number of pages attached to given transaction.
	 * Some slots in above page array can be NULL, since
	 * for example page can be under writeback already,
	 * so we skip it in this transaction.
	 */
	unsigned int			page_num;

	/*
	 * Transaction flags: single dst or broadcast and so on.
	 */
	unsigned int			flags;

	/*
	 * Size of the data, which can be placed into
	 * iovec.iov_base area.
	 */
	unsigned int			total_size;

	/*
	 * Number of pages to be sent to remote server.
	 * Usually equal to above page_num, but in case of partial
	 * writeback it can accumulate only pages already completed
	 * previous writeback.
	 */
	unsigned int			attached_pages;

	/*
	 * Attached number of bytes in all above pages.
	 */
	unsigned int			attached_size;

	/*
	 * Unique transacton generation number.
	 * Used as identity in the network state tree of transactions.
	 */
	unsigned int			gen;

	/*
	 * Transaction completion status.
	 */
	int				result;

	/*
	 * Superblock this transaction belongs to
	 */
	struct pohmelfs_sb		*psb;

	/*
	 * Crypto engine, which processed this transaction.
	 * Can be not NULL only if crypto engine holds encrypted pages.
	 */
	struct pohmelfs_crypto_engine	*eng;

	/* Private data */
	void				*private;

	/* Completion callback, invoked just before transaction is destroyed */
	netfs_trans_complete_t		complete;
};

static inline int netfs_trans_cur_len(struct netfs_trans *t)
{
	return (signed)(t->total_size - t->iovec.iov_len);
}

static inline void *netfs_trans_current(struct netfs_trans *t)
{
	return t->iovec.iov_base + t->iovec.iov_len;
}

struct netfs_trans *netfs_trans_alloc(struct pohmelfs_sb *psb, unsigned int size,
		unsigned int flags, unsigned int nr);
void netfs_trans_free(struct netfs_trans *t);
int netfs_trans_finish(struct netfs_trans *t, struct pohmelfs_sb *psb);
int netfs_trans_finish_send(struct netfs_trans *t, struct pohmelfs_sb *psb);

static inline void netfs_trans_reset(struct netfs_trans *t)
{
	t->complete = NULL;
}

struct netfs_trans_dst {
	struct list_head		trans_entry;
	struct rb_node			state_entry;

	unsigned long			send_time;

	/*
	 * Times this transaction was resent to its old or new,
	 * depending on flags, destinations. When it reaches maximum
	 * allowed number, specified in superblock->trans_retries,
	 * transaction will be freed with ETIMEDOUT error.
	 */
	unsigned int			retries;

	struct netfs_trans		*trans;
	struct netfs_state		*state;
};

struct netfs_trans_dst *netfs_trans_search(struct netfs_state *st, unsigned int gen);
void netfs_trans_drop_dst(struct netfs_trans_dst *dst);
void netfs_trans_drop_dst_nostate(struct netfs_trans_dst *dst);
void netfs_trans_drop_trans(struct netfs_trans *t, struct netfs_state *st);
void netfs_trans_drop_last(struct netfs_trans *t, struct netfs_state *st);
int netfs_trans_resend(struct netfs_trans *t, struct pohmelfs_sb *psb);
int netfs_trans_remove_nolock(struct netfs_trans_dst *dst, struct netfs_state *st);

int netfs_trans_init(void);
void netfs_trans_exit(void);

struct pohmelfs_crypto_engine {
	u64				iv;		/* Crypto IV for current operation */
	unsigned long			timeout;	/* Crypto waiting timeout */
	unsigned int			size;		/* Size of crypto scratchpad */
	void				*data;		/* Temporal crypto scratchpad */
	/*
	 * Crypto operations performed on objects.
	 */
	struct crypto_hash		*hash;
	struct crypto_ablkcipher	*cipher;

	struct pohmelfs_crypto_thread	*thread;	/* Crypto thread which hosts this engine */

	struct page			**pages;
	unsigned int			page_num;
};

struct pohmelfs_crypto_thread {
	struct list_head		thread_entry;

	struct task_struct		*thread;
	struct pohmelfs_sb		*psb;

	struct pohmelfs_crypto_engine	eng;

	struct netfs_trans		*trans;

	wait_queue_head_t		wait;
	int				error;

	unsigned int			size;
	struct page			*page;
};

void pohmelfs_crypto_thread_make_ready(struct pohmelfs_crypto_thread *th);

/*
 * Network state, attached to one server.
 */
struct netfs_state {
	struct mutex		__state_lock;		/* Can not allow to use the same socket simultaneously */
	struct mutex		__state_send_lock;
	struct netfs_cmd	cmd;			/* Cached command */
	struct netfs_inode_info	info;			/* Cached inode info */

	void			*data;			/* Cached some data */
	unsigned int		size;			/* Size of that data */

	struct pohmelfs_sb	*psb;			/* Superblock */

	struct task_struct	*thread;		/* Async receiving thread */

	/* Waiting/polling machinery */
	wait_queue_t		wait;
	wait_queue_head_t	*whead;
	wait_queue_head_t	thread_wait;

	struct mutex		trans_lock;
	struct rb_root		trans_root;

	struct pohmelfs_ctl	ctl;			/* Remote peer */

	struct socket		*socket;		/* Socket object */
	struct socket		*read_socket;		/* Cached pointer to socket object.
							 * Used to determine if between lock drops socket was changed.
							 * Never used to read data or any kind of access.
							 */
	/*
	 * Crypto engines to process incoming data.
	 */
	struct pohmelfs_crypto_engine	eng;

	int			need_reset;
};

int netfs_state_init(struct netfs_state *st);
void netfs_state_exit(struct netfs_state *st);

static inline void netfs_state_lock_send(struct netfs_state *st)
{
	mutex_lock(&st->__state_send_lock);
}

static inline int netfs_state_trylock_send(struct netfs_state *st)
{
	return mutex_trylock(&st->__state_send_lock);
}

static inline void netfs_state_unlock_send(struct netfs_state *st)
{
	BUG_ON(!mutex_is_locked(&st->__state_send_lock));

	mutex_unlock(&st->__state_send_lock);
}

static inline void netfs_state_lock(struct netfs_state *st)
{
	mutex_lock(&st->__state_lock);
}

static inline void netfs_state_unlock(struct netfs_state *st)
{
	BUG_ON(!mutex_is_locked(&st->__state_lock));

	mutex_unlock(&st->__state_lock);
}

static inline unsigned int netfs_state_poll(struct netfs_state *st)
{
	unsigned int revents = POLLHUP | POLLERR;

	netfs_state_lock(st);
	if (st->socket)
		revents = st->socket->ops->poll(NULL, st->socket, NULL);
	netfs_state_unlock(st);

	return revents;
}

struct pohmelfs_config;

struct pohmelfs_sb {
	struct rb_root		mcache_root;
	struct mutex		mcache_lock;
	atomic_long_t		mcache_gen;
	unsigned long		mcache_timeout;

	unsigned int		idx;

	unsigned int		trans_retries;

	atomic_t		trans_gen;

	unsigned int		crypto_attached_size;
	unsigned int		crypto_align_size;

	unsigned int		crypto_fail_unsupported;

	unsigned int		crypto_thread_num;
	struct list_head	crypto_active_list, crypto_ready_list;
	struct mutex		crypto_thread_lock;

	unsigned int		trans_max_pages;
	unsigned long		trans_data_size;
	unsigned long		trans_timeout;

	unsigned long		drop_scan_timeout;
	unsigned long		trans_scan_timeout;

	unsigned long		wait_on_page_timeout;

	struct list_head	flush_list;
	struct list_head	drop_list;
	spinlock_t		ino_lock;
	u64			ino;

	/*
	 * Remote nodes POHMELFS connected to.
	 */
	struct list_head	state_list;
	struct mutex		state_lock;

	/*
	 * Currently active state to request data from.
	 */
	struct pohmelfs_config	*active_state;


	wait_queue_head_t	wait;

	/*
	 * Timed checks: stale transactions, inodes to be freed and so on.
	 */
	struct delayed_work	dwork;
	struct delayed_work	drop_dwork;

	struct super_block	*sb;

	struct backing_dev_info	bdi;

	/*
	 * Algorithm strings.
	 */
	char			*hash_string;
	char			*cipher_string;

	u8			*hash_key;
	u8			*cipher_key;

	/*
	 * Algorithm string lengths.
	 */
	unsigned int		hash_strlen;
	unsigned int		cipher_strlen;
	unsigned int		hash_keysize;
	unsigned int		cipher_keysize;

	/*
	 * Controls whether to perfrom crypto processing or not.
	 */
	int			perform_crypto;

	/*
	 * POHMELFS statistics.
	 */
	u64			total_size;
	u64			avail_size;
	atomic_long_t		total_inodes;

	/*
	 * Xattr support, read-only and so on.
	 */
	u64			state_flags;

	/*
	 * Temporary storage to detect changes in the wait queue.
	 */
	long			flags;
};

static inline void netfs_trans_update(struct netfs_cmd *cmd,
		struct netfs_trans *t, unsigned int size)
{
	unsigned int sz = ALIGN(size, t->psb->crypto_align_size);

	t->iovec.iov_len += sizeof(struct netfs_cmd) + sz;
	cmd->cpad = __cpu_to_be16(sz - size);
}

static inline struct pohmelfs_sb *POHMELFS_SB(struct super_block *sb)
{
	return sb->s_fs_info;
}

static inline struct pohmelfs_inode *POHMELFS_I(struct inode *inode)
{
	return container_of(inode, struct pohmelfs_inode, vfs_inode);
}

static inline u64 pohmelfs_new_ino(struct pohmelfs_sb *psb)
{
	u64 ino;

	spin_lock(&psb->ino_lock);
	ino = psb->ino++;
	spin_unlock(&psb->ino_lock);

	return ino;
}

static inline void pohmelfs_put_inode(struct pohmelfs_inode *pi)
{
	struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb);

	spin_lock(&psb->ino_lock);
	list_move_tail(&pi->inode_entry, &psb->drop_list);
	pi->drop_count++;
	spin_unlock(&psb->ino_lock);
}

struct pohmelfs_config {
	struct list_head	config_entry;

	struct netfs_state	state;
};

struct pohmelfs_config_group {
	/*
	 * Entry in the global config group list.
	 */
	struct list_head	group_entry;

	/*
	 * Index of the current group.
	 */
	unsigned int		idx;
	/*
	 * Number of config_list entries in this group entry.
	 */
	unsigned int		num_entry;
	/*
	 * Algorithm strings.
	 */
	char			*hash_string;
	char			*cipher_string;

	/*
	 * Algorithm string lengths.
	 */
	unsigned int		hash_strlen;
	unsigned int		cipher_strlen;

	/*
	 * Key and its size.
	 */
	unsigned int		hash_keysize;
	unsigned int		cipher_keysize;
	u8			*hash_key;
	u8			*cipher_key;

	/*
	 * List of config entries (network state info) for given idx.
	 */
	struct list_head	config_list;
};

int __init pohmelfs_config_init(void);
void pohmelfs_config_exit(void);
int pohmelfs_copy_config(struct pohmelfs_sb *psb);
int pohmelfs_copy_crypto(struct pohmelfs_sb *psb);
int pohmelfs_config_check(struct pohmelfs_config *config, int idx);
int pohmelfs_state_init_one(struct pohmelfs_sb *psb, struct pohmelfs_config *conf);

extern const struct file_operations pohmelfs_dir_fops;
extern const struct inode_operations pohmelfs_dir_inode_ops;

int pohmelfs_state_init(struct pohmelfs_sb *psb);
void pohmelfs_state_exit(struct pohmelfs_sb *psb);
void pohmelfs_state_flush_transactions(struct netfs_state *st);

void pohmelfs_fill_inode(struct inode *inode, struct netfs_inode_info *info);

void pohmelfs_name_del(struct pohmelfs_inode *parent, struct pohmelfs_name *n);
void pohmelfs_free_names(struct pohmelfs_inode *parent);
struct pohmelfs_name *pohmelfs_search_hash(struct pohmelfs_inode *pi, u32 hash);

void pohmelfs_inode_del_inode(struct pohmelfs_sb *psb, struct pohmelfs_inode *pi);

struct pohmelfs_inode *pohmelfs_create_entry_local(struct pohmelfs_sb *psb,
	struct pohmelfs_inode *parent, struct qstr *str, u64 start, int mode);

int pohmelfs_write_create_inode(struct pohmelfs_inode *pi);

int pohmelfs_write_inode_create(struct inode *inode, struct netfs_trans *trans);
int pohmelfs_remove_child(struct pohmelfs_inode *parent, struct pohmelfs_name *n);

struct pohmelfs_inode *pohmelfs_new_inode(struct pohmelfs_sb *psb,
		struct pohmelfs_inode *parent, struct qstr *str,
		struct netfs_inode_info *info, int link);

int pohmelfs_setattr(struct dentry *dentry, struct iattr *attr);
int pohmelfs_setattr_raw(struct inode *inode, struct iattr *attr);

int pohmelfs_meta_command(struct pohmelfs_inode *pi, unsigned int cmd_op, unsigned int flags,
		netfs_trans_complete_t complete, void *priv, u64 start);
int pohmelfs_meta_command_data(struct pohmelfs_inode *pi, u64 id, unsigned int cmd_op, char *addon,
		unsigned int flags, netfs_trans_complete_t complete, void *priv, u64 start);

void pohmelfs_check_states(struct pohmelfs_sb *psb);
void pohmelfs_switch_active(struct pohmelfs_sb *psb);

int pohmelfs_construct_path_string(struct pohmelfs_inode *pi, void *data, int len);
int pohmelfs_path_length(struct pohmelfs_inode *pi);

struct pohmelfs_crypto_completion {
	struct completion	complete;
	int			error;
};

int pohmelfs_trans_crypt(struct netfs_trans *t, struct pohmelfs_sb *psb);
void pohmelfs_crypto_exit(struct pohmelfs_sb *psb);
int pohmelfs_crypto_init(struct pohmelfs_sb *psb);

int pohmelfs_crypto_engine_init(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb);
void pohmelfs_crypto_engine_exit(struct pohmelfs_crypto_engine *e);

int pohmelfs_crypto_process_input_data(struct pohmelfs_crypto_engine *e, u64 iv,
		void *data, struct page *page, unsigned int size);
int pohmelfs_crypto_process_input_page(struct pohmelfs_crypto_engine *e,
		struct page *page, unsigned int size, u64 iv);

static inline u64 pohmelfs_gen_iv(struct netfs_trans *t)
{
	u64 iv = t->gen;

	iv <<= 32;
	iv |= ((unsigned long)t) & 0xffffffff;

	return iv;
}

int pohmelfs_data_lock(struct pohmelfs_inode *pi, u64 start, u32 size, int type);
int pohmelfs_data_unlock(struct pohmelfs_inode *pi, u64 start, u32 size, int type);
int pohmelfs_data_lock_response(struct netfs_state *st);

static inline int pohmelfs_need_lock(struct pohmelfs_inode *pi, int type)
{
	if (test_bit(NETFS_INODE_OWNED, &pi->state)) {
		if (type == pi->lock_type)
			return 0;
		if ((type == POHMELFS_READ_LOCK) && (pi->lock_type == POHMELFS_WRITE_LOCK))
			return 0;
	}

	if (!test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state))
		return 0;

	return 1;
}

int __init pohmelfs_mcache_init(void);
void pohmelfs_mcache_exit(void);

/* #define CONFIG_POHMELFS_DEBUG */

#ifdef CONFIG_POHMELFS_DEBUG
#define dprintka(f, a...) printk(f, ##a)
#define dprintk(f, a...) printk("%d: " f, task_pid_vnr(current), ##a)
#else
#define dprintka(f, a...) do {} while (0)
#define dprintk(f, a...) do {} while (0)
#endif

static inline void netfs_trans_get(struct netfs_trans *t)
{
	atomic_inc(&t->refcnt);
}

static inline void netfs_trans_put(struct netfs_trans *t)
{
	if (atomic_dec_and_test(&t->refcnt)) {
		dprintk("%s: t: %p, gen: %u, err: %d.\n",
			__func__, t, t->gen, t->result);
		if (t->complete)
			t->complete(t->pages, t->page_num,
				t->private, t->result);
		netfs_trans_free(t);
	}
}

struct pohmelfs_mcache {
	struct rb_node			mcache_entry;
	struct completion		complete;

	atomic_t			refcnt;

	u64				gen;

	void				*data;
	u64				start;
	u32				size;
	int				err;

	struct netfs_inode_info		info;
};

struct pohmelfs_mcache *pohmelfs_mcache_alloc(struct pohmelfs_sb *psb, u64 start,
		unsigned int size, void *data);
void pohmelfs_mcache_free(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m);
struct pohmelfs_mcache *pohmelfs_mcache_search(struct pohmelfs_sb *psb, u64 gen);
void pohmelfs_mcache_remove_locked(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m);

static inline void pohmelfs_mcache_get(struct pohmelfs_mcache *m)
{
	atomic_inc(&m->refcnt);
}

static inline void pohmelfs_mcache_put(struct pohmelfs_sb *psb,
		struct pohmelfs_mcache *m)
{
	if (atomic_dec_and_test(&m->refcnt))
		pohmelfs_mcache_free(psb, m);
}

/*#define POHMELFS_TRUNCATE_ON_INODE_FLUSH
 */

#endif /* __KERNEL__*/

#endif /* __NETFS_H */
