/*
 * Copyright (c) 2000-2001 Silicon Graphics, Inc.
 * 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.
 *
 * This program is distributed in the hope that it would 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software Foundation,
 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
#ifndef _INV_PRIV_H_
#define _INV_PRIV_H_

#include <sys/file.h> /* for flock */
#include "global.h"
#include "inventory.h"
#include "mlog.h"


/*----------------------------------------------------------------------*/
/* The Inventory stores its data in a hierarchical manner.              */
/*                                                                      */
/* At the first level is the Fstab, which maintains a table of unique   */
/* file system ids for all file systems that it knows of. From this fsid*/
/* we can derive the Inventory Index for that file system.              */
/*                                                                      */
/* Inventory Index contains references to all the Storage Objects that  */
/* carry data associated with (its) file system. The Storage Objects,   */
/* the third level (of indirection) are reserved for certain (variable) */
/* time periods, and the inv-index keeps track of those timeperiods     */
/* in its table. In other words, for a given time, there is a unique    */
/* stobj that exists to contain a session that occured at that time..   */
/*                                                                      */
/*----------------------------------------------------------------------*/

#define INV_LOCKFILE		inv_lockfile()
#define INVTSESS_COOKIE		"idbsess0"
#define INVT_STOBJ_MAXSESSIONS	5
#define INVT_MAX_INVINDICES	-1	/* unlimited */
#define FSTAB_UPDATED		1
#define NEW_INVINDEX		2

/* session flags ( seshdr.sh_flag ) */
#define INVT_PARTIAL		(uint)0x0001
#define INVT_RESUMED            (uint)0x0002

/* media file flags ( mfile.mf_flag ) */
#define INVT_MFILE_GOOD         (u_char)0x01
#define INVT_MFILE_INVDUMP      (u_char)0x02

#define INVT_ENDTIME		BOOL_FALSE
#define INVT_STARTTIME		BOOL_TRUE
#define INVT_DOLOCK		BOOL_TRUE
#define INVT_DONTLOCK		BOOL_FALSE

#define INVLOCK( fd, m )	flock( fd, m ) 

/* return values */
#define INV_OK			(int) 1
#define INV_ERR			(int) -1

#define I_DONE                  (int) -1
#define I_EMPTYINV              (int) -2

/* flags to oref_resolve_new_stobj() */
#define IS_EMPTY		BOOL_TRUE
#define IS_NOT_EMPTY		BOOL_FALSE

#define INV_PERMS               S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH

#define IS_WITHIN(ttpe, tm)	( tm >= (ttpe)->tp_start && \
				  tm <= (ttpe)->tp_end )
#define IDX_HDR_OFFSET(n) 	(off64_t) ( sizeof( invt_entry_t ) * \
					   (size_t) (n)\
					   + sizeof( invt_counter_t ) )
#define STOBJ_OFFSET( nhdrs, nsess ) (off64_t) ( \
			       sizeof( invt_sescounter_t ) + \
			       (size_t) nhdrs * sizeof( invt_seshdr_t ) + \
			       (size_t) nsess * sizeof( invt_session_t ) )

#define STREQL( n,m )		( strcmp((n),(m)) == 0 )
#define UUID_EQL( n,m,t )	( uuid_compare( n, m, t ) == 0 )
#define IS_PARTIAL_SESSION( h ) ( (h)->sh_flag & INVT_PARTIAL )
#define IS_RESUMED_SESSION( h ) ( (h)->sh_flag & INVT_RESUMED )
#define SC_EOF_INITIAL_POS	(off64_t) (sizeof( invt_sescounter_t ) + \
					 INVT_STOBJ_MAXSESSIONS * \
					 ( sizeof( invt_seshdr_t ) + \
					   sizeof( invt_session_t ) ) )

#define INV_PERROR(s)  	mlog( MLOG_NORMAL,"INV: %s %s\n", s,strerror(errno) )
#define INV_OFLAG(f)    ( f == INV_SEARCH_ONLY ) ? O_RDONLY: O_RDWR

/*----------------------------------------------------------------------*/
/* On Disk Data Structures                                              */
/*                                                                      */
/*----------------------------------------------------------------------*/

