/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1999 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Code to maintain the runtime and on-disk filehandle mapping table for
 * nfslog.
 */

#include <assert.h>
#include <errno.h>
#include <nfs/nfs.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <libintl.h>
#include <unistd.h>
#include <nfs/nfs.h>
#include <nfs/nfs_log.h>
#include "fhtab.h"
#include "nfslogd.h"

#define	ROUNDUP32(val)		(((val) + 3) & ~3)

#define	IS_DOT_FILENAME(name)						\
	((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))

#define	PRINT_LINK_DATA(fp, func, dfh, name, str)			\
	(void) fprintf(fp, "%s: name '%s', dfh ",			\
		func, (((name) != NULL) ? name : ""));			\
	debug_opaque_print(fp, dfh, sizeof (*(dfh)));			\
	(void) fprintf(fp, "%s\n", str);


#define	PRINT_FULL_DATA(fp, func, dfh, fh, name, str)			\
	(void) fprintf(fp, "%s: name '%s', dfh ",			\
		func, (((name) != NULL) ? name : ""));			\
	debug_opaque_print(fp, dfh, sizeof (*(dfh)));			\
	if ((fh) != NULL) {						\
		(void) fprintf(fp, ", fh ");				\
		debug_opaque_print(fp, fh, sizeof (*(fh)));		\
	}								\
	(void) fprintf(fp, "%s\n", str);

/*
 * export handle cache
 */
struct export_handle_cache {
	fhandle_t			fh;
	char				*name;
	struct export_handle_cache	*next;
};

static struct export_handle_cache	*exp_handle_cache = NULL;

extern bool_t nfsl_prin_fh;

static int	fh_add(char *, fhandle_t *, fhandle_t *, char *);

static char *get_export_path(fhandle_t *, char *);
static void sprint_fid(char *, uint_t, const fhandle_t *);
static void fh_print_all_keys(char *fhpath, fhandle_t *fh);
static int fh_compare(fhandle_t *fh1, fhandle_t *fh2);
static fhlist_ent *fh_lookup(char *fhpath, fhandle_t *fh, fhlist_ent *fhrecp,
	int *errorp);
static int fh_remove_mc_link(char *fhpath, fhandle_t *dfh, char *name,
	char **pathp);
static int fh_remove(char *fhpath, fhandle_t *dfh, char *name, char **pathp);
static int fh_rename(char *fhpath, fhandle_t *from_dfh, char *from_name,
	char **from_pathp, fhandle_t *to_dfh, char *to_name);

static fhlist_ent *fh_lookup_link(char *fhpath, fhandle_t *dfh, fhandle_t *fh,
	char *name, fhlist_ent *fhrecp, int *errorp);
static struct nfsl_fh_proc_disp *nfslog_find_fh_dispatch(
	nfslog_request_record *);
static struct export_handle_cache *find_fh_in_export_cache(fhandle_t *fh);
static void add_fh_to_export_cache(fhandle_t *fh, char *path);
static char *update_export_point(char *fhpath, fhandle_t *fh, char *path);
static char *fh_print_absolute(char *fhpath, fhandle_t *fh, char *name);
static void nfslog_null_fhargs(caddr_t *nfsl_args, caddr_t *nfsl_res,
	char *fhpath, char **pathp1, char **pathp2);
static void nfslog_LOOKUP_calc(fhandle_t *dfh, char *name, fhandle_t *fh,
	char *fhpath, char **pathp1, char **pathp2, char *str);

/*
 * NFS VERSION 2
 */

/*
 * Functions for updating the fhtable for fhtoppath and for returning
 * the absolute pathname
 */
static void nfslog_GETATTR2_fhargs(fhandle_t *,
	nfsstat *, char *fhpath, char **, char **);
static void nfslog_SETATTR2_fhargs(nfslog_setattrargs *, nfsstat *,
	char *, char **, char **);
static void nfslog_LOOKUP2_fhargs(nfslog_diropargs *, nfslog_diropres *,
	char *, char **, char **);
static void nfslog_READLINK2_fhargs(fhandle_t *, nfslog_rdlnres *,
	char *, char **, char **);
static void nfslog_READ2_fhargs(nfslog_nfsreadargs *, nfslog_rdresult *,
	char *, char **, char **);
static void nfslog_WRITE2_fhargs(nfslog_writeargs *, nfslog_writeresult *,
	char *, char **, char **);
static void nfslog_CREATE2_fhargs(nfslog_createargs *, nfslog_diropres*,
	char *, char **, char **);
static void nfslog_REMOVE2_fhargs(nfslog_diropargs *, nfsstat *,
	char *, char **, char **);
static void nfslog_RENAME2_fhargs(nfslog_rnmargs *, nfsstat *,
	char *, char **, char **);
static void nfslog_LINK2_fhargs(nfslog_linkargs *, nfsstat *,
	char *, char **, char **);
static void nfslog_SYMLINK2_fhargs(nfslog_symlinkargs *, nfsstat *,
	char *, char **, char **);
static void nfslog_READDIR2_fhargs(nfslog_rddirargs *, nfslog_rddirres *,
	char *, char **, char **);
static void nfslog_STATFS2_fhargs(fhandle_t *, nfsstat *,
	char *, char **, char **);

/*
 * NFS VERSION 3
 *
 * Functions for updating the fhtable for fhtoppath
 */
static void nfslog_GETATTR3_fhargs(nfs_fh3 *, nfsstat3 *,
	char *, char **, char **);
static void nfslog_SETATTR3_fhargs(nfslog_SETATTR3args *,	nfsstat3 *,
	char *, char **, char **);
static void nfslog_LOOKUP3_fhargs(nfslog_diropargs3 *, nfslog_LOOKUP3res *,
	char *, char **, char **);
static void nfslog_ACCESS3_fhargs(nfs_fh3 *, nfsstat3 *,
	char *, char **, char **);
static void nfslog_READLINK3_fhargs(nfs_fh3 *, nfslog_READLINK3res *,
	char *, char **, char **);
static void nfslog_READ3_fhargs(nfslog_READ3args *, nfslog_READ3res *,
	char *, char **, char **);
static void nfslog_WRITE3_fhargs(nfslog_WRITE3args *, nfslog_WRITE3res *,
	char *, char **, char **);
static void nfslog_CREATE3_fhargs(nfslog_CREATE3args *, nfslog_CREATE3res *,
	char *, char **, char **);
static void nfslog_MKDIR3_fhargs(nfslog_MKDIR3args *, nfslog_MKDIR3res *,
	char *, char **, char **);
static void nfslog_SYMLINK3_fhargs(nfslog_SYMLINK3args *, nfslog_SYMLINK3res *,
	char *, char **, char **);
static void nfslog_MKNOD3_fhargs(nfslog_MKNOD3args *, nfslog_MKNOD3res *,
	char *, char **, char **);
static void nfslog_REMOVE3_fhargs(nfslog_REMOVE3args *, nfsstat3 *,
	char *, char **, char **);
static void nfslog_RMDIR3_fhargs(nfslog_RMDIR3args *, nfsstat3 *,
	char *, char **, char **);
static void nfslog_RENAME3_fhargs(nfslog_RENAME3args *, nfsstat3 *,
	char *, char **, char **);
static void nfslog_LINK3_fhargs(nfslog_LINK3args *, nfsstat3 *,
	char *, char **, char **);
static void nfslog_READDIR3_fhargs(nfs_fh3 *, nfsstat3 *,
	char *, char **, char **);
static void nfslog_READDIRPLUS3_fhargs(nfslog_READDIRPLUS3args *,
	nfslog_READDIRPLUS3res *,
	char *, char **, char **);
static void nfslog_FSSTAT3_fhargs(nfs_fh3 *, nfsstat3 *,
	char *, char **, char **);
static void nfslog_FSINFO3_fhargs(nfs_fh3 *, nfsstat3 *,
	char *, char **, char **);
static void nfslog_PATHCONF3_fhargs(nfs_fh3 *, nfsstat3 *,
	char *, char **, char **);
static void nfslog_COMMIT3_fhargs(nfslog_COMMIT3args *, nfsstat3 *,
	char *, char **, char **);

/*
 * NFSLOG VERSION 1
 *
 * Functions for updating the fhtable for fhtoppath
 */
static void nfslog_SHARE_fhargs(nfslog_sharefsargs *, nfslog_sharefsres *,
	char *, char **, char **);
static void nfslog_UNSHARE_fhargs(nfslog_sharefsargs *, nfslog_sharefsres *,
	char *, char **, char **);
static void nfslog_GETFH_fhargs(nfslog_getfhargs *, nfsstat *,
	char *, char **, char **);

/*
 * Define the actions taken per prog/vers/proc:
 *
 * In some cases, the nl types are the same as the nfs types and a simple
 * bcopy should suffice. Rather that define tens of identical procedures,
 * simply define these to bcopy. Similarly this takes care of different
 * procs that use same parameter struct.
 */

static struct nfsl_fh_proc_disp nfsl_fh_proc_v2[] = {
	/*
	 * NFS VERSION 2
	 */

	/* RFS_NULL = 0 */
	{nfslog_null_fhargs, xdr_void, xdr_void, 0, 0},

	/* RFS_GETATTR = 1 */
	{nfslog_GETATTR2_fhargs, xdr_fhandle, xdr_nfsstat,
		sizeof (fhandle_t), sizeof (nfsstat)},

