#ifndef _SPADFS_SPADFSCK_SCK_H
#define _SPADFS_SPADFSCK_SCK_H

#ifndef __NOT_FROM_SPAD_TREE
#include <SYS/TYPES.H>
#include <ARCH/BT.H>
#include <ARCH/BSF.H>
#include <SPAD/LIST.H>
#include <SPAD/LIBC.H>
#include <SPAD/SYNC.H>
#include <SPAD/VM.H>
#include <STDLIB.H>
#include <UNISTD.H>
#include <STRING.H>
#include <TIME.H>
#include <SYS/STAT.H>
#include <ERRNO.H>
#include <FCNTL.H>
#include <STDARG.H>
#else
#define _GNU_SOURCE
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS	64
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include "../SPAD-API.H"
#endif
#include "../STRUCT.H"

#define HASH_SIZE		4096
#define BUFFER_SIZE		131072	/* maximum page size is 131072 */
#define SECTORS_PER_BUFFER	(BUFFER_SIZE >> SSIZE_BITS)
typedef __u64 blk_t;
#define blk_format		__64_format

/* this will enable options that have no practical use --- they can be used just
   to test fsck itself */
#define TESTCODE	/* !!! FIXME */

#ifdef __linux__
#if !defined(__GLIBC__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 1)
#include <asm/unistd.h>
#ifdef __NR__llseek
#define use_llseek
#endif
#endif
#endif

#ifdef use_llseek
#ifdef pread
#undef pread
#endif
#ifdef pwrite
#undef pwrite
#endif
#define pread xpread
#define pwrite xpwrite
ssize_t xpread(int h, void *buf, size_t count, __u64 off);
ssize_t xpwrite(int h, void *buf, size_t count, __u64 off);
typedef __u64 io_t;
#else
typedef off_t io_t;
#endif

typedef struct __recovery RECOVERY;

/* SCK.C */

extern int ro;
extern int y;
extern int force;
extern int modified;
extern int rebuild_apages;
extern int reset_cc;
extern int dont_store_cross_links;
extern char *swapfile;
extern unsigned long memory_limit;
extern char *logfile;
extern int async_writes;
extern __s64 reserve;
#ifdef TESTCODE
extern int recover_all_files;
extern int recover_all_directories;
extern int fragment_recovered_files;
extern int move_recovered_files;
extern int dont_sort_recovered_files;
extern int make_apage_bitmaps;
extern int always_swap;
extern int nodirect;
extern int nocache;
extern int debug_malloc;
#endif
extern char *dev;
extern int h;
extern struct stat st;

/* SCKFN */

void *mem_alloc(size_t s);
void mem_free(void *p);
void mem_done(void);

/* SCKLOG.C */

#define MAX_SAME_ERRORS	5

void __PRINTF_ATTR__(2,3) log_printf(int important, char *msg, ...);
void __PRINTF_ATTR__(2,3) status_printf(int r, char *msg, ...);
#define qprefix __FILE__, __LINE__,
int __PRINTF_ATTR__(4,5) query(char *file, long line, int q, char *msg, ...);
void print_progress(blk_t total, blk_t partial, int exact);
void log_suppress(void);
int init_log(void);
int done_log(void);

/* SCKBUF.C */

void *dummy_buffer(void);
void *read_buffer(blk_t blk, unsigned n);
int write_buffer(blk_t blk, unsigned n, void *ptr);
void *rw_swap_buffer(int hn, blk_t blk, int wr);
extern int buffer_nocache;
extern char buffer_error[__MAX_STR_LEN];
void init_buffers(void);
void done_buffers(void);

/* SCKALLOC.C */

extern char alloc_error[__MAX_STR_LEN];
extern int alloc_over_blocks;
extern blk_t newalloc;
extern int swap_allocmap;
extern unsigned long map_allocated;
extern blk_t blocks_allocated;
extern blk_t total_allocated;
void init_alloc(void);
int init_swapfile(void);
void enable_swapping(void);
int check_allocation(blk_t b, blk_t l);
int alloc_blocks(blk_t b, blk_t l);
int free_blocks(blk_t b, blk_t l);
int get_blockrun(blk_t b, int alloced, blk_t *e);
int force_alloc_blocks(blk_t b, blk_t l);
int alloc_space(int pri, char *message, blk_t n_sec, int mask, blk_t *result);
int alloc_try(blk_t b, blk_t n_sec);
int reset_allocations(void);
void done_alloc(void);

/* SCKXL.C */

extern int cross_link_pass;
extern int were_cross_links;
void init_cross_links(void);
void done_cross_links(void);
int check_xlink_overlap(blk_t b, blk_t l, blk_t *b_e);
int sys_alloc(blk_t blk, blk_t l, int pass, char *name, blk_t *off, char *(*get_filename)(void *p), void *p);
int sys_free(blk_t blk, blk_t l, int pass);

/* SCKSUPER.C */