typedef struct invt_session {
	uuid_t 		 s_sesid;	/* this session's id: 16 bytes*/
	uuid_t		 s_fsid;	/* file system id */
	char		 s_label[INV_STRLEN];  /* session label */
	char		 s_mountpt[INV_STRLEN];/* path to the mount point */
	char		 s_devpath[INV_STRLEN];/* path to the device */
	uint		 s_cur_nstreams;/* number of streams created under
					   this session so far */
	uint		 s_max_nstreams;/* number of media streams in 
					   the session */
	char		 s_padding[16];
} invt_session_t;			   
 
typedef struct invt_seshdr {
	off64_t		sh_sess_off;    /* offset to rest of the sessioninfo */
	off64_t		sh_streams_off; /* offset to start of the set of 
					   stream hdrs */
	time32_t	sh_time;        /* time of the dump */
	__uint32_t	sh_flag;        /* for misc flags */
	u_char		sh_level;       /* dump level */
	u_char		sh_pruned;      /* pruned by invutil flag */
	char		sh_padding[22];
} invt_seshdr_t;


/* Each session consists of a number of media streams. While it is given that
   there won't be multiple writesessions (ie. dumpsessions) concurrently,
   there can be multiple media streams operating on a single file system, 
   each writing media files within its own stream. Hence, we have a linked
   list of media files, that the stream keeps track of. */


typedef struct invt_breakpt {
	xfs_ino_t	ino;		/* the 64bit inumber */
	off64_t		offset;		/* offset into the file */
} invt_breakpt_t;

typedef struct invt_stream {
	/* duplicate info from mediafiles for speed */
	invt_breakpt_t	st_startino;	/* the starting pt */
	invt_breakpt_t	st_endino;	/* where we actually ended up. this
					   means we've written upto but not
					   including this breakpoint. */
	off64_t		st_firstmfile;	/*offsets to the start and end of*/
	off64_t		st_lastmfile;	/* .. linked list of mediafiles */
	char            st_cmdarg[INV_STRLEN]; /* drive path */
	uint		st_nmediafiles; /* number of mediafiles */
	bool_t		st_interrupted;	/* was this stream interrupted ? */
	char		st_padding[16];
} invt_stream_t;



typedef struct invt_mediafile {
	uuid_t		 mf_moid;	/* media object id */
	char		 mf_label[INV_STRLEN];	/* media file label */
	invt_breakpt_t	 mf_startino;	/* file that we started out with */
	invt_breakpt_t	 mf_endino;	/* the dump file we ended this 
					   media file with */
	off64_t		 mf_nextmf;	/* links to other mfiles */
	off64_t		 mf_prevmf;
	uint		 mf_mfileidx; 	/* index within the media object */
	u_char           mf_flag;       /* Currently MFILE_GOOD, INVDUMP */
	off64_t		 mf_size;       /* size of the media file */
	char		 mf_padding[15];
} invt_mediafile_t;



typedef struct invt_timeperiod {
	time32_t	tp_start;
	time32_t	tp_end;
} invt_timeperiod_t;

typedef struct invt_entry {
	invt_timeperiod_t 	ie_timeperiod;
	char			ie_filename[INV_STRLEN];
	char   			ie_padding[16];
} invt_entry_t;

/* Cheap Inheritance, and an attempt to avoid a nested type */
#define INVT_COUNTER_FIELDS  \
        __uint32_t    ic_vernum;/* on disk version number for posterity */\
	uint	      ic_curnum;/* number of sessions/invindices recorded \
				   so far */				  \
	uint	      ic_maxnum;/* maximum number of sessions/inv_indices \
				   that we can record on this stobj */
#define INVT_COUNTER_FIELDS_SIZE 0xc

typedef struct invt_counter {
	INVT_COUNTER_FIELDS
	char	      ic_padding[0x20 - INVT_COUNTER_FIELDS_SIZE];
} invt_counter_t;

typedef struct invt_sescounter {
	INVT_COUNTER_FIELDS
	off64_t	       ic_eof;   /* current end of the file, where the next
				 media file or stream will be written to */
	char 	       ic_padding[0x20 - ( INVT_COUNTER_FIELDS_SIZE + 
					   sizeof( off64_t) )];
} invt_sescounter_t;


typedef struct invt_fstab {
	uuid_t	ft_uuid;
	char	ft_mountpt[INV_STRLEN];
	char	ft_devpath[INV_STRLEN];
	char 	ft_padding[16];
} invt_fstab_t;



