root/usr/src/cmd/fs.d/nfs/nfslog/fhtab.c
/*
 * 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.
 */

/*
 * 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);
        }
}