extern __u64 super_ptr[64];
#define super (*(struct superblock *)super_ptr)
extern __u64 tx_ptr[64];
#define tx (*(struct txblock *)tx_ptr)
extern int tx_valid;
extern unsigned block_mask;
extern unsigned dnode_hash_bits;
extern unsigned dnode_data_size;
extern unsigned dnode_page_sectors;

void init_super(void);
int check_super(void);
int recover_super_allocs(void);
int fix_txblock(void);
int fix_errorflag(void);
void write_super(void);
void write_txblock(void);
void done_super(void);

/* SCKCCT.C */

extern int reset_cc;
extern __s32 cct[65536];
int check_cct(void);
int recover_cct_allocs(void);
int fix_cct(void);
int cc_valid(__u16 cc, __s32 txc);
int cc_check(__u16 *cc, __s32 *txc, char *str1, char *(*get_filename)(void *p), void *p, blk_t blk);

/* SCKAPAGE.C */
 
extern unsigned n_apages;
extern blk_t *swapspace;
extern int allow_swapping;
void init_apages(void);
int check_apages(void);
int recover_apage_allocs(void);
int fix_apages(void);
void done_apages(void);

/* SCKFBLK.C */

typedef struct {
	LIST_ENTRY list;
	blk_t start;
	unsigned len;
	int dno;
} FNODE_DESC;

int get_fnode_description(blk_t blk, LIST_HEAD *l, char *(*get_filename)(void *p), void *p);
void free_fnode_description(LIST_HEAD *l);
int dealloc_and_free_fnode_description(LIST_HEAD *l);

/* SCKFXFN.C */

struct fixed_fnode_block *update_fixed_fnode_block(struct fixed_fnode_block *fx, blk_t blk);
struct fixed_fnode_block *reread_fixed_fnode_block(blk_t blk);
struct fnode *fixed_fnode_block_fnode(struct fixed_fnode_block *fx, int complementary);
int read_fixed_fnode(blk_t blk, int directory, struct fixed_fnode_block **result_fx, struct fnode **result_fnode, char *(*get_filename)(void *p), void *p);

/* SCKHDLNK.C */

int hardlink_link(blk_t blk);
void hardlink_unlink(blk_t blk);
void hardlink_delete(blk_t blk);
void hardlink_set_disk_nlink(blk_t blk, blk_t disk_nlink);
void hardlink_set_recovery(blk_t blk, RECOVERY *rec);
RECOVERY *hardlink_get_recovery(blk_t blk);
int hardlink_search_for_bad_nlink(blk_t *fx_blk, blk_t *real_nlink);
void hardlink_init(void);
void hardlink_done(void);

/* SCKDIR.C */

extern blk_t root_dir;
int check_dir_tree(void);
int claim_recovered_directory(char *path, char *filename, blk_t dnode, void **result);
int undo_recovered_directory(void *result);
void done_filename(void);

/* SCKFILE.C */

typedef struct {
	LIST_ENTRY list;
	blk_t lbn;
	blk_t start;
	blk_t len;
	blk_t ano;
} ALLOC;

int check_file(struct fnode *fn, LIST_HEAD *allocs, char *(*get_filename)(void *p), void *p);
int force_block_alloc_file_runs(LIST_HEAD *l);
int dealloc_blocklist(int r, LIST_HEAD *list, int pass);
int dealloc_and_free_blocklist(int r, LIST_HEAD *list);
void free_file_runs(LIST_HEAD *l);

/* SCKEA.C */

#define EA_TYPE_NAME		1
#define EA_TYPE_FILE		2
#define EA_TYPE_FILE_NAME	3

char *check_ea(struct fnode *fnode, struct fnode_ea *ea, unsigned size, int type);
void fix_ea(struct fnode *fnode, struct fnode_ea *ea, unsigned *size, int type);

/* SCKRCV.C */

typedef struct {
	__u64 size;
	__u32 ctime;
	__u32 mtime;
	__u8 flags;
	blk_t dnode;
	LIST_HEAD runs;
	unsigned ea_size;
	char ea[FNODE_MAX_EA_SIZE];
} RECOVERY_FILEDESC;

void init_recovery(void);
int recover_file(RECOVERY_FILEDESC *rfd, char *file_ea, unsigned file_ea_size, char *path, char *filename, char *name_ea, unsigned name_ea_size, RECOVERY **output);
int add_recovery_hardlink(RECOVERY *rec, char *path, char *filename, char *name_ea, unsigned name_ea_size);
int do_directories_recovery(void);
int do_files_recovery(void);
void done_recovery(void);

/* SCKCRT.C */

int add_fnode_to_dir(char *path, char *filename, struct fnode *fn, struct fnode_ea *ea, unsigned ea_size);
int create_fixed_fnode(struct fnode *new_fnode, struct fnode_ea *ea, unsigned ea_size, blk_t *result);

#endif