/*----------------------------------------------------------------------*/
/* The Tokens                                                           */
/*                                                                      */
/*----------------------------------------------------------------------*/
typedef struct invt_desc_entry {
	int	d_invindex_fd;	/* open file descriptor of inv index */
	int	d_stobj_fd;	/* fd of storage object */
	u_char	d_update_flag;  /* indicates whether fstab was updated with
				   this file system or not and also if
				   we had to create a new invindex file */
	inv_oflag_t d_oflag;    /* the open mode (SEARCH_ONLY, SEARCH_N_MOD) */
	off64_t	d_invindex_off; /* for every session, we need a reference 
				   to its invindex entry, so that when we
				   close a session, we know which one */
} invt_desc_entry_t;


typedef struct invt_sesdesc_entry {
	invt_desc_entry_t *sd_invtok;	/* generic inventory token */
	off64_t		   sd_session_off;
	off64_t		   sd_sesshdr_off;
	time32_t	   sd_sesstime;	/* time that session started. 
					   needed for closing the session */
} invt_sesdesc_entry_t;
	
struct invt_mediafile;

typedef struct invt_strdesc_entry {
	invt_sesdesc_entry_t  *md_sesstok;   /* the session token */
	off64_t		       md_stream_off;/* offset to the media stream 
						header */	
	struct invt_mediafile *md_lastmfile; /* just so that we dont have
						to get it back from disk
						when we add the next mfile
						to the linked list */

} invt_strdesc_entry_t;
	



/*----------------------------------------------------------------------*/
/* Info Structures - simple logical containers for sessions and indices */
/*                                                                      */
/*----------------------------------------------------------------------*/

typedef struct invt_sessinfo {
	int 		  stobjfd;
	invt_seshdr_t    *seshdr; 
	invt_session_t   *ses;
	invt_stream_t    *strms;
	invt_mediafile_t *mfiles;
} invt_sessinfo_t;

typedef struct invt_idxinfo {
	int 		invfd;
	invt_counter_t	*icnt;
	invt_entry_t	*iarr;
	uint		index;
}invt_idxinfo_t;

#define INVT_MOID	1
#define INVT_LABEL	2
#define INVT_NULLTYPE	-1
typedef struct invt_mobjinfo {
	int 		type;
	void		*value;
} invt_mobjinfo_t;


typedef struct invt_pr_ctx {
	int 			index;
	int			depth;
	bool_t			fstab;
	bool_t			invidx;
	bool_t			invcheck;
	invt_mobjinfo_t		mobj;
	u_char			level;
} invt_pr_ctx_t;

typedef bool_t (*search_callback_t) (int, invt_seshdr_t *, void *, void *);


#define GET_REC( fd, buf, sz, off )  \
                 get_invtrecord( fd, buf, sz, off, SEEK_SET, INVT_DOLOCK )

#define GET_REC_NOLOCK( fd, buf, sz, off )  \
                 get_invtrecord( fd, buf, sz, off, SEEK_SET, INVT_DONTLOCK )

#define GET_REC_SEEKCUR( fd, buf, sz, off )  \
                 get_invtrecord( fd, buf, sz, off, SEEK_CUR, INVT_DOLOCK )

#define GET_ALLHDRS_N_CNTS( fd, h, c, hsz, csz ) \
                 get_headerinfo( fd, h, c, hsz, csz, INVT_DOLOCK )

#define GET_ALLHDRS_N_CNTS_NOLOCK( fd, h, c, hsz, csz ) \
                 get_headerinfo( fd, h, c, hsz, csz, INVT_DONTLOCK )

#define PUT_REC( fd, buf, sz, off )  \
                 put_invtrecord( fd, buf, sz, off, SEEK_SET, INVT_DOLOCK )

#define PUT_REC_NOLOCK( fd, buf, sz, off )  \
                 put_invtrecord( fd, buf, sz, off, SEEK_SET, INVT_DONTLOCK )

#define PUT_REC_SEEKCUR( fd, buf, sz, off )  \
                 put_invtrecord( fd, buf, sz, off, SEEK_CUR, INVT_DOLOCK )

#define PUT_REC_NOLOCK_SEEKCUR( fd, buf, sz, off )  \
                 put_invtrecord( fd, buf, sz, off, SEEK_CUR, INVT_DONTLOCK )