	/* RFS_SETATTR = 2 */
	{nfslog_SETATTR2_fhargs, xdr_nfslog_setattrargs, xdr_nfsstat,
		sizeof (nfslog_setattrargs), sizeof (nfsstat)},

	/* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */
	{nfslog_null_fhargs, xdr_void, xdr_void, 0, 0},

	/* RFS_LOOKUP = 4 */
	{nfslog_LOOKUP2_fhargs, xdr_nfslog_diropargs, xdr_nfslog_diropres,
		sizeof (nfslog_diropargs), sizeof (nfslog_diropres)},

	/* RFS_READLINK = 5 */
	{nfslog_READLINK2_fhargs, xdr_fhandle, xdr_nfslog_rdlnres,
		sizeof (fhandle_t), sizeof (nfslog_rdlnres)},

	/* RFS_READ = 6 */
	{nfslog_READ2_fhargs, xdr_nfslog_nfsreadargs, xdr_nfslog_rdresult,
		sizeof (nfslog_nfsreadargs), sizeof (nfslog_rdresult)},

	/* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */
	{nfslog_null_fhargs, xdr_void, xdr_void, 0, 0},

	/* RFS_WRITE = 8 */
	{nfslog_WRITE2_fhargs, xdr_nfslog_writeargs, xdr_nfslog_writeresult,
		sizeof (nfslog_writeargs), sizeof (nfslog_writeresult)},

	/* RFS_CREATE = 9 */
	{nfslog_CREATE2_fhargs, xdr_nfslog_createargs, xdr_nfslog_diropres,
		sizeof (nfslog_createargs), sizeof (nfslog_diropres)},

	/* RFS_REMOVE = 10 */
	{nfslog_REMOVE2_fhargs, xdr_nfslog_diropargs, xdr_nfsstat,
		sizeof (nfslog_diropargs), sizeof (nfsstat)},

	/* RFS_RENAME = 11 */
	{nfslog_RENAME2_fhargs, xdr_nfslog_rnmargs, xdr_nfsstat,
		sizeof (nfslog_rnmargs), sizeof (nfsstat)},

	/* RFS_LINK = 12 */
	{nfslog_LINK2_fhargs, xdr_nfslog_linkargs, xdr_nfsstat,
		sizeof (nfslog_linkargs), sizeof (nfsstat)},

	/* RFS_SYMLINK = 13 */
	{nfslog_SYMLINK2_fhargs, xdr_nfslog_symlinkargs, xdr_nfsstat,
		sizeof (nfslog_symlinkargs), sizeof (nfsstat)},

	/* RFS_MKDIR = 14 */
	{nfslog_CREATE2_fhargs, xdr_nfslog_createargs, xdr_nfslog_diropres,
		sizeof (nfslog_createargs), sizeof (nfslog_diropres)},

	/* RFS_RMDIR = 15 */
	{nfslog_REMOVE2_fhargs, xdr_nfslog_diropargs, xdr_nfsstat,
		sizeof (nfslog_diropargs), sizeof (nfsstat)},

	/* RFS_READDIR = 16 */
	{nfslog_READDIR2_fhargs, xdr_nfslog_rddirargs, xdr_nfslog_rddirres,
		sizeof (nfslog_rddirargs), sizeof (nfslog_rddirres)},

	/* RFS_STATFS = 17 */
	{nfslog_STATFS2_fhargs, xdr_fhandle, xdr_nfsstat,
		sizeof (fhandle_t), sizeof (nfsstat)},
};


/*
 * NFS VERSION 3
 */

static struct nfsl_fh_proc_disp nfsl_fh_proc_v3[] = {

	/* RFS_NULL = 0 */
	{nfslog_null_fhargs, xdr_void, xdr_void, 0, 0},

	/* RFS3_GETATTR = 1 */
	{nfslog_GETATTR3_fhargs, xdr_nfs_fh3, xdr_nfsstat3,
		sizeof (nfs_fh3), sizeof (nfsstat3)},

	/* RFS3_SETATTR = 2 */
	{nfslog_SETATTR3_fhargs, xdr_nfslog_SETATTR3args, xdr_nfsstat3,
		sizeof (nfslog_SETATTR3args), sizeof (nfsstat3)},

	/* RFS3_LOOKUP = 3 */
	{nfslog_LOOKUP3_fhargs, xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res,
		sizeof (nfslog_diropargs3), sizeof (nfslog_LOOKUP3res)},

	/* RFS3_ACCESS = 4 */
	{nfslog_ACCESS3_fhargs, xdr_nfs_fh3, xdr_nfsstat3,
		sizeof (nfs_fh3), sizeof (nfsstat3)},

	/* RFS3_READLINK = 5 */
	{nfslog_READLINK3_fhargs, xdr_nfs_fh3, xdr_nfslog_READLINK3res,
		sizeof (nfs_fh3), sizeof (nfslog_READLINK3res)},

	/* RFS3_READ = 6 */
	{nfslog_READ3_fhargs, xdr_nfslog_READ3args, xdr_nfslog_READ3res,
		sizeof (nfslog_READ3args), sizeof (nfslog_READ3res)},

	/* RFS3_WRITE = 7 */
	{nfslog_WRITE3_fhargs, xdr_nfslog_WRITE3args, xdr_nfslog_WRITE3res,
		sizeof (nfslog_WRITE3args), sizeof (nfslog_WRITE3res)},

	/* RFS3_CREATE = 8 */
	{nfslog_CREATE3_fhargs, xdr_nfslog_CREATE3args, xdr_nfslog_CREATE3res,
		sizeof (nfslog_CREATE3args), sizeof (nfslog_CREATE3res)},

	/* RFS3_MKDIR = 9 */
	{nfslog_MKDIR3_fhargs, xdr_nfslog_MKDIR3args, xdr_nfslog_MKDIR3res,
		sizeof (nfslog_MKDIR3args), sizeof (nfslog_MKDIR3res)},

	/* RFS3_SYMLINK = 10 */
	{nfslog_SYMLINK3_fhargs, xdr_nfslog_SYMLINK3args,
		xdr_nfslog_SYMLINK3res,
		sizeof (nfslog_SYMLINK3args), sizeof (nfslog_SYMLINK3res)},

	/* RFS3_MKNOD = 11 */
	{nfslog_MKNOD3_fhargs, xdr_nfslog_MKNOD3args, xdr_nfslog_MKNOD3res,
		sizeof (nfslog_MKNOD3args), sizeof (nfslog_MKNOD3res)},

	/* RFS3_REMOVE = 12 */
	{nfslog_REMOVE3_fhargs, xdr_nfslog_REMOVE3args, xdr_nfsstat3,
		sizeof (nfslog_REMOVE3args), sizeof (nfsstat3)},

	/* RFS3_RMDIR = 13 */
	{nfslog_RMDIR3_fhargs, xdr_nfslog_RMDIR3args, xdr_nfsstat3,
		sizeof (nfslog_RMDIR3args), sizeof (nfsstat3)},

	/* RFS3_RENAME = 14 */
	{nfslog_RENAME3_fhargs, xdr_nfslog_RENAME3args, xdr_nfsstat3,
		sizeof (nfslog_RENAME3args), sizeof (nfsstat3)},

	/* RFS3_LINK = 15 */
	{nfslog_LINK3_fhargs, xdr_nfslog_LINK3args, xdr_nfsstat3,
		sizeof (nfslog_LINK3args), sizeof (nfsstat3)},

	/* RFS3_READDIR = 16 */
	{nfslog_READDIR3_fhargs, xdr_nfs_fh3, xdr_nfsstat3,
		sizeof (nfs_fh3), sizeof (nfsstat3)},

	/* RFS3_READDIRPLUS = 17 */
	{nfslog_READDIRPLUS3_fhargs,
		xdr_nfslog_READDIRPLUS3args, xdr_nfslog_READDIRPLUS3res,
		sizeof (nfslog_READDIRPLUS3args),
		sizeof (nfslog_READDIRPLUS3res)},

	/* RFS3_FSSTAT = 18 */
	{nfslog_FSSTAT3_fhargs, xdr_nfs_fh3, xdr_nfsstat3,
		sizeof (nfs_fh3), sizeof (nfsstat3)},

	/* RFS3_FSINFO = 19 */
	{nfslog_FSINFO3_fhargs, xdr_nfs_fh3, xdr_nfsstat3,
		sizeof (nfs_fh3), sizeof (nfsstat3)},

	/* RFS3_PATHCONF = 20 */
	{nfslog_PATHCONF3_fhargs, xdr_nfs_fh3, xdr_nfsstat3,
		sizeof (nfs_fh3), sizeof (nfsstat3)},

	/* RFS3_COMMIT = 21 */
	{nfslog_COMMIT3_fhargs, xdr_nfslog_COMMIT3args, xdr_nfsstat3,
		sizeof (nfslog_COMMIT3args), sizeof (nfsstat3)},
};

/*
 * NFSLOG VERSION 1
 */

static struct nfsl_fh_proc_disp nfsl_log_fh_proc_v1[] = {

	/* NFSLOG_NULL = 0 */
	{nfslog_null_fhargs, xdr_void, xdr_void, 0, 0},

	/* NFSLOG_SHARE = 1 */
	{nfslog_SHARE_fhargs, xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres,
		sizeof (nfslog_sharefsargs), sizeof (nfslog_sharefsres)},

