/*
 * 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 <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/dir.h>
#include <assert.h>
#include <string.h>
#include <uuid/uuid.h>

#include "config.h"

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





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


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

	fd = open (INV_FSTAB, INV_OFLAG(forwhat));

	if (fd < 0)
		return -1; /* if ENOENT, fstab_put_entry will CREAT */

	INVLOCK(fd, LOCK_EX);
	if ((*numfs = GET_ALLHDRS_N_CNTS_NOLOCK(fd, (void**) arr,
						 (void **)cnt,
						 sizeof(invt_fstab_t),
						 sizeof(invt_counter_t))
	     ) < 0) {
		mlog(MLOG_NORMAL | MLOG_INV, _(
			"INV: couldn't get fstab headers\n"));
	}
#ifdef INVT_DEBUG
	mlog(MLOG_NITTY | MLOG_INV, "INV: number of filesystems in fstab %d\n",
	      *numfs);
#endif
	/* fstab is left locked EX on exit. The caller takes does
	   the unlocking */
	return fd;
}


/*----------------------------------------------------------------------*/
/*                                                                      */
/*                                                                      */
/* caller takes the responsibility of calling this only when the FSTAB_ */
/* UPDATED flag in the inv_idbtoken is off, and also of updating the */
/* flag upon successful return from put_fstab_entry.                    */
/*----------------------------------------------------------------------*/


int
fstab_put_entry(uuid_t *fsidp, char *mntpt, char *dev, inv_oflag_t forwhat)
{
	int numfs, i, fd;
	invt_counter_t *cnt;
	invt_fstab_t *arr;
	int rval = 1;

	assert(forwhat != INV_SEARCH_ONLY);

	/* fd is locked on succesful return */
	fd = fstab_getall(&arr, &cnt, &numfs, forwhat);
	if (fd < 0) {
		if (errno != ENOENT) {
			return -1;
		}
		if ((fd = open(INV_FSTAB, INV_OFLAG(forwhat) | O_CREAT, S_IRUSR|S_IWUSR))
		    < 0) {
			INV_PERROR (INV_FSTAB);
			return -1;
		}

		INVLOCK(fd, LOCK_EX);
		fchmod(fd, INV_PERMS);

		cnt = (invt_counter_t *) malloc(sizeof (invt_counter_t));

		cnt->ic_maxnum = -1;
		cnt->ic_curnum = 0;
		cnt->ic_vernum = INV_VERSION;

	} else if (numfs > 0) {

		for (i = 0; i < numfs; i++) {
		    if (uuid_compare(*fsidp, arr[i].ft_uuid) == 0) {

/*			if ((STREQL(arr[i].ft_mountpt, mntpt)) &&
			    (STREQL(arr[i].ft_devpath, dev)))
*/
				free (arr);
				free (cnt);
				close(fd);
				return 1;

		}
	    }
	    /* entry not found. just follow thru to create a new one */
	    free (arr);
	}

	/* make a new fstab entry and insert it at the end. the table
	   is locked EXclusively at this point */
	{
		invt_fstab_t ent;
		off64_t hoff;

		memcpy(&ent.ft_uuid, fsidp, sizeof(uuid_t));
		strcpy(ent.ft_mountpt, mntpt);
		strcpy(ent.ft_devpath, dev);

		/* increase the number of entries first */
#ifdef INVT_DEBUG
		mlog(MLOG_NITTY | MLOG_INV,"INV: putting new fstab entry for %s ....\n",
		      mntpt);
#endif
		cnt->ic_curnum++;
		hoff = (off64_t) (sizeof(invt_counter_t) +
				 (size_t)(cnt->ic_curnum - 1) *
				           sizeof(invt_fstab_t));

		rval = PUT_COUNTERS(fd, cnt);
		if (rval > 0) {
		      rval = PUT_REC_NOLOCK(fd, &ent, sizeof(ent), hoff);
		}

	}
	INVLOCK(fd, LOCK_UN);
	free (cnt);
	close (fd);
	return rval;
}





int
fstab_get_fname(void *pred,
		 char *fname,
		 inv_predicate_t bywhat,
		 inv_oflag_t forwhat)
{
	uuid_t *uuidp = 0;
	char uuidstr[UUID_STR_LEN + 1];
	invt_fstab_t *arr;


	if (bywhat != INV_BY_UUID) {
		int numfs, i, fd;
		invt_counter_t *cnt;

		/* on sucessful return fd is locked */
		fd = fstab_getall(&arr, &cnt, &numfs, forwhat);
		if (fd < 0)
			return -1;
		if (numfs <= 0) {
			mlog(MLOG_NORMAL | MLOG_INV, _(
			      "INV: No recorded filesystems in"
			      " inventory's fstab.\n"));
			return -1;
		}
		INVLOCK(fd, LOCK_UN);
		close (fd);
		free (cnt); /* we dont need it */

		/* first get hold of the uuid for this mount point/device */

		for (i = 0; i < numfs; i++) {
			if ((bywhat == INV_BY_MOUNTPT &&
			     (STREQL(arr[i].ft_mountpt, pred))) ||
			     (bywhat == INV_BY_DEVPATH &&
			     (STREQL(arr[i].ft_devpath, pred)))) {

				uuidp = &arr[i].ft_uuid;
				break;
			}
		}
#ifdef INVT_DEBUG
		if (!uuidp)
			mlog(MLOG_DEBUG | MLOG_INV,"INV: get_fname: unable to find %s"
			      " in the inventory\n", (char *)pred);
#endif

	} else {
		uuidp = (uuid_t *)pred;
	}

	if (!uuidp)
		return -1;

	uuid_unparse(*uuidp, uuidstr);

	strncpy (fname, INV_DIRPATH, INV_STRLEN);
	strcat (fname, "/");
	strcat (fname, uuidstr);

	if (bywhat != INV_BY_UUID)
		free (arr);

	assert((int) strlen(fname) < INV_STRLEN);
	return 1;
}


void
fstab_DEBUG_print(invt_fstab_t *arr, int num)
{
	int i;
	char str[UUID_STR_LEN + 1];

	mlog(MLOG_NORMAL | MLOG_INV, _("\n\n--------- fstab ------------\n"));
	for (i = 0; i < num; i++) {
		printf(_("Mount\t%s\n"), arr[i].ft_mountpt);
		printf(_("Dev\t%s\n"), arr[i].ft_devpath);
		uuid_unparse(arr[i].ft_uuid, str);
		printf(_("FSid\t%s\n\n"), str);
	}
	mlog(MLOG_NORMAL | MLOG_INV, "\n---------========------------\n");
}