#define GET_COUNTERS( fd, cnt ) get_counters( fd, (void **)(cnt), \
					      sizeof(invt_counter_t) )
#define GET_SESCOUNTERS( fd, cnt ) get_counters( fd, (void **)(cnt), \
						sizeof(invt_sescounter_t) )

#define PUT_COUNTERS( fd, cnt ) PUT_REC_NOLOCK( fd, (void *)(cnt), \
					     sizeof( invt_counter_t ), \
					     (off64_t) 0 )

#define PUT_SESCOUNTERS( fd, cnt )  PUT_REC_NOLOCK( fd, (void *)(cnt), \
					     sizeof( invt_sescounter_t ), \
					     (off64_t) 0 )

#define GET_SESHEADERS( fd, hdrs, n ) get_headers( fd, (void**)(hdrs), \
				   (size_t) ( n * sizeof(invt_seshdr_t ) ),\
				          sizeof( invt_sescounter_t )  )
						   
#define GET_ENTRIES(fd, hdrs, n, sz) get_headers(fd, (void**)(hdrs), \
				   (size_t) (n * sz), sizeof(invt_counter_t))

/*----------------------------------------------------------------------*/
/* Function Prototypes							*/
/*                                                                      */
/*                                                                      */
/*----------------------------------------------------------------------*/


/*----------------------------------------------------------------------*/
inv_idbtoken_t
idx_create( char *fname, inv_oflag_t forwhat );

int
idx_create_entry( inv_idbtoken_t *tok, int invfd, bool_t firstentry );

int
idx_put_sesstime( inv_sestoken_t tok, bool_t whichtime);


int
idx_find_stobj( invt_idxinfo_t *idx, time32_t tm );

uint
idx_insert_newentry( int fd, int *stobjfd, invt_entry_t *iarr, 
		     invt_counter_t *icnt,
		     time32_t tm );
int
idx_put_newentry( invt_idxinfo_t *idx, invt_entry_t *ient );

int
idx_get_stobj( int invfd, inv_oflag_t forwhat, int *index );

int
idx_recons_time( time32_t tm, invt_idxinfo_t *idx );

int
idx_DEBUG_printinvindices( invt_entry_t *iarr, uint num );

int
idx_DEBUG_print ( int fd );

/*----------------------------------------------------------------------*/

int
stobj_create( char *fname );

int
stobj_create_session( inv_sestoken_t tok, int fd, invt_sescounter_t *sescnt, 
		      invt_session_t *ses, invt_seshdr_t *hdr );

int
stobj_put_mediafile( inv_stmtoken_t tok, invt_mediafile_t *mf );

off64_t
stobj_put_session( 
	int fd, 
	invt_sescounter_t *sescnt, 
	invt_session_t *ses, 
	invt_seshdr_t *hdr,
	invt_stream_t *strms,
	invt_mediafile_t *mfiles );

int
stobj_put_streams( int fd, invt_seshdr_t *hdr, invt_session_t *ses, 
		   invt_stream_t *strms,
		   invt_mediafile_t *mfiles );

int
stobj_hdrcmp( const void *h1, const void *h2 );

int
stobj_sortheaders( int fd, uint num );

uint
stobj_find_splitpoint( int fd, invt_seshdr_t *harr, uint ns, time32_t tm );

int
stobj_split( invt_idxinfo_t *idx, int fd, invt_sescounter_t *sescnt, 
	     invt_sessinfo_t *newsess );
bool_t
stobj_replace_session( int fd, invt_sescounter_t *sescnt, invt_session_t *ses,
		       invt_seshdr_t *hdr, invt_sessinfo_t *newsess );

int
stobj_delete_mfile( int fd, inv_stream_t *strm, invt_mediafile_t *mf,
		    off64_t  mfileoff );

bool_t
stobj_pack_sessinfo( int fd, invt_session_t *ses, invt_seshdr_t *hdr,
		     void  **bufpp, size_t *bufszp );

bool_t
stobj_unpack_sessinfo(  
        void              *bufp,
        size_t             bufsz,
	invt_sessinfo_t   *s );

bool_t
stobj_delete_sessinfo( int fd, invt_sescounter_t *sescnt,
		       invt_session_t *ses, invt_seshdr_t *hdr );

bool_t
stobj_delete_mobj( int fd, invt_seshdr_t *hdr, void *arg,
		   void **buf );