	/* NFSLOG_UNSHARE = 2 */
	{nfslog_UNSHARE_fhargs, xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres,
		sizeof (nfslog_sharefsargs), sizeof (nfslog_sharefsres)},

	/* NFSLOG_LOOKUP3 = 3 */
	{nfslog_LOOKUP3_fhargs, xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res,
		sizeof (nfslog_diropargs3), sizeof (nfslog_LOOKUP3res)},

	/* NFSLOG_GETFH = 4 */
	{nfslog_GETFH_fhargs, xdr_nfslog_getfhargs, xdr_nfsstat,
		sizeof (nfslog_getfhargs), sizeof (nfsstat)},
};

static struct nfsl_fh_vers_disp nfsl_fh_vers_disptable[] = {
	{sizeof (nfsl_fh_proc_v2) / sizeof (nfsl_fh_proc_v2[0]),
	    nfsl_fh_proc_v2},
	{sizeof (nfsl_fh_proc_v3) / sizeof (nfsl_fh_proc_v3[0]),
	    nfsl_fh_proc_v3},
};

static struct nfsl_fh_vers_disp nfsl_log_fh_vers_disptable[] = {
	{sizeof (nfsl_log_fh_proc_v1) / sizeof (nfsl_log_fh_proc_v1[0]),
	    nfsl_log_fh_proc_v1},
};

static struct nfsl_fh_prog_disp nfsl_fh_dispatch_table[] = {
	{NFS_PROGRAM,
	    NFS_VERSMIN,
	    sizeof (nfsl_fh_vers_disptable) /
		sizeof (nfsl_fh_vers_disptable[0]),
	    nfsl_fh_vers_disptable},
	{NFSLOG_PROGRAM,
	    NFSLOG_VERSMIN,
	    sizeof (nfsl_log_fh_vers_disptable) /
		sizeof (nfsl_log_fh_vers_disptable[0]),
	    nfsl_log_fh_vers_disptable},
};

static int	nfsl_fh_dispatch_table_arglen =
			sizeof (nfsl_fh_dispatch_table) /
			sizeof (nfsl_fh_dispatch_table[0]);

extern int debug;

/*
 * print the fid into the given string as a series of hex digits.
 * XXX Ideally, we'd like to just convert the filehandle into an i-number,
 * but the fid encoding is a little tricky (see nfs_fhtovp() and
 * ufs_vget()) and may be private to UFS.
 */

static void
sprint_fid(char *buf, uint_t buflen, const fhandle_t *fh)
{
	int i;
	uchar_t byte;
	uint_t fhlen;

	/*
	 * If the filehandle somehow got corrupted, only print the part
	 * that makes sense.
	 */
	if (fh->fh_len > NFS_FHMAXDATA)
		fhlen = NFS_FHMAXDATA;
	else
		fhlen = fh->fh_len;
	assert(2 * fhlen < buflen);

	for (i = 0; i < fhlen; i++) {
		byte = fh->fh_data[i];
		(void) sprintf(buf + 2 * i, "%02x", byte);
	}
}

static void
fh_print_all_keys(char *fhpath, fhandle_t *fh)
{
	if ((fhpath == NULL) || (fh == NULL) || (debug <= 1))
		return;
	(void) printf("\nBegin all database keys\n");
	db_print_all_keys(fhpath, &fh->fh_fsid, stdout);
	(void) printf("\nEnd   all database keys\n");
}

#define	FH_ADD(path, dfh, fh, name) \
	fh_add(path, dfh, fh, name)

/*
 * Add the filehandle "fh", which has the name "name" and lives in
 * directory "dfh", to the table "fhlist".  "fhlist" will be updated if the
 * entry is added to the front of the list.
 * Return 0 for success, error code otherwise.
 */
static int
fh_add(char *fhpath, fhandle_t *dfh, fhandle_t *fh, char *name)
{
	uint_t	flags = 0;
	int	error;

	if (IS_DOT_FILENAME(name)) {
		/* we don't insert these to the database but not an error */
		if (debug > 3) {
			PRINT_FULL_DATA(stdout, "fh_add", dfh, fh, name,
				" - no dot files")
		}
		return (0);
	}
	if (dfh && (memcmp(fh, dfh, NFS_FHSIZE) == 0)) {
		flags |= EXPORT_POINT;
	}

	/* Add to database */
	error = db_add(fhpath, dfh, name, fh, flags);
	if (debug > 1) {
		if (error != 0) {
			(void) printf("db_add error %s:\n",
				((error >= 0) ? strerror(error) : "Unknown"));
			PRINT_FULL_DATA(stdout, "fh_add", dfh, fh, name, "")
		} else if (debug > 2) {
			PRINT_FULL_DATA(stdout, "fh_add", dfh, fh, name, "")
		}
	}
	return (error);
}

/*
 * fh_compare returns 0 if the file handles match, error code otherwise
 */
static int
fh_compare(fhandle_t *fh1, fhandle_t *fh2)
{
	if (memcmp(fh1, fh2, NFS_FHSIZE))
		return (errno);
	else
		return (0);
}

/*
 * Try to find the filehandle "fh" in the table.  Returns 0 and the
 * corresponding table entry if found, error otherwise.
 * If successfull and fhrecpp is non-null then *fhrecpp points to the
 * returned record. If *fhrecpp was initially null, that record had
 * been malloc'd and must be freed by caller.
 */

static fhlist_ent *
fh_lookup(char *fhpath, fhandle_t *fh, fhlist_ent *fhrecp, int *errorp)
{
	if (debug > 3) {
		(void) printf("fh_lookup: fh ");
		debug_opaque_print(stdout, fh, sizeof (*fh));
		(void) printf("\n");
	}
	return (db_lookup(fhpath, fh, fhrecp, errorp));
}

/*
 * Remove the mc link if exists when removing a regular link.
 * Return 0 for success, error code otherwise.
 */
static int
fh_remove_mc_link(char *fhpath, fhandle_t *dfh, char *name, char **pathp)
{
	int	error;
	char	*str, *str1;

	/* Delete the multi-component path if exists */
	if ((pathp == NULL) || (*pathp == NULL)) {
		str = nfslog_get_path(dfh, name, fhpath, "remove_mc_link");
		str1 = str;
	} else {
		str = *pathp;
		str1 = NULL;
	}
	error = db_delete_link(fhpath, &public_fh, str);
	if (str1 != NULL)
		free(str1);
	return (error);
}

/*
 * Remove the link entry from the fh table.
 * Return 0 for success, error code otherwise.
 */
static int
fh_remove(char *fhpath, fhandle_t *dfh, char *name, char **pathp)
{
	/*
	 * disconnect element from list
	 *
	 * Remove the link entry for the file. Remove fh entry if last link.
	 */
	if (IS_DOT_FILENAME(name)) {
		/* we don't insert these to the database but not an error */
		if (debug > 2) {
			PRINT_LINK_DATA(stdout, "fh_remove", dfh, name,
				" - no dot files")
		}
		return (0);
	}
	if (debug > 2) {
		PRINT_LINK_DATA(stdout, "fh_remove", dfh, name, "")
	}
	/* Delete the multi-component path if exists */
	(void) fh_remove_mc_link(fhpath, dfh, name, pathp);
	return (db_delete_link(fhpath, dfh, name));
}

/*
 * fh_rename - renames a link in the database (adds the new one if from link
 * did not exist).
 * Return 0 for success, error code otherwise.
 */
static int
fh_rename(char *fhpath, fhandle_t *from_dfh, char *from_name, char **from_pathp,
	fhandle_t *to_dfh, char *to_name)
{
	if (debug > 2) {
		PRINT_LINK_DATA(stdout, "fh_rename: from:", from_dfh,
		    from_name, "")
		PRINT_LINK_DATA(stdout, "fh_rename: to  :", to_dfh,
		    to_name, "")
	}
	/*
	 * if any of these are dot files (should not happen), the rename
	 * becomes a "delete" or "add" operation because the dot files
	 * don't get in the database
	 */
	if (IS_DOT_FILENAME(to_name)) {
		/* it is just a delete op */
		if (debug > 2) {
			(void) printf("to: no dot files\nDelete from: '%s'\n",
				from_name);
		}
		return (fh_remove(fhpath, from_dfh, from_name, from_pathp));
	} else if (IS_DOT_FILENAME(from_name)) {
		/* we don't insert these to the database */
		if (debug > 2) {
			(void) printf("rename - from: no dot files\n");
		}
		/* can't insert the target, because don't have a handle */
		return (EINVAL);
	}
	/* Delete the multi-component path if exists */
	(void) fh_remove_mc_link(fhpath, from_dfh, from_name, from_pathp);
	return (db_rename_link(fhpath, from_dfh, from_name, to_dfh, to_name));
}

/*
 * fh_lookup_link - search the fhtable for the link defined by (dfh,name,fh).
 * Return 0 and set *fhrecpp to the fhlist item corresponding to it if found,
 * or error if not found.
 * Possible configurations:
 * 1. dfh, fh, name are all non-null: Only exact match accepted.
 * 2. dfh,name non-null, fh null: return first match found.
 * 3. fh,name non-null, dfh null: return first match found.
 * 3. fh non-null, dfh, name null: return first match found.
 * If successfull and fhrecpp is non-null then *fhrecpp points to the
 * returned record. If *fhrecpp was initially null, that record had
 * been malloc'd and must be freed by caller.
 */
