/*
 * 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, INVT_DOLOCK)

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

#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, INVT_DOLOCK)

#define PUT_REC_NOLOCK(fd, buf, sz, off)  \
                 put_invtrecord(fd, buf, sz, off, 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, bool_t dolock);

int
put_invtrecord(int fd, void *buf, size_t bufsz, off64_t off, 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
