/*
 * 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
 */

#include <xfs/xfs.h>
#include <xfs/jdm.h>

#include <unistd.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/dir.h>
#include <assert.h>
#include <string.h>

#include "config.h"

#include "types.h"
#include "inv_priv.h"



/*----------------------------------------------------------------------*/
/*  get_counters, get_headers, get_invtrecord, put_invtrecord, ...      */
/*                                                                      */
/*  These implement low level routines that take care of disk I/Os.     */
/*  In most cases, the caller has the option of locking before calling  */
/*  the routine.                                                        */
/*                                                                      */
/*----------------------------------------------------------------------*/

int
get_counters(int fd, void **cntpp, size_t cntsz)
{
	/* object must be locked at least SHARED by caller */
	uint num;
	assert(cntsz >= sizeof(invt_counter_t));

	*cntpp =  calloc(1, cntsz);

	/* find the number of sessions and the max possible */
	if (GET_REC_NOLOCK(fd, (void *) *cntpp, cntsz, (off64_t) 0) < 0) {
		free(*cntpp);
		*cntpp = NULL;
		return -1;
	}

	num = ((invt_counter_t *)(*cntpp))->ic_curnum;

	if (((invt_counter_t *)(*cntpp))->ic_vernum != INV_VERSION) {
		mlog(MLOG_NORMAL | MLOG_INV, _(
		      "INV : Unknown version %d - Expected version %d\n"),
		      (int) ((invt_counter_t *)(*cntpp))->ic_vernum,
		      (int) INV_VERSION);
		assert (((invt_counter_t *)(*cntpp))->ic_vernum ==
			INV_VERSION);
	}

	return (int) num;
}





/*----------------------------------------------------------------------*/
/* get_headers                                                          */
/*----------------------------------------------------------------------*/

int
get_headers(int fd, void **hdrs, size_t bufsz, size_t off)
{

	*hdrs = malloc(bufsz);
	if (*hdrs == NULL) {
		INV_PERROR(_("get_headers() - malloc(seshdrs)\n"));
		return -1;
	}
	/* file must be locked at least SHARED by caller */

	/* get the array of hdrs */
	if (GET_REC_NOLOCK(fd, (void *) *hdrs, bufsz, (off64_t)off) < 0) {
		free (*hdrs);
		*hdrs = NULL;
		return -1;
	}

	return 1;
}



/*----------------------------------------------------------------------*/
/* get_invtrecord                                                       */
/*----------------------------------------------------------------------*/

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

	assert (fd >= 0);

	if (dolock)
		INVLOCK(fd, LOCK_SH);

	nread = pread(fd, buf, bufsz, (off_t)off);
	if (nread != (int) bufsz) {
		INV_PERROR(_("Error in reading inventory record :"));
		if (dolock)
			INVLOCK(fd, LOCK_UN);
		return -1;
	}

	if (dolock)
		INVLOCK(fd, LOCK_UN);

	return nread;
}






/*----------------------------------------------------------------------*/
/* put_invtrecord                                                       */
/*----------------------------------------------------------------------*/

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

	if (dolock)
		INVLOCK(fd, LOCK_EX);

	nwritten = pwrite(fd, buf, bufsz, (off_t)off);
	if (nwritten != (int) bufsz) {
		INV_PERROR(_("Error in writing inventory record :"));
		if (dolock)
			INVLOCK(fd, LOCK_UN);
		return -1;
	}

	if (dolock)
		INVLOCK(fd, LOCK_UN);
	return nwritten;
}







/*----------------------------------------------------------------------*/
/* get_headerinfo                                                       */
/*----------------------------------------------------------------------*/


int
get_headerinfo(int fd, void **hdrs, void **cnt,
	        size_t hdrsz, size_t cntsz, bool_t dolock)
{
	int num;

	/* get a lock on the table for reading */
	if (dolock) INVLOCK(fd, LOCK_SH);

	num = get_counters(fd, cnt, cntsz);

	/* If there are no sessions recorded yet, we're done too */
	if (num > 0) {
		if (get_headers(fd, hdrs, hdrsz * (size_t)num, cntsz) < 0) {
			free (*cnt);
			num = -1;
		}
	}

	if (dolock) INVLOCK(fd, LOCK_UN);
	return num;
}



/*----------------------------------------------------------------------*/
/* get_lastheader                                                       */
/*----------------------------------------------------------------------*/

int
get_lastheader(int fd, void **ent, size_t hdrsz, size_t cntsz)
{
	int	     	 nindices;
	void	 	 *arr = NULL;
	invt_counter_t	 *cnt = NULL;
	char 		 *pos;
	/* get the entries in the inv_index */
	if ((nindices = GET_ALLHDRS_N_CNTS(fd, &arr, (void **)&cnt,
					 hdrsz, cntsz)) <= 0) {
		return -1;
	}

	/* if there's space anywhere at all, then it must be in the last
	   entry */
	*ent = malloc(hdrsz);
	pos = (char *) arr + ((uint)nindices - 1) * hdrsz;
	memcpy(*ent, pos, hdrsz);
	free (arr);
	free (cnt);

	return nindices;
}