static fhlist_ent *
fh_lookup_link(char *fhpath, fhandle_t *dfh, fhandle_t *fh, char *name,
	fhlist_ent *fhrecp, int *errorp)
{
	fhlist_ent	*in_fhrecp = fhrecp;

	if ((name != NULL) && IS_DOT_FILENAME(name)) {
		/* we don't insert these to the database but not an error */
		if (debug > 2) {
			PRINT_FULL_DATA(stdout, "fh_lookup_link", dfh, fh, name,
				" - no dot files\n")
		}
		*errorp = 0;
		return (NULL);
	}
	if (debug > 3) {
		PRINT_FULL_DATA(stdout, "fh_lookup_link", dfh, fh, name, "")
	}
	/* Add to database */
	if (fh != NULL) {
		fhrecp = db_lookup(fhpath, fh, fhrecp, errorp);
		if (fhrecp == NULL) {
			if (debug > 3)
				(void) printf("fh_lookup_link: fh not found\n");
			return (NULL);
		}
		/* Check if name and dfh match, if not search link */
		if (((dfh == NULL) || !fh_compare(dfh, &fhrecp->dfh)) &&
		    ((name == NULL) || (strcmp(name, fhrecp->name) == 0))) {
			/* found it */
			goto exit;
		}
		/* Found the primary record, but it's a different link */
		if (debug == 3) {	/* Only log if >2 but already printed */
			PRINT_FULL_DATA(stdout, "fh_lookup_link", dfh, fh,
				name, "")
		}
		if (debug > 2) {
			PRINT_LINK_DATA(stdout, "Different primary link",
				&fhrecp->dfh, fhrecp->name, "")
		}
		/* can now free the record unless it was supplied by caller */
		if (fhrecp != in_fhrecp) {
			free(fhrecp);
			fhrecp = NULL;
		}
	}
	/* If here, we must search by link */
	if ((dfh == NULL) || (name == NULL)) {
		if (debug > 2)
			(void) printf("fh_lookup_link: invalid params\n");
		*errorp = EINVAL;
		return (NULL);
	}
	fhrecp = db_lookup_link(fhpath, dfh, name, fhrecp, errorp);
	if (fhrecp == NULL) {
		if (debug > 3)
			(void) printf("fh_lookup_link: link not found: %s\n",
			    ((*errorp >= 0) ? strerror(*errorp) : "Unknown"));
		return (NULL);
	}
	/* If all args supplied, check if an exact match */
	if ((fh != NULL) && fh_compare(fh, &fhrecp->fh)) {
		if (debug > 2) {
			PRINT_FULL_DATA(stderr, "fh_lookup_link", dfh, fh,
				name, "")
			PRINT_LINK_DATA(stderr, "Different primary link",
			    &fhrecp->dfh, fhrecp->name, "")
		}
		if (fhrecp != in_fhrecp)
			free(fhrecp);
		*errorp = EINVAL;
		return (NULL);
	}
exit:
	if (debug > 3)
		(void) printf("lookup: found '%s' in fhtable\n", name);
	*errorp = 0;
	return (fhrecp);
}

/*
 * Export handle cache is maintained if we see an export handle that either
 * cannot have the path for it determined, or we failed store it.
 * Usually the path of an export handle can be identified in the NFSLOGTAB
 * and since every path for that filesystem will be affected, it's worth
 * caching the ones we had problem identifying.
 */

/*
 * find_fh_in_export_cache - given an export fh, find it in the cache and
 * return the handle
 */
static struct export_handle_cache *
find_fh_in_export_cache(fhandle_t *fh)
{
	struct export_handle_cache	*p;

	for (p = exp_handle_cache; p != NULL; p = p->next) {
		if (memcmp(fh, &p->fh, sizeof (*fh)) == 0)
			break;
	}
	return (p);
}

static void
add_fh_to_export_cache(fhandle_t *fh, char *path)
{
	struct export_handle_cache	*new;

	if ((new = malloc(sizeof (*new))) == NULL) {
		syslog(LOG_ERR, gettext(
		"add_fh_to_export_cache: alloc new for '%s' Error %s\n"),
			((path != NULL) ? path : ""), strerror(errno));
		return;
	}
	if (path != NULL) {
		if ((new->name = malloc(strlen(path) + 1)) == NULL) {
			syslog(LOG_ERR, gettext(
				"add_fh_to_export_cache: alloc '%s'"
				    " Error %s\n"), path, strerror(errno));
			free(new);
			return;
		}
		(void) strcpy(new->name, path);
	} else {
		new->name = NULL;
	}
	(void) memcpy(&new->fh, fh, sizeof (*fh));
	new->next = exp_handle_cache;
	exp_handle_cache = new;
}

/*
 * update_export_point - called when the path for fh cannot be determined.
 * In the past it called get_export_path() to get the name of the
 * export point given a filehandle. This was a hack, since there's no
 * reason why the filehandle should be lost.
 *
 * If a match is found, insert the path to the database.
 * Return the inserted fhrecp is found,
 * and NULL if not. If it is an exported fs but not in the list, log a
 * error.
 * If input fhrecp is non-null, it is a valid address for result,
 * otherwise malloc it.
 */
static char *
update_export_point(char *fhpath, fhandle_t *fh, char *path)
{
	struct export_handle_cache	*p;

	if ((fh == NULL) || memcmp(&fh->fh_data, &fh->fh_xdata, fh->fh_len)) {
		/* either null fh or not the root of a shared directory */
		return (NULL);
	}
	/* Did we already see (and fail) this one? */
	if ((p = find_fh_in_export_cache(fh)) != NULL) {
		/* Found it! */
		if (debug > 2) {
			PRINT_LINK_DATA(stdout, "update_export_point",
				fh, ((p->name != NULL) ? p->name : ""), "");
		}
		if (p->name == NULL)
			return (NULL);
		/*
		 * We should not normally be here - only add to cache if
		 * fh_add failed.
		 */
		if ((path == NULL) &&
		    ((path = malloc(strlen(p->name) + 1)) == NULL)) {
			syslog(LOG_ERR, gettext(
				"update_export_point: malloc '%s' Error %s"),
				    p->name, strerror(errno));
			return (NULL);
		}
		(void) strcpy(path, p->name);
		return (path);
	}
	if ((path = get_export_path(fh, path)) == NULL) {
		add_fh_to_export_cache(fh, NULL);
		return (NULL);
	}
	/* Found it! */
	if (debug > 2) {
		PRINT_LINK_DATA(stdout, "update_export_point", fh, path, "")
	}
	if (FH_ADD(fhpath, fh, fh, path)) {
		/* cache this handle so we don't repeat the search */
		add_fh_to_export_cache(fh, path);
	}
	return (path);
}

/*
 * HACK!!! To get rid of get_export_path() use
 */
/* ARGSUSED */
static char *
get_export_path(fhandle_t *fh, char *path)
{
	return (NULL);
}

/*
 * Return the absolute pathname for the filehandle "fh", using the mapping
 * table "fhlist".  The caller must free the return string.
 * name is an optional dir component name, to be appended at the end
 * (if name is non-null, the function assumes the fh is the parent directory)
 *
 * Note: The original code was recursive, which was much more elegant but
 * ran out of stack...
 */

static char *
fh_print_absolute(char *fhpath, fhandle_t *fh, char *name)
{
	char		*str, *rootname, parent[MAXPATHLEN];
	int		i, j, k, len, error;
	fhlist_ent	fhrec, *fhrecp;
	fhandle_t	prevfh;
	int		namelen;

	if (debug > 3)
		(void) printf("fh_print_absolute: input name '%s'\n",
			((name != NULL) ? name : ""));
	/* If name starts with '/' we are done */
	if ((name != NULL) && (name[0] == '/')) {
		if ((str = strdup(name)) == NULL) {
			syslog(LOG_ERR, gettext(
				"fh_print_absolute: strdup '%s' error %s\n"),
				name, strerror(errno));
		}
		return (str);
	}
	namelen = ((name != NULL) ? strlen(name) + 2 : 0);
	parent[0] = '\0';

	/* remember the last filehandle we've seen */
	(void) memcpy((void *) &prevfh, (void *) fh, sizeof (*fh));
	fh = &prevfh;

	/* dump all names in reverse order */
	while ((fhrecp = fh_lookup(fhpath, fh, &fhrec, &error)) != NULL &&
		!(fhrecp->flags & (EXPORT_POINT | PUBLIC_PATH))) {

		if (debug > 3) {
			(void) printf("fh_print_absolute: name '%s'%s\n",
				fhrecp->name,
				((fhrecp->flags & EXPORT_POINT) ? "root" : ""));
		}
		if (memcmp(&prevfh, &fhrecp->dfh, sizeof (*fh)) == 0) {
			/* dfh == prevfh but not an export point */
			if (debug > 1) {
				(void) printf(
					"fh_print_absolute: fhrec loop:\n");
					debug_opaque_print(stdout, fhrecp,
					fhrecp->reclen);
			}
			break;
		}
		(void) strcat(parent, "/");
		(void) strcat(parent, fhrecp->name);

		/* remember the last filehandle we've seen */
		(void) memcpy(&prevfh, &fhrecp->dfh, sizeof (fhrecp->dfh));
	}
	assert(fh == &prevfh);

	if (fhrecp != NULL) {
		rootname = fhrecp->name;
	} else {
		/* Check if export point, just in case... */
		/* There should be enough room in parent, leave the '\0' */
		rootname = update_export_point(
				fhpath, fh, &parent[strlen(parent) + 1]);
	}
	/* Now need to reverse the order */
	if (rootname != NULL) {	/* *fhrecp is the export point */
		len = strlen(rootname) + 2;
	} else {
		len = 2 * (NFS_FHMAXDATA + fh->fh_len);	/* fid instead */
	}
	len = ROUNDUP32(len + namelen + strlen(parent));
	if ((str = malloc(len)) == NULL) {
		syslog(LOG_ERR, gettext(
			"fh_print_absolute: malloc %d error %s\n"),
			    len, strerror(errno));
		return (NULL);
	}
	/* first put the export point path in */
	if (rootname != NULL) {	/* *fhrecp is the export point */
		(void) strcpy(str, rootname);
	} else {
		sprint_fid(str, len, fh);
	}
	for (k = strlen(str), i = strlen(parent); (k < len) && (i >= 0); i--) {
		for (j = i; (j >= 0) && (parent[j] != '/'); j--);
		if (j < 0)
			break;
		(void) strcpy(&str[k], &parent[j]);
		k += strlen(&str[k]);
		parent[j] = '\0';
	}
	if ((name != NULL) && ((k + namelen) <= len)) {
		str[k] = '/';
		(void) strcpy(&str[k + 1], name);
	}
	if (debug > 3)
		(void) printf("fh_print_absolute: path '%s'\n", str);
	return (str);
}