int
stobj_get_sessinfo ( inv_sestoken_t tok, invt_seshdr_t *hdr, invt_session_t *ses );

void
stobj_makefname( char *fname );

int
stobj_insert_session( invt_idxinfo_t *idx, int fd,
		      invt_sessinfo_t *s );

int
stobj_make_invsess( int fd, inv_session_t **buf, invt_seshdr_t *hdr );

int
stobj_copy_invsess( int fd, invt_seshdr_t *hdr, invt_session_t *ses, 
		    inv_session_t **buf);

void
DEBUG_sessionprint( inv_session_t *ses, uint ref, invt_pr_ctx_t *prctx);

void
DEBUG_sessprint( invt_session_t *ses );

bool_t
stobj_getsession_byuuid(int fd, invt_seshdr_t *hdr, void *sesid, void **buf);

bool_t
stobj_getsession_bylabel(int fd, invt_seshdr_t *hdr, void *label, void **buf);


void
stobj_convert_session(inv_session_t *ises, invt_session_t *ses, 
		      invt_seshdr_t *hdr);

void
stobj_convert_strm(inv_stream_t *expstrm, invt_stream_t *strm);

void
stobj_convert_mfile(inv_mediafile_t *expmf, invt_mediafile_t *mf);

void
stobj_convert_sessinfo(inv_session_t **buf, invt_sessinfo_t *sinfo);

/*----------------------------------------------------------------------*/

int
fstab_get_fname( void *pred, char *fname, inv_predicate_t bywhat, 
		 inv_oflag_t forwhat );

int
fstab_put_entry( uuid_t *fsidp, char *mntpt, char *dev, inv_oflag_t forwhat );

int
fstab_getall( invt_fstab_t **arr, invt_counter_t **cnt, int *numfs, 
	      inv_oflag_t forwhat );

void
fstab_DEBUG_print( invt_fstab_t *arr, int num );


/*----------------------------------------------------------------------*/

int
get_invtentry( char *fname, time32_t tm, invt_entry_t *buf, size_t bufsz );

int
get_invtrecord( int fd, void *buf, size_t bufsz, off64_t off, int, bool_t dolock );

int
put_invtrecord( int fd, void *buf, size_t bufsz, off64_t off, int, bool_t dolock );

inv_idbtoken_t
get_token( int fd, int objfd );

void
destroy_token( inv_idbtoken_t tok );


int
get_headers( int fd, void **hdrs, size_t bufsz, size_t cntsz );

int
get_counters( int fd, void **cntpp, size_t sz );

int
get_sescounters( int fd, invt_sescounter_t **cntpp );

int
get_lastheader( int fd, void **ent, size_t hdrsz,  size_t cntsz );


inv_sestoken_t
get_sesstoken( inv_idbtoken_t tok );

int
get_headerinfo( int fd, void **hdrs, void **cnt,
	        size_t hdrsz, size_t cntsz, bool_t doblock );

bool_t
invmgr_query_all_sessions(uuid_t *fsidp, void *inarg, void **outarg,
			  search_callback_t func);

int
search_invt(uuid_t *fsidp, int invfd, void *arg, void **buf,
	    search_callback_t do_chkcriteria);
int
invmgr_inv_print( int invfd, invt_pr_ctx_t *prctx);

int
invmgr_inv_check( int invfd );

bool_t
tm_level_lessthan( int fd, invt_seshdr_t *hdr, void *arg,
		   void **tm );

bool_t
lastsess_level_lessthan( int fd, invt_seshdr_t *hdr,  void *arg,
			 void **buf );

bool_t
lastsess_level_equalto( int fd, invt_seshdr_t *hdr,  void *arg, void **buf );

int
DEBUG_displayallsessions( int fd, invt_seshdr_t *hdr, uint ref, 
			  invt_pr_ctx_t *prctx);

int
make_invdirectory( inv_oflag_t forwhat );

bool_t
init_idb( void *pred, inv_predicate_t bywhat, inv_oflag_t forwhat, 
	 inv_idbtoken_t *tok );

int
inv_getopt( int argc, char **argv, invt_pr_ctx_t *prctx);

bool_t
insert_session( invt_sessinfo_t *s);

/* To reconstruct a complete inventory from dumped inventories */
extern bool_t
inv_put_sessioninfo( invt_sessinfo_t *s );
	        

#endif