/*
 * nfslog_find_fh_dispatch - get the dispatch struct for this request
 */
static struct nfsl_fh_proc_disp *
nfslog_find_fh_dispatch(nfslog_request_record *logrec)
{
	nfslog_record_header		*logrechdr = &logrec->re_header;
	struct nfsl_fh_prog_disp	*progtable;	/* prog struct */
	struct nfsl_fh_vers_disp	*verstable;	/* version struct */
	int				i, vers;

	/* Find prog element - search because can't use prog as array index */
	for (i = 0; (i < nfsl_fh_dispatch_table_arglen) &&
	    (logrechdr->rh_prognum != nfsl_fh_dispatch_table[i].nfsl_dis_prog);
		i++);
	if (i >= nfsl_fh_dispatch_table_arglen) {	/* program not logged */
		/* not an error */
		return (NULL);
	}
	progtable = &nfsl_fh_dispatch_table[i];
	/* Find vers element - no validity check - if here it's valid vers */
	vers = logrechdr->rh_version - progtable->nfsl_dis_versmin;
	verstable = &progtable->nfsl_dis_vers_table[vers];
	/* Find proc element - no validity check - if here it's valid proc */
	return (&verstable->nfsl_dis_proc_table[logrechdr->rh_procnum]);
}

/* ARGSUSED */
static void
nfslog_null_fhargs(caddr_t *nfsl_args, caddr_t *nfsl_res,
	char *fhpath, char **pathp1, char **pathp2)
{
	*pathp1 = NULL;
	*pathp2 = NULL;
}

/*
 * nfslog_LOOKUP_calc - called by both lookup3 and lookup2. Handles the
 * mclookup as well as normal lookups.
 */
/* ARGSUSED */
static void
nfslog_LOOKUP_calc(fhandle_t *dfh, char *name, fhandle_t *fh,
	char *fhpath, char **pathp1, char **pathp2, char *str)
{
	int		error;
	fhlist_ent	fhrec;
	char		*name1 = NULL;

	if (fh == &public_fh) {
		/* a fake lookup to inform us of the public fs path */
		if (error = FH_ADD(fhpath, fh, fh, name)) {
			syslog(LOG_ERR, gettext(
				"%s: Add Public fs '%s' failed: %s\n"),
				str, name,
				((error >= 0) ? strerror(error) : "Unknown"));
		}
		if (pathp1 != NULL) {
			*pathp1 = nfslog_get_path(dfh, NULL, fhpath, str);
			*pathp2 = NULL;
		}
		return;
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(dfh, name, fhpath, str);
		*pathp2 = NULL;
	}

	/* If public fh mclookup, then insert complete path */
	if (dfh == &public_fh) {
		if (pathp1 != NULL) {
			name = *pathp1;
		} else {
			name = nfslog_get_path(dfh, name, fhpath, str);
			name1 = name;
		}
	}
	if (fh_lookup_link(fhpath, dfh, fh, name, &fhrec, &error) != NULL) {
		/* link already in table */
		if (name1 != NULL)
			free(name1);
		return;
	}
	/* A new link so add it */
	if (error = FH_ADD(fhpath, dfh, fh, name)) {
		syslog(LOG_ERR, gettext(
			"%s: Add fh for '%s' failed: %s\n"), str,
			    name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
	if (name1 != NULL)
		free(name1);
}

/*
 * NFS VERSION 2
 */

/* Functions for updating the fhtable for fhtoppath */

/*
 * nfslog_GETATTR2_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_GETATTR2_fhargs(fhandle_t *args, nfsstat *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nGETATTR2: fh ");
		debug_opaque_print(stdout, args, sizeof (*args));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE2(args),
				NULL, fhpath, "getattr2");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_SETATTR2_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_SETATTR2_fhargs(nfslog_setattrargs *args, nfsstat *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nSETATTR2: fh ");
		debug_opaque_print(stdout, &args->saa_fh,
			sizeof (args->saa_fh));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE2(&args->saa_fh),
				NULL, fhpath, "setattr2");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_LOOKUP2_fhargs - search the table to ensure we have not added this
 * one already. Note that if the response status was anything but okay,
 * there is no fh to check...
 */
/* ARGSUSED */
static void
nfslog_LOOKUP2_fhargs(nfslog_diropargs *args, nfslog_diropres *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh, *fh;

	dfh = &args->da_fhandle;
	name = args->da_name;
	if (debug > 2) {
		if (res->dr_status == NFS_OK)
			fh = &res->nfslog_diropres_u.dr_ok.drok_fhandle;
		else
			fh = NULL;
		PRINT_FULL_DATA(stdout, "=============\nLOOKUP2",
			dfh, fh, name, "")
		if (res->dr_status !=  NFS_OK)
			(void) printf("status %d\n", res->dr_status);
	}
	dfh = NFSLOG_GET_FHANDLE2(dfh);
	if ((dfh == &public_fh) && (name[0] == '\x80')) {
		/* special mclookup */
		name = &name[1];
	}
	if (res->dr_status != NFS_OK) {
		if (pathp1 != NULL) {
			*pathp1 = nfslog_get_path(dfh, name, fhpath, "lookup2");
			*pathp2 = NULL;
		}
		return;
	}
	fh = NFSLOG_GET_FHANDLE2(&res->nfslog_diropres_u.dr_ok.drok_fhandle);
	nfslog_LOOKUP_calc(dfh, name, fh, fhpath, pathp1, pathp2, "Lookup2");
}

/*
 * nfslog_READLINK2_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_READLINK2_fhargs(fhandle_t *args, nfslog_rdlnres *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nREADLINK2: fh ");
		debug_opaque_print(stdout, args, sizeof (*args));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE2(args),
				NULL, fhpath, "readlink2");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_READ2_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_READ2_fhargs(nfslog_nfsreadargs *args, nfslog_rdresult *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nREAD2: fh ");
		debug_opaque_print(stdout, &args->ra_fhandle,
			sizeof (args->ra_fhandle));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(
				NFSLOG_GET_FHANDLE2(&args->ra_fhandle),
				NULL, fhpath, "read2");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_WRITE2_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_WRITE2_fhargs(nfslog_writeargs *args, nfslog_writeresult *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nWRITE2: fh ");
		debug_opaque_print(stdout, &args->waargs_fhandle,
			sizeof (args->waargs_fhandle));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(
			NFSLOG_GET_FHANDLE2(&args->waargs_fhandle),
			NULL, fhpath, "write2");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_CREATE2_fhargs - if the operation succeeded, we are sure there can
 * be no such link in the fhtable, so just add it.
 */
/* ARGSUSED */
static void
nfslog_CREATE2_fhargs(nfslog_createargs *args, nfslog_diropres *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh, *fh;
	int		error;

	name = args->ca_da.da_name;
	dfh = &args->ca_da.da_fhandle;
	if (debug > 2) {
		if (res->dr_status == NFS_OK)
			fh = &res->nfslog_diropres_u.dr_ok.drok_fhandle;
		else
			fh = NULL;
		PRINT_FULL_DATA(stdout, "=============\nCREATE2",
			dfh, fh, name, "")
		if (res->dr_status != NFS_OK)
			(void) printf("status %d\n", res->dr_status);
	}
	dfh = NFSLOG_GET_FHANDLE2(dfh);
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(dfh, name, fhpath, "create2");
		*pathp2 = NULL;
	}

	if (res->dr_status != NFS_OK)
		/* no returned fh so nothing to add */
		return;

	/* A new file handle so add it */
	fh = NFSLOG_GET_FHANDLE2(&res->nfslog_diropres_u.dr_ok.drok_fhandle);
	if (error = FH_ADD(fhpath, dfh, fh, name)) {
		syslog(LOG_ERR, gettext(
			"Create2: Add fh for '%s' failed: %s\n"),
			    name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_REMOVE2_fhargs - if the operation succeeded, remove the link from
 * the fhtable.
 */
/* ARGSUSED */
static void
nfslog_REMOVE2_fhargs(nfslog_diropargs *args, nfsstat *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh;
	int		error;

	name = args->da_name;
	dfh = &args->da_fhandle;
	if (debug > 2) {
		PRINT_LINK_DATA(stdout, "=============\nREMOVE2", dfh, name, "")
		if (*res != NFS_OK)
			(void) printf("status %d\n", *res);
	}
	dfh = NFSLOG_GET_FHANDLE2(dfh);
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(dfh, name, fhpath, "remove2");
		*pathp2 = NULL;
	}

	if (*res != NFS_OK)
		/* remove failed so nothing to update */
		return;

	if (error = fh_remove(fhpath, dfh, name, pathp1)) {
		syslog(LOG_ERR, gettext("Remove2: '%s' failed: %s\n"),
			name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfsl_RENAME2_fhargs - updates the dfh and name fields for the given fh
 *	to change them to the new name.
 */
/* ARGSUSED */
static void
nfslog_RENAME2_fhargs(nfslog_rnmargs *args, nfsstat *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char			*from_name, *to_name;
	fhandle_t		*from_dfh, *to_dfh;
	int			error;

	from_name = args->rna_from.da_name;
	from_dfh = &args->rna_from.da_fhandle;
	to_name = args->rna_to.da_name;
	to_dfh = &args->rna_to.da_fhandle;
	if (debug > 2) {
		PRINT_LINK_DATA(stdout, "=============\nRENAME2: from",
			from_dfh, from_name, "")
		PRINT_LINK_DATA(stdout, "RENAME2: to  ", to_dfh,
			to_name, "")
		if (*res != NFS_OK)
			(void) printf("status %d\n", *res);
	}
	from_dfh = NFSLOG_GET_FHANDLE2(from_dfh);
	to_dfh = NFSLOG_GET_FHANDLE2(to_dfh);
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(from_dfh, from_name, fhpath,
			"rename2 from");
		*pathp2 = nfslog_get_path(to_dfh, to_name, fhpath,
			"rename2 to");
	}

	if (*res != NFS_OK)
		/* rename failed so nothing to update */
		return;

	/* Rename the link in the database */
	if (error = fh_rename(fhpath, from_dfh, from_name, pathp1,
			to_dfh, to_name)) {
		syslog(LOG_ERR, gettext(
			"Rename2: Update from '%s' to '%s' failed: %s\n"),
				from_name, to_name,
				((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_LINK2_fhargs - adds link name and fh to fhlist. Note that as a
 *	result we may have more than one name for an fh.
 */
/* ARGSUSED */
static void
nfslog_LINK2_fhargs(nfslog_linkargs *args, nfsstat *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh, *fh;
	int		error;

	fh = &args->la_from;
	name = args->la_to.da_name;
	dfh = &args->la_to.da_fhandle;
	if (debug > 2) {
		PRINT_FULL_DATA(stdout, "=============\nLINK2",
			dfh, fh, name, "")
		if (*res != NFS_OK)
			(void) printf("status %d\n", *res);
	}
	dfh = NFSLOG_GET_FHANDLE2(dfh);
	fh = NFSLOG_GET_FHANDLE2(fh);
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(fh, NULL, fhpath, "link2 from");
		*pathp2 = nfslog_get_path(dfh, name, fhpath, "link2 to");
	}

	if (*res != NFS_OK)
		/* no returned fh so nothing to add */
		return;

	/* A new link so add it, have fh_add find the link count */
	if (error = FH_ADD(fhpath, dfh, fh, name)) {
		syslog(LOG_ERR, gettext(
			"Link2: Add fh for '%s' failed: %s\n"),
			    name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_SYMLINK2_fhargs - adds symlink name and fh to fhlist if fh returned.
 */
/* ARGSUSED */
static void
nfslog_SYMLINK2_fhargs(nfslog_symlinkargs *args, nfsstat *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh;

	name = args->sla_from.da_name;
	dfh = &args->sla_from.da_fhandle;
	if (debug > 2) {
		PRINT_LINK_DATA(stdout, "=============\nSYMLINK2",
			dfh, name, "")
	}
	dfh = NFSLOG_GET_FHANDLE2(dfh);
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(dfh, name, fhpath, "symlink2");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_READDIR2_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_READDIR2_fhargs(nfslog_rddirargs *args, nfslog_rddirres *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nREADDIR2: fh ");
		debug_opaque_print(stdout, &args->rda_fh,
			sizeof (args->rda_fh));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE2(&args->rda_fh),
				NULL, fhpath, "readdir2");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_STATFS2_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_STATFS2_fhargs(fhandle_t *args, nfsstat *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nSTATFS2: fh ");
		debug_opaque_print(stdout, args, sizeof (*args));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE2(args),
				NULL, fhpath, "statfs2");
		*pathp2 = NULL;
	}
}

/*
 * NFS VERSION 3
 */

/* Functions for updating the fhtable for fhtoppath */

/*
 * nfslog_GETATTR3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_GETATTR3_fhargs(nfs_fh3 *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nGETATTR3: fh ");
		debug_opaque_print(stdout, args, sizeof (*args));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(args), NULL,
			fhpath, "getattr3");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_SETATTR3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_SETATTR3_fhargs(nfslog_SETATTR3args *args,	nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nSETATTR3: fh ");
		debug_opaque_print(stdout, &args->object,
			sizeof (args->object));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->object),
			NULL, fhpath, "setattr3");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_LOOKUP3_fhargs - search the table to ensure we have not added this
 * one already. Note that if the response status was anything but okay,
 * there is no fh to check...
 */
/* ARGSUSED */
static void
nfslog_LOOKUP3_fhargs(nfslog_diropargs3 *args, nfslog_LOOKUP3res *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh, *fh;

	name = args->name;
	dfh = NFSLOG_GET_FHANDLE3(&args->dir);

	if (debug > 2) {
		if (res->status == NFS3_OK)
			fh = NFSLOG_GET_FHANDLE3(
				&res->nfslog_LOOKUP3res_u.object);
		else
			fh = NULL;
		PRINT_FULL_DATA(stdout, "=============\nLOOKUP3",
			dfh, fh, name, "")
		if (res->status != NFS3_OK)
			(void) printf("status %d\n", res->status);
	}
	if ((dfh == &public_fh) && (name[0] == '\x80')) {
		/* special mclookup */
		name = &name[1];
	}
	if (res->status != NFS3_OK) {
		if (pathp1 != NULL) {
			*pathp1 = nfslog_get_path(dfh, name, fhpath, "lookup3");
			*pathp2 = NULL;
		}
		return;
	}
	fh = NFSLOG_GET_FHANDLE3(&res->nfslog_LOOKUP3res_u.object);
	nfslog_LOOKUP_calc(dfh, name, fh, fhpath, pathp1, pathp2, "Lookup3");
}

/*
 * nfslog_ACCESS3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_ACCESS3_fhargs(nfs_fh3 *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nACCESS3: fh ");
		debug_opaque_print(stdout, args,
			sizeof (*args));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(args),
			NULL, fhpath, "access3");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_READLINK3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_READLINK3_fhargs(nfs_fh3 *args, nfslog_READLINK3res *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nREADLINK3: fh ");
		debug_opaque_print(stdout, args, sizeof (*args));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(args), NULL,
			fhpath, "readlink3");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_READ3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_READ3_fhargs(nfslog_READ3args *args, nfslog_READ3res *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nREAD3: fh ");
		debug_opaque_print(stdout, &args->file,
			sizeof (args->file));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->file),
			NULL, fhpath, "read3");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_WRITE3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_WRITE3_fhargs(nfslog_WRITE3args *args, nfslog_WRITE3res *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nWRITE3: fh ");
		debug_opaque_print(stdout, &args->file,
			sizeof (args->file));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->file),
			NULL, fhpath, "write3");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_CREATE3_fhargs - if the operation succeeded, we are sure there can
 * be no such link in the fhtable, so just add it.
 */
/* ARGSUSED */
static void
nfslog_CREATE3_fhargs(nfslog_CREATE3args *args, nfslog_CREATE3res *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh, *fh;
	int		error;

	name = args->where.name;
	dfh = NFSLOG_GET_FHANDLE3(&args->where.dir);

	if (debug > 2) {
		if (res->status == NFS3_OK)
			fh = NFSLOG_GET_FHANDLE3(
				&res->nfslog_CREATE3res_u.ok.obj.handle);
		else
			fh = NULL;
		PRINT_FULL_DATA(stdout, "=============\nCREATE3",
			dfh, fh, name, "")
		if (res->status != NFS3_OK)
			(void) printf("status %d\n", res->status);
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(dfh, name, fhpath, "create3");
		*pathp2 = NULL;
	}

	if ((res->status != NFS3_OK) ||
		!res->nfslog_CREATE3res_u.ok.obj.handle_follows)
		/* no returned fh so nothing to add */
		return;

	/* A new file handle so add it */
	fh = NFSLOG_GET_FHANDLE3(&res->nfslog_CREATE3res_u.ok.obj.handle);
	if (error = FH_ADD(fhpath, dfh, fh, name)) {
		syslog(LOG_ERR, gettext(
			"Create3: Add fh for '%s' failed: %s\n"),
			    name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_MKDIR3_fhargs - if the operation succeeded, we are sure there can
 * be no such link in the fhtable, so just add it.
 */
/* ARGSUSED */
static void
nfslog_MKDIR3_fhargs(nfslog_MKDIR3args *args, nfslog_MKDIR3res *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh, *fh;
	int		error;

	name = args->where.name;
	dfh = NFSLOG_GET_FHANDLE3(&args->where.dir);

	if (debug > 2) {
		if (res->status == NFS3_OK)
			fh = NFSLOG_GET_FHANDLE3(
				&res->nfslog_MKDIR3res_u.obj.handle);
		else
			fh = NULL;
		PRINT_FULL_DATA(stdout, "=============\nMKDIR3",
			dfh, fh, name, "")
		if (res->status != NFS3_OK)
			(void) printf("status %d\n", res->status);
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(dfh, name, fhpath, "mkdir3");
		*pathp2 = NULL;
	}

	if ((res->status != NFS3_OK) ||
		!res->nfslog_MKDIR3res_u.obj.handle_follows)
		/* no returned fh so nothing to add */
		return;

	/* A new file handle so add it */
	fh = NFSLOG_GET_FHANDLE3(&res->nfslog_MKDIR3res_u.obj.handle);
	if (error = FH_ADD(fhpath, dfh, fh, name)) {
		syslog(LOG_ERR, gettext(
			"Mkdir3: Add fh for '%s' failed: %s\n"),
			    name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_REMOVE3_fhargs - if the operation succeeded, remove the link from
 * the fhtable.
 */
/* ARGSUSED */
static void
nfslog_REMOVE3_fhargs(nfslog_REMOVE3args *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh;
	int		error;

	name = args->object.name;
	dfh = NFSLOG_GET_FHANDLE3(&args->object.dir);

	if (debug > 2) {
		PRINT_LINK_DATA(stdout, "=============\nREMOVE3", dfh, name, "")
		if (*res != NFS3_OK)
			(void) printf("status %d\n", *res);
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(dfh, name, fhpath, "remove3");
		*pathp2 = NULL;
	}

	if (*res != NFS3_OK)
		/* remove failed so nothing to update */
		return;

	if (error = fh_remove(fhpath, dfh, name, pathp1)) {
		syslog(LOG_ERR, gettext("Remove3: '%s' failed: %s\n"),
			name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_RMDIR3_fhargs - if the operation succeeded, remove the link from
 * the fhtable.
 */
/* ARGSUSED */
static void
nfslog_RMDIR3_fhargs(nfslog_RMDIR3args *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh;
	int		error;

	name = args->object.name;
	dfh = NFSLOG_GET_FHANDLE3(&args->object.dir);

	if (debug > 2) {
		PRINT_LINK_DATA(stdout, "=============\nRMDIR3", dfh, name, "")
		if (*res != NFS3_OK)
			(void) printf("status %d\n", *res);
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(dfh, name, fhpath, "rmdir3");
		*pathp2 = NULL;
	}

	if (*res != NFS3_OK)
		/* rmdir failed so nothing to update */
		return;

	if (error = fh_remove(fhpath, dfh, name, pathp1)) {
		syslog(LOG_ERR, gettext("Rmdir3: '%s' failed: %s\n"),
			name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_RENAME3_fhargs - if the operation succeeded, update the existing
 * fhtable entry to point to new dir and name.
 */
/* ARGSUSED */
static void
nfslog_RENAME3_fhargs(nfslog_RENAME3args *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char			*from_name, *to_name;
	fhandle_t		*from_dfh, *to_dfh;
	int			error;

	from_name = args->from.name;
	from_dfh = NFSLOG_GET_FHANDLE3(&args->from.dir);
	to_name = args->to.name;
	to_dfh = NFSLOG_GET_FHANDLE3(&args->to.dir);

	if (debug > 2) {
		PRINT_LINK_DATA(stdout, "=============\nRENAME3: from",
			from_dfh, from_name, "")
		PRINT_LINK_DATA(stdout, "=============\nRENAME3: to  ",
			to_dfh, to_name, "")
		if (*res != NFS3_OK)
			(void) printf("status %d\n", *res);
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(from_dfh, from_name, fhpath,
			"rename3 from");
		*pathp2 = nfslog_get_path(to_dfh, to_name, fhpath,
			"rename3 to");
	}
	if (*res != NFS3_OK)
		/* rename failed so nothing to update */
		return;

	if (error = fh_rename(fhpath, from_dfh, from_name, pathp1,
			to_dfh, to_name)) {
		syslog(LOG_ERR, gettext(
			"Rename3: Update from '%s' to '%s' failed: %s\n"),
				from_name, to_name,
				((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_LINK3_fhargs - if the operation succeeded, we are sure there can
 * be no such link in the fhtable, so just add it.
 */
/* ARGSUSED */
static void
nfslog_LINK3_fhargs(nfslog_LINK3args *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char			*name;
	fhandle_t		*dfh, *fh;
	int			error;

	fh = NFSLOG_GET_FHANDLE3(&args->file);
	name = args->link.name;
	dfh = NFSLOG_GET_FHANDLE3(&args->link.dir);

	if (debug > 2) {
		PRINT_FULL_DATA(stdout, "=============\nLINK3",
			dfh, fh, name, "")
		if (*res != NFS3_OK)
			(void) printf("status %d\n", *res);
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(fh, NULL, fhpath, "link3 from");
		*pathp2 = nfslog_get_path(dfh, name, fhpath, "link3 to");
	}

	if (*res != NFS3_OK)
		/* link failed so nothing to add */
		return;

	/* A new link so add it, have fh_add find link count */
	if (error = FH_ADD(fhpath, dfh, fh, name)) {
		syslog(LOG_ERR, gettext(
			"Link3: Add fh for '%s' failed: %s\n"),
			    name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_MKNOD3_fhargs - if the operation succeeded, we are sure there can
 * be no such link in the fhtable, so just add it.
 */
/* ARGSUSED */
static void
nfslog_MKNOD3_fhargs(nfslog_MKNOD3args *args, nfslog_MKNOD3res *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh, *fh;
	int		error;

	name = args->where.name;
	dfh = NFSLOG_GET_FHANDLE3(&args->where.dir);

	if (debug > 2) {
		if (res->status == NFS3_OK)
			fh = NFSLOG_GET_FHANDLE3(
				&res->nfslog_MKNOD3res_u.obj.handle);
		else
			fh = NULL;
		PRINT_FULL_DATA(stdout, "=============\nMKNOD3",
			dfh, fh, name, "")
		if (res->status != NFS3_OK)
			(void) printf("status %d\n", res->status);
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(dfh, name, fhpath, "mknod3");
		*pathp2 = NULL;
	}
	if ((res->status != NFS3_OK) ||
		!res->nfslog_MKNOD3res_u.obj.handle_follows)
		/* no returned fh so nothing to add */
		return;

	/* A new file handle so add it */
	fh = NFSLOG_GET_FHANDLE3(&res->nfslog_MKNOD3res_u.obj.handle);
	if (error = FH_ADD(fhpath, dfh, fh, name)) {
		syslog(LOG_ERR, gettext("Mknod3: Add fh for '%s' failed: %s\n"),
			name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_SYMLINK3_fhargs - if the operation succeeded, we are sure there can
 * be no such link in the fhtable, so just add it.
 */
/* ARGSUSED */
static void
nfslog_SYMLINK3_fhargs(nfslog_SYMLINK3args *args, nfslog_SYMLINK3res *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh, *fh;
	int		error;

	name = args->where.name;
	dfh = NFSLOG_GET_FHANDLE3(&args->where.dir);

	if (debug > 2) {
		if (res->status == NFS3_OK)
			fh = NFSLOG_GET_FHANDLE3(
				&res->nfslog_SYMLINK3res_u.obj.handle);
		else
			fh = NULL;
		PRINT_FULL_DATA(stdout, "=============\nSYMLINK3",
			dfh, fh, name, "")
		if (res->status != NFS3_OK)
			(void) printf("status %d\n", res->status);
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(dfh, name, fhpath, "symlink3");
		*pathp2 = NULL;
	}

	if ((res->status != NFS3_OK) ||
		!res->nfslog_SYMLINK3res_u.obj.handle_follows)
		/* no returned fh so nothing to add */
		return;

	/* A new file handle so add it */
	fh = NFSLOG_GET_FHANDLE3(&res->nfslog_SYMLINK3res_u.obj.handle);
	if (error = FH_ADD(fhpath, dfh, fh, name)) {
		syslog(LOG_ERR, gettext(
			"Symlink3: Add fh for '%s' failed: %s\n"),
			    name, ((error >= 0) ? strerror(error) : "Unknown"));
	}
}

/*
 * nfslog_READDIR3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_READDIR3_fhargs(nfs_fh3 *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nREADDIR3: fh ");
		debug_opaque_print(stdout, args,
			sizeof (*args));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(args),
			NULL, fhpath, "readdir3");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_READDIRPLUS3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_READDIRPLUS3_fhargs(nfslog_READDIRPLUS3args *args,
	nfslog_READDIRPLUS3res *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	char		*name;
	fhandle_t	*dfh, *fh;
	nfslog_entryplus3 *ep;

	if (debug > 2) {
		(void) printf("=============\nREADDIRPLUS3: fh ");
		debug_opaque_print(stdout, &args->dir,
			sizeof (args->dir));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->dir),
			NULL, fhpath, "readdirplus3");
		*pathp2 = NULL;
	}

	if (res->status == NFS3_OK) {

		dfh = NFSLOG_GET_FHANDLE3(&args->dir);

		/*
		 * Loop through the fh/name pair and add them
		 * to the mappings.
		 */
		for (ep = res->nfslog_READDIRPLUS3res_u.ok.reply.entries;
			ep != NULL;
			ep = ep->nextentry) {

			name = ep->name;

			fh = NFSLOG_GET_FHANDLE3(&ep->name_handle.handle);

			nfslog_LOOKUP_calc(dfh, name, fh,
				fhpath, NULL, NULL,
				"ReaddirPlus3");
		}
	}
}

/*
 * nfslog_FSSTAT3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_FSSTAT3_fhargs(nfs_fh3 *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nFSSTAT3: fh ");
		debug_opaque_print(stdout, args,
			sizeof (*args));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(args), NULL,
			fhpath, "fsstat3");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_FSINFO3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_FSINFO3_fhargs(nfs_fh3 *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nFSINFO3: fh ");
		debug_opaque_print(stdout, args,
			sizeof (*args));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(args), NULL,
			fhpath, "fsinfo3");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_PATHCONF3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_PATHCONF3_fhargs(nfs_fh3 *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nPATHCONF3: fh ");
		debug_opaque_print(stdout, args,
			sizeof (*args));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(args), NULL,
			fhpath, "pathconf3");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_COMMIT3_fhargs - updates path1 but no fhtable changes
 */
/* ARGSUSED */
static void
nfslog_COMMIT3_fhargs(nfslog_COMMIT3args *args, nfsstat3 *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	if (debug > 2) {
		(void) printf("=============\nCOMMIT3: fh ");
		debug_opaque_print(stdout, &args->file,
			sizeof (args->file));
		(void) printf("\n");
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->file),
			NULL, fhpath, "commit3");
		*pathp2 = NULL;
	}
}

/*
 * NFSLOG VERSION 1
 */

/*
 * nfslog_SHARE_fhargs - adds export path and handle to fhlist
 */
/* ARGSUSED */
static void
nfslog_SHARE_fhargs(nfslog_sharefsargs *args, nfslog_sharefsres *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	fhlist_ent	fhrec;
	fhandle_t	*fh;
	int		error;

	if (debug > 2) {
		(void) printf(
			"=============\nSHARE: name '%s', fh ", args->sh_path);
		debug_opaque_print(stdout, &args->sh_fh_buf,
			sizeof (fhandle_t));
		(void) printf("\n");
	}

	fh = &args->sh_fh_buf;

	/*
	 * This bcopy is done because the fh_data for the export/share directory
	 * is not meaningful with respect to the database keys.  Therefore, we
	 * copy the export or fh_xdata fid to the fh_data so that a reasonable
	 * entry will be added in the data base.
	 */
	bcopy(fh->fh_xdata, fh->fh_data, fh->fh_xlen);

	/* If debug print the database */
	if (debug > 10) {
		fh_print_all_keys(fhpath, fh);
	}
	if (fh_lookup_link(fhpath, fh, fh,
		args->sh_path, &fhrec, &error) == NULL) {
		if (error = FH_ADD(fhpath, fh, fh, args->sh_path)) {
			syslog(LOG_ERR, gettext(
				"Share: Add fh for '%s' failed: %s\n"),
				    args->sh_path, ((error >= 0) ?
				    strerror(error) : "Unknown"));
		}
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(fh, NULL, fhpath, "share");
		*pathp2 = NULL;
	}
}

/*
 * nfslog_UNSHARE_fhargs - remove export path and handle from fhlist
 */
/* ARGSUSED */
static void
nfslog_UNSHARE_fhargs(nfslog_sharefsargs *args, nfslog_sharefsres *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	fhandle_t	*fh;
	int		error;

	if (debug > 2) {
		(void) printf("=============\nUNSHARE: name '%s', fh ",
			args->sh_path);
		debug_opaque_print(stdout, &args->sh_fh_buf,
			sizeof (fhandle_t));
		(void) printf("\n");
	}

	fh = &args->sh_fh_buf;

	/*
	 * This bcopy is done because the fh_data for the export/share directory
	 * is not meaningful with respect to the database keys.  Therefore, we
	 * copy the export or fh_xdata fid to the fh_data so that a reasonable
	 * entry will be added in the data base.
	 */
	bcopy(fh->fh_xdata, fh->fh_data, fh->fh_xlen);

	/* If debug print the database */
	if (debug > 10) {
		fh_print_all_keys(fhpath, fh);
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(fh, NULL, fhpath, "share");
		*pathp2 = NULL;
	}
	if (error = fh_remove(fhpath, fh, args->sh_path, pathp1)) {
		syslog(LOG_ERR, gettext("Unshare: '%s' failed: %s\n"),
			args->sh_path, ((error >= 0) ? strerror(error) :
			"Unknown"));
	}
}

/* ARGSUSED */
static void
nfslog_GETFH_fhargs(nfslog_getfhargs *args, nfsstat *res,
	char *fhpath, char **pathp1, char **pathp2)
{
	fhlist_ent	fhrec;
	fhandle_t	*fh;
	int		error;

	if (debug > 2) {
		(void) printf("=============\nGETFH3: name '%s', fh ",
			args->gfh_path);
		debug_opaque_print(stdout, &args->gfh_fh_buf,
			sizeof (fhandle_t));
		(void) printf("\n");
	}

	fh = &args->gfh_fh_buf;

	/* If debug print the database */
	if (debug > 10) {
		fh_print_all_keys(fhpath, fh);
	}
	if (fh_lookup_link(fhpath, fh, fh,
		args->gfh_path, &fhrec, &error) == NULL) {
		if (error = FH_ADD(fhpath, fh, fh, args->gfh_path)) {
			syslog(LOG_ERR, gettext(
				"Getfh: Add fh for '%s' failed: %s\n"),
				    args->gfh_path, ((error >= 0) ?
				    strerror(error) : "Unknown"));
		}
	}
	if (pathp1 != NULL) {
		*pathp1 = nfslog_get_path(fh, NULL, fhpath, "getfh");
		*pathp2 = NULL;
	}
}

/*
 * Exported function
 */

/*
 * nfslog_get_path - gets the path for this file. fh must be supplied,
 * name may be null. If name is supplied, fh is assumed to be a directory
 * filehandle, with name as its component. fhpath is the generic path for the
 * fhtopath table and prtstr is the name of the caller (for debug purposes).
 * Returns the malloc'd path. The caller must free it later.
 */
char *
nfslog_get_path(fhandle_t *fh, char *name, char *fhpath, char *prtstr)
{
	char	*pathp = fh_print_absolute(fhpath, fh, name);

	if (debug > 3) {
		(void) printf("   %s: path '%s', fh ", prtstr, pathp);
		debug_opaque_print(stdout, fh, sizeof (*fh));
		(void) printf("\n");
	}
	return (pathp);
}

/*
 * nfslog_process_fh_rec - updates the fh table based on the rpc req
 * Return 0 for success, error otherwise. If success return the path
 * for the input file handle(s) if so indicated.
 */
int
nfslog_process_fh_rec(struct nfslog_lr *lrp, char *fhpath, char **pathp1,
	char **pathp2, bool_t return_path)
{
	struct nfsl_fh_proc_disp	*disp;
	nfslog_request_record 		*logrec = &lrp->log_record;
	nfslog_record_header		*logrechdr = &logrec->re_header;

	if ((disp = nfslog_find_fh_dispatch(logrec)) != NULL) {
		/*
		 * Allocate space for the args and results and decode
		 */
		logrec->re_rpc_arg = calloc(1, disp->args_size);

		if (!(*disp->xdr_args)(&lrp->xdrs, logrec->re_rpc_arg)) {
			free(logrec->re_rpc_arg);
			logrec->re_rpc_arg = NULL;
			syslog(LOG_ERR, gettext("argument decode failed"));
			return (FALSE);
		}
		/* used later for free of data structures */
		lrp->xdrargs = disp->xdr_args;

		logrec->re_rpc_res = calloc(1, disp->res_size);
		if (!(*disp->xdr_res)(&lrp->xdrs, logrec->re_rpc_res)) {
			free(logrec->re_rpc_res);
			logrec->re_rpc_res = NULL;
			syslog(LOG_ERR, gettext("results decode failed"));
			return (FALSE);
		}
		/* used later for free of data structures */
		lrp->xdrres = disp->xdr_res;

		/*
		 * Process the operation within the context of the file handle
		 * mapping process
		 */
		if (return_path) {
			(*disp->nfsl_dis_args)(logrec->re_rpc_arg,
				logrec->re_rpc_res, fhpath, pathp1, pathp2);
		} else {
			if ((logrechdr->rh_version == NFS_VERSION &&
				logrechdr->rh_procnum == RFS_LINK) ||
				(logrechdr->rh_version == NFS_V3 &&
				logrechdr->rh_procnum == NFSPROC3_LINK)) {

				(*disp->nfsl_dis_args)(logrec->re_rpc_arg,
					logrec->re_rpc_res,
					fhpath,	pathp1, pathp2);
			} else {
				(*disp->nfsl_dis_args)(logrec->re_rpc_arg,
					logrec->re_rpc_res,
					fhpath, NULL, NULL);
			}
		}
		return (TRUE);
	} else {
		syslog(LOG_ERR, gettext("procedure unknown"));
		return (FALSE);
	}
}