# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. FSTYPE= nfs TYPEPROG= nfslogd ATTMK= $(TYPEPROG) DEFAULTFILES= nfslogd.dfl include ../../Makefile.fstype COMMON= nfslog_config.o nfslogtab.o LOCAL= process_buffer.o fhtab.o nfslogd.o nfslog_elf.o \ nfslog_trans.o nfslog_ipaddr.o readbuf.o dbtab.o \ nfs_log_xdr.o buffer_list.o OBJS= $(LOCAL) $(COMMON) SRCS= $(LOCAL:%.o=%.c) $(COMMON:%.o=../lib/%.c) LDLIBS += -lsocket -lnsl CFLAGS += $(CCVERBOSE) CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-switch CERRWARN += -Wno-type-limits # not linted SMATCH=off CPPFLAGS += -D_FILE_OFFSET_BITS=64 # # Message catalog # POFILE= nfslog.po catalog: $(POFILE) $(POFILE): $(SRCS) $(RM) $@ $(COMPILE.cpp) $(SRCS) > $(POFILE).i $(XGETTEXT) $(XGETFLAGS) $(POFILE).i sed "/^domain/d" messages.po > $@ $(RM) messages.po $(POFILE).i $(TYPEPROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) install: all $(ROOTETCDEFAULTFILES) nfslog_config.o: ../lib/nfslog_config.c $(COMPILE.c) ../lib/nfslog_config.c nfslogtab.o: ../lib/nfslogtab.c $(COMPILE.c) ../lib/nfslogtab.c clean: $(RM) $(OBJS) /* * 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. */ #include #include #include #include #include #include #include #include #include #include #include "nfslogd.h" #include "../lib/nfslogtab.h" #include "buffer_list.h" static int buildbuffer_list(struct buffer_ent **, timestruc_t *); static void free_buffer_ent(struct buffer_ent *); static struct buffer_ent *findbuffer(struct buffer_ent *, char *); static void free_sharepnt_list(struct sharepnt_ent *); static void free_sharepnt_ent(struct sharepnt_ent *); #ifdef DEBUG static void print_sharepnt_list(struct sharepnt_ent *); #endif static struct sharepnt_ent *findsharepnt(struct sharepnt_ent *, char *, struct sharepnt_ent **); /* * Builds the buffer list from NFSLOGTAB and returns it in *listpp. * Returns 0 on success, non-zero error code otherwise. */ int getbuffer_list(struct buffer_ent **listpp, timestruc_t *lu) { *listpp = NULL; return (buildbuffer_list(listpp, lu)); } /* * If NFSLOGTAB has not been modified since the last time we read it, * it simply returns the same buffer list, otherwise it re-reads NFSLOGTAB * and rebuilds the list. * No NFSLOGTAB is not treated as an error. * Returns 0 on success, non-zero error code otherwise */ int checkbuffer_list(struct buffer_ent **listpp, timestruc_t *lu) { struct stat st; int error = 0; if (stat(NFSLOGTAB, &st) == -1) { error = errno; if (error != ENOENT) { syslog(LOG_ERR, gettext("Can't stat %s - %s"), NFSLOGTAB, strerror(error)); error = 0; } return (error); } if (lu->tv_sec == st.st_mtim.tv_sec && lu->tv_nsec == st.st_mtim.tv_nsec) return (0); free_buffer_list(listpp); /* free existing list first */ return (buildbuffer_list(listpp, lu)); } /* * Does the actual work of reading NFSLOGTAB, and building the * buffer list. If *be_head already contains entries, it will * update the list with new information. * Returns 0 on success, non-zero error code otherwise. */ static int buildbuffer_list(struct buffer_ent **be_head, timestruc_t *lu) { FILE *fd; struct buffer_ent *be_tail = NULL, *bep; struct sharepnt_ent *se_tail = NULL, *sep; struct logtab_ent *lep; struct stat st; int error = 0, res; if ((fd = fopen(NFSLOGTAB, "r+")) == NULL) { error = errno; if (error != ENOENT) { syslog(LOG_ERR, gettext("%s - %s\n"), NFSLOGTAB, strerror(error)); error = 0; } return (error); } if (lockf(fileno(fd), F_LOCK, 0L) < 0) { error = errno; syslog(LOG_ERR, gettext("cannot lock %s - %s\n"), NFSLOGTAB, strerror(error)); (void) fclose(fd); return (error); } assert(*be_head == NULL); while ((res = logtab_getent(fd, &lep)) > 0) { if (bep = findbuffer(*be_head, lep->le_buffer)) { /* * Add sharepnt to buffer list */ if (sep = findsharepnt(bep->be_sharepnt, lep->le_path, &se_tail)) { /* * Sharepoint already in list, * update its state. */ sep->se_state = lep->le_state; } else { /* * Need to add to sharepoint list */ sep = (struct sharepnt_ent *) malloc(sizeof (*sep)); if (sep == NULL) { error = ENOMEM; goto errout; } (void) memset(sep, 0, sizeof (*sep)); sep->se_name = strdup(lep->le_path); if (sep->se_name == NULL) { error = ENOMEM; goto errout; } sep->se_state = lep->le_state; assert(se_tail != NULL); assert(se_tail->se_next == NULL); se_tail->se_next = sep; } } else { /* * Add new buffer to list */ bep = (struct buffer_ent *)malloc(sizeof (*bep)); if (bep == NULL) { error = ENOMEM; goto errout; } (void) memset(bep, 0, sizeof (*bep)); bep->be_name = strdup(lep->le_buffer); if (bep->be_name == NULL) { error = ENOMEM; goto errout; } if (*be_head == NULL) *be_head = bep; else be_tail->be_next = bep; be_tail = bep; bep->be_sharepnt = (struct sharepnt_ent *) malloc(sizeof (*(bep->be_sharepnt))); (void) memset(bep->be_sharepnt, 0, sizeof (*(bep->be_sharepnt))); if (bep->be_sharepnt == NULL) { error = ENOMEM; goto errout; } bep->be_sharepnt->se_name = strdup(lep->le_path); if (bep->be_sharepnt->se_name == NULL) { error = ENOMEM; goto errout; } bep->be_sharepnt->se_state = lep->le_state; } } if (res < 0) { error = EIO; goto errout; } /* * Get modification time while we have the file locked. */ if (lu) { if ((error = fstat(fileno(fd), &st)) == -1) { syslog(LOG_ERR, gettext("Can't stat %s"), NFSLOGTAB); goto errout; } *lu = st.st_mtim; } (void) fclose(fd); return (error); errout: (void) fclose(fd); if (lep) logtab_ent_free(lep); free_buffer_list(be_head); assert(*be_head == NULL); syslog(LOG_ERR, gettext("cannot read %s: %s\n"), NFSLOGTAB, strerror(error)); return (error); } /* * Removes the entry from the buffer list and frees it. */ void remove_buffer_ent(struct buffer_ent **be_listpp, struct buffer_ent *bep) { struct buffer_ent *p, *prev; for (p = prev = *be_listpp; p != NULL; p = p->be_next) { if (p == bep) { if (p == *be_listpp) *be_listpp = (*be_listpp)->be_next; else prev->be_next = bep->be_next; free_buffer_ent(bep); break; } prev = p; } } /* * Frees the buffer list. */ void free_buffer_list(struct buffer_ent **be_listpp) { struct buffer_ent *bep, *nextp; for (bep = *be_listpp; bep != NULL; bep = nextp) { nextp = bep->be_next; free_buffer_ent(bep); } *be_listpp = NULL; } static void free_buffer_ent(struct buffer_ent *bep) { assert(bep != NULL); if (debug) (void) printf("freeing %s\n", bep->be_name); if (bep->be_name != NULL) free(bep->be_name); if (bep->be_sharepnt != NULL) free_sharepnt_list(bep->be_sharepnt); free(bep); } static void free_sharepnt_list(struct sharepnt_ent *sep_listp) { struct sharepnt_ent *nextp; for (; sep_listp != NULL; sep_listp = nextp) { nextp = sep_listp->se_next; free_sharepnt_ent(sep_listp); } free(sep_listp); } /* * Removes the entry from the sharepnt list and frees it. */ void remove_sharepnt_ent(struct sharepnt_ent **se_listpp, struct sharepnt_ent *sep) { struct sharepnt_ent *p, *prev; for (p = prev = *se_listpp; p != NULL; p = p->se_next) { if (p == sep) { if (p == *se_listpp) *se_listpp = (*se_listpp)->se_next; else prev->se_next = sep->se_next; free_sharepnt_ent(sep); break; } prev = p; } } static void free_sharepnt_ent(struct sharepnt_ent *sep) { assert(sep != NULL); if (debug) (void) printf("freeing %s\n", sep->se_name); if (sep->se_name != NULL) free(sep->se_name); free(sep); } #ifdef DEBUG void printbuffer_list(struct buffer_ent *bep) { for (; bep != NULL; bep = bep->be_next) { (void) printf("%s\n", bep->be_name); if (bep->be_sharepnt != NULL) print_sharepnt_list(bep->be_sharepnt); } } static void print_sharepnt_list(struct sharepnt_ent *sep) { for (; sep != NULL; sep = sep->se_next) (void) printf("\t(%d) %s\n", sep->se_state, sep->se_name); } #endif /* * Returns a pointer to the buffer matching 'name', NULL otherwise. */ static struct buffer_ent * findbuffer(struct buffer_ent *bep, char *name) { for (; bep != NULL; bep = bep->be_next) { if (strcmp(bep->be_name, name) == 0) return (bep); } return (NULL); } /* * Returns a pointer the sharepoint entry matching 'name'. * Otherwise, it sets '*se_tail' to the last element of the list * to make insertion of new element easier, and returns NULL. */ static struct sharepnt_ent * findsharepnt( struct sharepnt_ent *sep, char *name, struct sharepnt_ent **se_tail) { struct sharepnt_ent *tail; for (; sep != NULL; sep = sep->se_next) { if (strcmp(sep->se_name, name) == 0) return (sep); tail = sep; } *se_tail = tail; return (NULL); } /* * 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. */ #ifndef _BUFFER_LIST_H #define _BUFFER_LIST_H #ifdef __cplusplus extern "C" { #endif #include /* * List of work buffers that need to be processed */ struct buffer_ent { uint_t be_flags; int be_error; /* last error reported */ char *be_name; /* work buffer path */ time_t be_lastprocessed; /* last time processed */ struct sharepnt_ent *be_sharepnt; /* share point list */ struct buffer_ent *be_next; }; /* * List of share points that refer to a given work buffer */ struct sharepnt_ent { uint_t se_flags; char *se_name; /* share point path */ int se_state; /* active or inactive? */ struct sharepnt_ent *se_next; }; #define SE_INTABLE 0x01 /* entry still in file table */ extern int getbuffer_list(struct buffer_ent **, timestruc_t *); extern int checkbuffer_list(struct buffer_ent **, timestruc_t *); extern void free_buffer_list(struct buffer_ent **); extern void remove_buffer_ent(struct buffer_ent **, struct buffer_ent *); extern void free_buffer_list(struct buffer_ent **); extern void remove_sharepnt_ent(struct sharepnt_ent **, struct sharepnt_ent *); #ifdef DEBUG extern void printbuffer_list(struct buffer_ent *); #endif #ifdef __cplusplus } #endif #endif /* _BUFFER_LIST_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Code to maintain the runtime and on-disk filehandle mapping table for * nfslog. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fhtab.h" #include "nfslogd.h" #define ROUNDUP32(val) (((val) + 3) & ~3) /* * It is important that this string not match the length of the * file handle key length NFS_FHMAXDATA */ #define DB_VERSION_STRING "NFSLOG_DB_VERSION" #define DB_VERSION "1" #define MAX_PRUNE_REC_CNT 100000 fhandle_t public_fh = { 0 }; struct db_list { fsid_t fsid; /* filesystem fsid */ char *path; /* dbm filepair path */ DBM *db; /* open dbm database */ bool_t getall; /* TRUE if all dbm for prefix open */ struct db_list *next; /* next db */ }; static struct db_list *db_fs_list = NULL; static char err_str[] = "DB I/O error has occurred"; struct link_keys { fh_secondary_key lnkey; int lnsize; struct link_keys *next; }; extern int debug; extern time_t mapping_update_interval; extern time_t prune_timeout; static int fill_link_key(char *linkkey, fhandle_t *dfh, char *name); static struct db_list *db_get_db(char *fhpath, fsid_t *fsid, int *errorp, int create_flag); static struct db_list *db_get_all_databases(char *fhpath, bool_t getall); static void debug_print_fhlist(FILE *fp, fhlist_ent *fhrecp); static void debug_print_linkinfo(FILE *fp, linkinfo_ent *fhrecp); static void debug_print_key(FILE *fp, char *str1, char *str2, char *key, int ksize); static void debug_print_key_and_data(FILE *fp, char *str1, char *str2, char *key, int ksize, char *data, int dsize); static int store_record(struct db_list *dbp, void *keyaddr, int keysize, void *dataaddr, int datasize, char *str); static void *fetch_record(struct db_list *dbp, void *keyaddr, int keysize, void *dataaddr, int *errorp, char *str); static int delete_record(struct db_list *dbp, void *keyaddr, int keysize, char *str); static int db_update_fhrec(struct db_list *dbp, void *keyaddr, int keysize, fhlist_ent *fhrecp, char *str); static int db_update_linkinfo(struct db_list *dbp, void *keyaddr, int keysize, linkinfo_ent *linkp, char *str); static fhlist_ent *create_primary_struct(struct db_list *dbp, fhandle_t *dfh, char *name, fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp, int *errorp); static fhlist_ent *db_add_primary(struct db_list *dbp, fhandle_t *dfh, char *name, fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp, int *errorp); static linkinfo_ent *get_next_link(struct db_list *dbp, char *linkkey, int *linksizep, linkinfo_ent *linkp, void **cookiep, int *errorp, char *msg); static void free_link_cookies(void *cookie); static void add_mc_path(struct db_list *dbp, fhandle_t *dfh, char *name, fhlist_ent *fhrecp, linkinfo_ent *linkp, int *errorp); static linkinfo_ent *create_link_struct(struct db_list *dbp, fhandle_t *dfh, char *name, fhlist_ent *fhrecp, int *errorp); static int db_add_secondary(struct db_list *dbp, fhandle_t *dfh, char *name, fhandle_t *fh, fhlist_ent *fhrecp); static linkinfo_ent *update_next_link(struct db_list *dbp, char *nextkey, int nextsize, char *prevkey, int prevsize, int *errorp); static int update_prev_link(struct db_list *dbp, char *nextkey, int nextsize, char *prevkey, int prevsize); static linkinfo_ent *update_linked_list(struct db_list *dbp, char *nextkey, int nextsize, char *prevkey, int prevsize, int *errorp); static int db_update_primary_new_head(struct db_list *dbp, linkinfo_ent *dellinkp, linkinfo_ent *nextlinkp, fhlist_ent *fhrecp); static int delete_link_by_key(struct db_list *dbp, char *linkkey, int *linksizep, int *errorp, char *errstr); static int delete_link(struct db_list *dbp, fhandle_t *dfh, char *name, char *nextlinkkey, int *nextlinksizep, int *errorp, char *errstr); /* * The following functions do the actual database I/O. Currently use DBM. */ /* * The "db_*" functions are functions that access the database using * database-specific calls. Currently the only database supported is * dbm. Because of the limitations of this database, in particular when * it comes to manipulating records with the same key, or using multiple keys, * the following design decisions have been made: * * Each file system has a separate dbm file, which are kept open as * accessed, listed in a linked list. * Two possible access mode are available for each file - either by * file handle, or by directory file handle and name. Since * dbm does not allow multiple keys, we will have a primary * and secondary key for each file/link. * The primary key is the pair (inode,gen) which can be obtained * from the file handle. This points to a record with * the full file handle and the secondary key (dfh-key,name) * for one of the links. * The secondary key is the pair (dfh-key,name) where dfh-key is * the primary key for the directory and the name is the * link name. It points to a record that contains the primary * key for the file and to the previous and next hard link * found for this file (if they exist). * * Summary of operations: * Adding a new file: Create the primary record and secondary (link) * record and add both to the database. The link record * would have prev and next links set to NULL. * * Adding a link to a file in the database: Add the link record, * to the head of the links list (i.e. prev = NULL, next = * secondary key recorded in the primary record). Update * the primary record to point to the new link, and the * secondary record for the old head of list to point to new. * * Deleting a file: Delete the link record. If it is the last link * then mark the primary record as deleted but don't delete * that one from the database (in case some clients still * hold the file handle). If there are other links, and the * deleted link is the head of the list (in the primary * record), update the primary record with the new head. * * Renaming a file: Add the new link and then delete the old one. * * Lookup by file handle (read, write, lookup, etc.) - fetch primary rec. * Lookup by dir info (delete, link, rename) - fetch secondary rec. * * XXX NOTE: The code is written single-threaded. To make it multi- * threaded, the following considerations must be made: * 1. Changes/access to the db list must be atomic. * 2. Changes/access for a specific file handle must be atomic * (example: deleting a link may affect up to 4 separate database * entries: the deleted link, the prev and next links if exist, * and the filehandle entry, if it points to the deleted link - * these changes must be atomic). */ /* * Create a link key given directory fh and name */ static int fill_link_key(char *linkkey, fhandle_t *dfh, char *name) { int linksize, linksize32; (void) memcpy(linkkey, &dfh->fh_data, dfh->fh_len); (void) strcpy(&linkkey[dfh->fh_len], name); linksize = dfh->fh_len + strlen(name) + 1; linksize32 = ROUNDUP32(linksize); if (linksize32 > linksize) bzero(&linkkey[linksize], linksize32 - linksize); return (linksize32); } /* * db_get_db - gets the database for the filesystem, or creates one * if none exists. Return the pointer for the database in *dbpp if success. * Return 0 for success, error code otherwise. */ static struct db_list * db_get_db(char *fhpath, fsid_t *fsid, int *errorp, int create_flag) { struct db_list *p, *newp; char fsidstr[30]; datum key, data; *errorp = 0; for (p = db_fs_list; (p != NULL) && memcmp(&p->fsid, fsid, sizeof (*fsid)); p = p->next); if (p != NULL) { /* Found it */ return (p); } /* Create it */ if ((newp = calloc(1, sizeof (*newp))) == NULL) { *errorp = errno; syslog(LOG_ERR, gettext( "db_get_db: malloc db failed: Error %s"), strerror(*errorp)); return (NULL); } (void) sprintf(fsidstr, "%08x%08x", fsid->val[0], fsid->val[1]); if ((newp->path = malloc(strlen(fhpath) + 2 + strlen(fsidstr))) == NULL) { *errorp = errno; syslog(LOG_ERR, gettext( "db_get_db: malloc dbpath failed: Error %s"), strerror(*errorp)); goto err_exit; } (void) sprintf(newp->path, "%s.%s", fhpath, fsidstr); /* * The open mode is masked by UMASK. */ if ((newp->db = dbm_open(newp->path, create_flag | O_RDWR, 0666)) == NULL) { *errorp = errno; syslog(LOG_ERR, gettext( "db_get_db: dbm_open db '%s' failed: Error %s"), newp->path, strerror(*errorp)); if (*errorp == 0) /* should not happen but may */ *errorp = -1; goto err_exit; } /* * Add the version identifier (have to check first in the * case the db exists) */ key.dptr = DB_VERSION_STRING; key.dsize = strlen(DB_VERSION_STRING); data = dbm_fetch(newp->db, key); if (data.dptr == NULL) { data.dptr = DB_VERSION; data.dsize = strlen(DB_VERSION); (void) dbm_store(newp->db, key, data, DBM_INSERT); } (void) memcpy(&newp->fsid, fsid, sizeof (*fsid)); newp->next = db_fs_list; db_fs_list = newp; if (debug > 1) { (void) printf("db_get_db: db %s opened\n", newp->path); } return (newp); err_exit: if (newp != NULL) { if (newp->db != NULL) { dbm_close(newp->db); } if (newp->path != NULL) { free(newp->path); } free(newp); } return (NULL); } /* * db_get_all_databases - gets the database for any filesystem. This is used * when any database will do - typically to retrieve the path for the * public filesystem. If any database is open - return the first one, * otherwise, search for it using fhpath. If getall is TRUE, open all * matching databases, and mark them (to indicate that all such were opened). * Return the pointer for a matching database if success. */ static struct db_list * db_get_all_databases(char *fhpath, bool_t getall) { char *dirptr, *fhdir, *fhpathname; int len, error; DIR *dirp; struct dirent *dp; fsid_t fsid; struct db_list *dbp, *ret_dbp; for (dbp = db_fs_list; dbp != NULL; dbp = dbp->next) { if (strncmp(fhpath, dbp->path, strlen(fhpath)) == 0) break; } if (dbp != NULL) { /* * if one database for that prefix is open, and either only * one is needed, or already opened all such databases, * return here without exhaustive search */ if (!getall || dbp->getall) return (dbp); } if ((fhdir = strdup(fhpath)) == NULL) { syslog(LOG_ERR, gettext( "db_get_all_databases: strdup '%s' Error '%s*'"), fhpath, strerror(errno)); return (NULL); } fhpathname = NULL; ret_dbp = NULL; if ((dirptr = strrchr(fhdir, '/')) == NULL) { /* no directory */ goto exit; } if ((fhpathname = strdup(&dirptr[1])) == NULL) { syslog(LOG_ERR, gettext( "db_get_all_databases: strdup '%s' Error '%s*'"), &dirptr[1], strerror(errno)); goto exit; } /* Terminate fhdir string at last '/' */ dirptr[1] = '\0'; /* Search the directory */ if (debug > 2) { (void) printf("db_get_all_databases: search '%s' for '%s*'\n", fhdir, fhpathname); } if ((dirp = opendir(fhdir)) == NULL) { syslog(LOG_ERR, gettext( "db_get_all_databases: opendir '%s' Error '%s*'"), fhdir, strerror(errno)); goto exit; } len = strlen(fhpathname); while ((dp = readdir(dirp)) != NULL) { if (strncmp(fhpathname, dp->d_name, len) == 0) { dirptr = &dp->d_name[len + 1]; if (*(dirptr - 1) != '.') { continue; } (void) sscanf(dirptr, "%08lx%08lx", (ulong_t *)&fsid.val[0], (ulong_t *)&fsid.val[1]); dbp = db_get_db(fhpath, &fsid, &error, 0); if (dbp != NULL) { ret_dbp = dbp; if (!getall) break; dbp->getall = TRUE; } } } (void) closedir(dirp); exit: if (fhpathname != NULL) free(fhpathname); if (fhdir != NULL) free(fhdir); return (ret_dbp); } static void debug_print_key(FILE *fp, char *str1, char *str2, char *key, int ksize) { (void) fprintf(fp, "%s: %s key (%d) ", str1, str2, ksize); debug_opaque_print(fp, key, ksize); /* may be inode,name - try to print the fields */ if (ksize >= NFS_FHMAXDATA) { (void) fprintf(fp, ": inode "); debug_opaque_print(fp, &key[2], sizeof (int)); (void) fprintf(fp, ", gen "); debug_opaque_print(fp, &key[2 + sizeof (int)], sizeof (int)); if (ksize > NFS_FHMAXDATA) { (void) fprintf(fp, ", name '%s'", &key[NFS_FHMAXDATA]); } } (void) fprintf(fp, "\n"); } static void debug_print_linkinfo(FILE *fp, linkinfo_ent *linkp) { if (linkp == NULL) return; (void) fprintf(fp, "linkinfo:\ndfh: "); debug_opaque_print(fp, (void *)&linkp->dfh, sizeof (linkp->dfh)); (void) fprintf(fp, "\nname: '%s'", LN_NAME(linkp)); (void) fprintf(fp, "\nmtime 0x%x, atime 0x%x, flags 0x%x, reclen %d\n", linkp->mtime, linkp->atime, linkp->flags, linkp->reclen); (void) fprintf(fp, "offsets: fhkey %d, name %d, next %d, prev %d\n", linkp->fhkey_offset, linkp->name_offset, linkp->next_offset, linkp->prev_offset); debug_print_key(fp, "fhkey", "", LN_FHKEY(linkp), LN_FHKEY_LEN(linkp)); debug_print_key(fp, "next", "", LN_NEXT(linkp), LN_NEXT_LEN(linkp)); debug_print_key(fp, "prev", "", LN_PREV(linkp), LN_PREV_LEN(linkp)); } static void debug_print_fhlist(FILE *fp, fhlist_ent *fhrecp) { if (fhrecp == NULL) return; (void) fprintf(fp, "fhrec:\nfh: "); debug_opaque_print(fp, (void *)&fhrecp->fh, sizeof (fhrecp->fh)); (void) fprintf(fp, "name '%s', dfh: ", fhrecp->name); debug_opaque_print(fp, (void *)&fhrecp->dfh, sizeof (fhrecp->dfh)); (void) fprintf(fp, "\nmtime 0x%x, atime 0x%x, flags 0x%x, reclen %d\n", fhrecp->mtime, fhrecp->atime, fhrecp->flags, fhrecp->reclen); } static void debug_print_key_and_data(FILE *fp, char *str1, char *str2, char *key, int ksize, char *data, int dsize) { debug_print_key(fp, str1, str2, key, ksize); (void) fprintf(fp, " ==> (%p,%d)\n", (void *)data, dsize); if (ksize > NFS_FHMAXDATA) { linkinfo_ent inf; /* probably a link struct */ (void) memcpy(&inf, data, sizeof (linkinfo_ent)); debug_print_linkinfo(fp, &inf); } else if (ksize == NFS_FHMAXDATA) { fhlist_ent inf; /* probably an fhlist struct */ (void) memcpy(&inf, data, sizeof (linkinfo_ent)); debug_print_fhlist(fp, &inf); } else { /* don't know... */ debug_opaque_print(fp, data, dsize); } } /* * store_record - store the record in the database and return 0 for success * or error code otherwise. */ static int store_record(struct db_list *dbp, void *keyaddr, int keysize, void *dataaddr, int datasize, char *str) { datum key, data; int error; char *errfmt = "store_record: dbm_store failed, Error: %s\n"; char *err; errno = 0; key.dptr = keyaddr; key.dsize = keysize; data.dptr = dataaddr; data.dsize = datasize; if (debug > 2) { debug_print_key_and_data(stdout, str, "dbm_store:\n ", key.dptr, key.dsize, data.dptr, data.dsize); } if (dbm_store(dbp->db, key, data, DBM_REPLACE) < 0) { /* Could not store */ error = dbm_error(dbp->db); dbm_clearerr(dbp->db); if (error) { if (errno) err = strerror(errno); else { err = err_str; errno = EIO; } } else { /* should not happen but sometimes does */ err = err_str; errno = -1; } if (debug) { debug_print_key(stderr, str, "store_record:" "dbm_store:\n", key.dptr, key.dsize); (void) fprintf(stderr, errfmt, err); } else syslog(LOG_ERR, gettext(errfmt), err); return (errno); } return (0); } /* * fetch_record - fetch the record from the database and return 0 for success * and errno for failure. * dataaddr is an optional valid address for the result. If dataaddr * is non-null, then that memory is already alloc'd. Else, alloc it, and * the caller must free the returned struct when done. */ static void * fetch_record(struct db_list *dbp, void *keyaddr, int keysize, void *dataaddr, int *errorp, char *str) { datum key, data; char *errfmt = "fetch_record: dbm_fetch failed, Error: %s\n"; char *err; errno = 0; *errorp = 0; key.dptr = keyaddr; key.dsize = keysize; data = dbm_fetch(dbp->db, key); if (data.dptr == NULL) { /* see if there is a database error */ if (dbm_error(dbp->db)) { /* clear and report the database error */ dbm_clearerr(dbp->db); *errorp = EIO; err = strerror(*errorp); syslog(LOG_ERR, gettext(errfmt), err); } else { /* primary record not in database */ *errorp = ENOENT; } if (debug > 3) { err = strerror(*errorp); debug_print_key(stderr, str, "fetch_record:" "dbm_fetch:\n", key.dptr, key.dsize); (void) fprintf(stderr, errfmt, err); } return (NULL); } /* copy to local struct because dbm may return non-aligned pointers */ if ((dataaddr == NULL) && ((dataaddr = malloc(data.dsize)) == NULL)) { *errorp = errno; syslog(LOG_ERR, gettext( "%s: dbm_fetch - malloc %ld: Error %s"), str, data.dsize, strerror(*errorp)); return (NULL); } (void) memcpy(dataaddr, data.dptr, data.dsize); if (debug > 3) { debug_print_key_and_data(stdout, str, "fetch_record:" "dbm_fetch:\n", key.dptr, key.dsize, dataaddr, data.dsize); } *errorp = 0; return (dataaddr); } /* * delete_record - delete the record from the database and return 0 for success * or error code for failure. */ static int delete_record(struct db_list *dbp, void *keyaddr, int keysize, char *str) { datum key; int error = 0; char *errfmt = "delete_record: dbm_delete failed, Error: %s\n"; char *err; errno = 0; key.dptr = keyaddr; key.dsize = keysize; if (debug > 2) { debug_print_key(stdout, str, "delete_record:" "dbm_delete:\n", key.dptr, key.dsize); } if (dbm_delete(dbp->db, key) < 0) { error = dbm_error(dbp->db); dbm_clearerr(dbp->db); if (error) { if (errno) err = strerror(errno); else { err = err_str; errno = EIO; } } else { /* should not happen but sometimes does */ err = err_str; errno = -1; } if (debug) { debug_print_key(stderr, str, "delete_record:" "dbm_delete:\n", key.dptr, key.dsize); (void) fprintf(stderr, errfmt, err); } else syslog(LOG_ERR, gettext(errfmt), err); } return (errno); } /* * db_update_fhrec - puts fhrec in db with updated atime if more than * mapping_update_interval seconds passed. Return 0 if success, error otherwise. */ static int db_update_fhrec(struct db_list *dbp, void *keyaddr, int keysize, fhlist_ent *fhrecp, char *str) { time_t cur_time = time(0); if (difftime(cur_time, fhrecp->atime) >= mapping_update_interval) { fhrecp->atime = cur_time; return (store_record(dbp, keyaddr, keysize, fhrecp, fhrecp->reclen, str)); } return (0); } /* * db_update_linkinfo - puts linkinfo in db with updated atime if more than * mapping_update_interval seconds passed. Return 0 if success, error otherwise. */ static int db_update_linkinfo(struct db_list *dbp, void *keyaddr, int keysize, linkinfo_ent *linkp, char *str) { time_t cur_time = time(0); if (difftime(cur_time, linkp->atime) >= mapping_update_interval) { linkp->atime = cur_time; return (store_record(dbp, keyaddr, keysize, linkp, linkp->reclen, str)); } return (0); } /* * create_primary_struct - add primary record to the database. * Database must be open when this function is called. * If success, return the added database entry. fhrecp may be used to * provide an existing memory area, else malloc it. If failed, *errorp * contains the error code and return NULL. */ static fhlist_ent * create_primary_struct(struct db_list *dbp, fhandle_t *dfh, char *name, fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp, int *errorp) { int reclen, reclen1; fhlist_ent *new_fhrecp = fhrecp; reclen1 = offsetof(fhlist_ent, name) + strlen(name) + 1; reclen = ROUNDUP32(reclen1); if (fhrecp == NULL) { /* allocated the memory */ if ((new_fhrecp = malloc(reclen)) == NULL) { *errorp = errno; syslog(LOG_ERR, gettext( "create_primary_struct: malloc %d Error %s"), reclen, strerror(*errorp)); return (NULL); } } /* Fill in the fields */ (void) memcpy(&new_fhrecp->fh, fh, sizeof (*fh)); (void) memcpy(&new_fhrecp->dfh, dfh, sizeof (*dfh)); new_fhrecp->flags = flags; if (dfh == &public_fh) new_fhrecp->flags |= PUBLIC_PATH; else new_fhrecp->flags &= ~PUBLIC_PATH; new_fhrecp->mtime = time(0); new_fhrecp->atime = new_fhrecp->mtime; (void) strcpy(new_fhrecp->name, name); if (reclen1 < reclen) { bzero((char *)((uintptr_t)new_fhrecp + reclen1), reclen - reclen1); } new_fhrecp->reclen = reclen; *errorp = store_record(dbp, &fh->fh_data, fh->fh_len, new_fhrecp, new_fhrecp->reclen, "create_primary_struct"); if (*errorp != 0) { /* Could not store */ if (fhrecp == NULL) /* caller did not supply pointer */ free(new_fhrecp); return (NULL); } return (new_fhrecp); } /* * db_add_primary - add primary record to the database. * If record already in and live, return it (even if for a different link). * If in database but marked deleted, replace it. If not in database, add it. * Database must be open when this function is called. * If success, return the added database entry. fhrecp may be used to * provide an existing memory area, else malloc it. If failed, *errorp * contains the error code and return NULL. */ static fhlist_ent * db_add_primary(struct db_list *dbp, fhandle_t *dfh, char *name, fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp, int *errorp) { fhlist_ent *new_fhrecp; fh_primary_key fhkey; if (debug > 2) (void) printf("db_add_primary entered: name '%s'\n", name); bcopy(&fh->fh_data, fhkey, fh->fh_len); new_fhrecp = fetch_record(dbp, fhkey, fh->fh_len, (void *)fhrecp, errorp, "db_add_primary"); if (new_fhrecp != NULL) { /* primary record is in the database */ /* Update atime if needed */ *errorp = db_update_fhrec(dbp, fhkey, fh->fh_len, new_fhrecp, "db_add_primary put fhrec"); if (debug > 2) (void) printf("db_add_primary exits(2): name '%s'\n", name); return (new_fhrecp); } /* primary record not in database - create it */ new_fhrecp = create_primary_struct(dbp, dfh, name, fh, flags, fhrecp, errorp); if (new_fhrecp == NULL) { /* Could not store */ if (debug > 2) (void) printf( "db_add_primary exits(1): name '%s' Error %s\n", name, ((*errorp >= 0) ? strerror(*errorp) : "Unknown")); return (NULL); } if (debug > 2) (void) printf("db_add_primary exits(0): name '%s'\n", name); return (new_fhrecp); } /* * get_next_link - get and check the next link in the chain. * Re-use space if linkp param non-null. Also set *linkkey and *linksizep * to values for next link (*linksizep set to 0 if last link). * cookie is used to detect corrupted link entries XXXXXXX * Return the link pointer or NULL if none. */ static linkinfo_ent * get_next_link(struct db_list *dbp, char *linkkey, int *linksizep, linkinfo_ent *linkp, void **cookiep, int *errorp, char *msg) { int linksize, nextsize; char *nextkey; linkinfo_ent *new_linkp = linkp; struct link_keys *lnp; linksize = *linksizep; if (linksize == 0) return (NULL); *linksizep = 0; new_linkp = fetch_record(dbp, linkkey, linksize, (void *)linkp, errorp, msg); if (new_linkp == NULL) return (NULL); /* Set linkkey to point to next record */ nextsize = LN_NEXT_LEN(new_linkp); if (nextsize == 0) return (new_linkp); /* Add this key to the cookie list */ if ((lnp = malloc(sizeof (struct link_keys))) == NULL) { syslog(LOG_ERR, gettext("get_next_key: malloc error %s\n"), strerror(errno)); if ((new_linkp != NULL) && (linkp == NULL)) free(new_linkp); return (NULL); } (void) memcpy(lnp->lnkey, linkkey, linksize); lnp->lnsize = linksize; lnp->next = *(struct link_keys **)cookiep; *cookiep = (void *)lnp; /* Make sure record does not point to itself or other internal loops */ nextkey = LN_NEXT(new_linkp); for (; lnp != NULL; lnp = lnp->next) { if ((nextsize == lnp->lnsize) && (memcmp( lnp->lnkey, nextkey, nextsize) == 0)) { /* * XXX This entry's next pointer points to * itself. This is only a work-around, remove * this check once bug 4203186 is fixed. */ if (debug) { (void) fprintf(stderr, "%s: get_next_link: last record invalid.\n", msg); debug_print_key_and_data(stderr, msg, "invalid rec:\n ", linkkey, linksize, (char *)new_linkp, new_linkp->reclen); } /* Return as if this is the last link */ return (new_linkp); } } (void) memcpy(linkkey, nextkey, nextsize); *linksizep = nextsize; return (new_linkp); } /* * free_link_cookies - free the cookie list */ static void free_link_cookies(void *cookie) { struct link_keys *dellnp, *lnp; lnp = (struct link_keys *)cookie; while (lnp != NULL) { dellnp = lnp; lnp = lnp->next; free(dellnp); } } /* * add_mc_path - add a mc link to a file that has other links. Add it at end * of linked list. Called when it's known there are other links. */ static void add_mc_path(struct db_list *dbp, fhandle_t *dfh, char *name, fhlist_ent *fhrecp, linkinfo_ent *linkp, int *errorp) { fh_secondary_key linkkey; int linksize, len; linkinfo_ent lastlink, *lastlinkp; void *cookie; linksize = fill_link_key(linkkey, &fhrecp->dfh, fhrecp->name); cookie = NULL; do { lastlinkp = get_next_link(dbp, linkkey, &linksize, &lastlink, &cookie, errorp, "add_mc_path"); } while (linksize > 0); free_link_cookies(cookie); /* reached end of list */ if (lastlinkp == NULL) { /* nothing to do */ if (debug > 1) { (void) fprintf(stderr, "add_mc_path link is null\n"); } return; } /* Add new link after last link */ /* * next - link key for the next in the list - add at end so null. * prev - link key for the previous link in the list. */ linkp->prev_offset = linkp->next_offset; /* aligned */ linksize = fill_link_key(LN_PREV(linkp), &lastlinkp->dfh, LN_NAME(lastlinkp)); linkp->reclen = linkp->prev_offset + linksize; /* aligned */ /* Add the link information to the database */ linksize = fill_link_key(linkkey, dfh, name); *errorp = store_record(dbp, linkkey, linksize, linkp, linkp->reclen, "add_mc_path"); if (*errorp != 0) return; /* Now update previous last link to point forward to new link */ /* Copy prev link out since it's going to be overwritten */ linksize = LN_PREV_LEN(lastlinkp); (void) memcpy(linkkey, LN_PREV(lastlinkp), linksize); /* Update previous last link to point to new one */ len = fill_link_key(LN_NEXT(lastlinkp), dfh, name); lastlinkp->prev_offset = lastlinkp->next_offset + len; /* aligned */ (void) memcpy(LN_PREV(lastlinkp), linkkey, linksize); lastlinkp->reclen = lastlinkp->prev_offset + linksize; /* Update the link information to the database */ linksize = fill_link_key(linkkey, &lastlinkp->dfh, LN_NAME(lastlinkp)); *errorp = store_record(dbp, linkkey, linksize, lastlinkp, lastlinkp->reclen, "add_mc_path prev"); } /* * create_link_struct - create the secondary struct. * (dfh,name) is the secondary key, fhrec is the primary record for the file * and linkpp is a place holder for the record (could be null). * Insert the record to the database. * Return 0 if success, error otherwise. */ static linkinfo_ent * create_link_struct(struct db_list *dbp, fhandle_t *dfh, char *name, fhlist_ent *fhrecp, int *errorp) { fh_secondary_key linkkey; int len, linksize; linkinfo_ent *linkp; if ((linkp = malloc(sizeof (linkinfo_ent))) == NULL) { *errorp = errno; syslog(LOG_ERR, gettext( "create_link_struct: malloc failed: Error %s"), strerror(*errorp)); return (NULL); } if (dfh == &public_fh) linkp->flags |= PUBLIC_PATH; else linkp->flags &= ~PUBLIC_PATH; (void) memcpy(&linkp->dfh, dfh, sizeof (*dfh)); linkp->mtime = time(0); linkp->atime = linkp->mtime; /* Calculate offsets of variable fields */ /* fhkey - primary key (inode/gen) */ /* name - component name (in directory dfh) */ linkp->fhkey_offset = ROUNDUP32(offsetof(linkinfo_ent, varbuf)); len = fill_link_key(LN_FHKEY(linkp), &fhrecp->fh, name); linkp->name_offset = linkp->fhkey_offset + fhrecp->fh.fh_len; linkp->next_offset = linkp->fhkey_offset + len; /* aligned */ /* * next - link key for the next link in the list - NULL if it's * the first link. If this is the public fs, only one link allowed. * Avoid setting a multi-component path as primary path, * unless no choice. */ len = 0; if (memcmp(&fhrecp->dfh, dfh, sizeof (*dfh)) || strcmp(fhrecp->name, name)) { /* different link than the one that's in the record */ if (dfh == &public_fh) { /* parent is public fh - either multi-comp or root */ if (memcmp(&fhrecp->fh, &public_fh, sizeof (public_fh))) { /* multi-comp path */ add_mc_path(dbp, dfh, name, fhrecp, linkp, errorp); if (*errorp != 0) { free(linkp); return (NULL); } return (linkp); } } else { /* new link to a file with a different one already */ len = fill_link_key(LN_NEXT(linkp), &fhrecp->dfh, fhrecp->name); } } /* * prev - link key for the previous link in the list - since we * always insert at the front of the list, it's always initially NULL. */ linkp->prev_offset = linkp->next_offset + len; /* aligned */ linkp->reclen = linkp->prev_offset; /* Add the link information to the database */ linksize = fill_link_key(linkkey, dfh, name); *errorp = store_record(dbp, linkkey, linksize, linkp, linkp->reclen, "create_link_struct"); if (*errorp != 0) { free(linkp); return (NULL); } return (linkp); } /* * db_add_secondary - add secondary record to the database (for the directory * information). * Assumes this is a new link, not yet in the database, and that the primary * record is already in. * If fhrecp is non-null, then fhrecp is the primary record. * Database must be open when this function is called. * Return 0 if success, error code otherwise. */ static int db_add_secondary(struct db_list *dbp, fhandle_t *dfh, char *name, fhandle_t *fh, fhlist_ent *fhrecp) { int nextsize, len, error; linkinfo_ent nextlink, *newlinkp, *nextlinkp; uint_t fhflags; char *nextaddr; fhlist_ent *new_fhrecp = fhrecp; fh_primary_key fhkey; if (debug > 2) (void) printf("db_add_secondary entered: name '%s'\n", name); bcopy(&fh->fh_data, fhkey, fh->fh_len); if (fhrecp == NULL) { /* Fetch the primary record */ new_fhrecp = fetch_record(dbp, fhkey, fh->fh_len, NULL, &error, "db_add_secondary primary"); if (new_fhrecp == NULL) { return (error); } } /* Update fhrec atime if needed */ error = db_update_fhrec(dbp, fhkey, fh->fh_len, new_fhrecp, "db_add_secondary primary"); fhflags = new_fhrecp->flags; /* now create and insert the secondary record */ newlinkp = create_link_struct(dbp, dfh, name, new_fhrecp, &error); if (fhrecp == NULL) { free(new_fhrecp); new_fhrecp = NULL; } if (newlinkp == NULL) { if (debug > 2) (void) printf("create_link_struct '%s' Error %s\n", name, ((error >= 0) ? strerror(error) : "Unknown")); return (error); } nextsize = LN_NEXT_LEN(newlinkp); if (nextsize == 0) { /* No next - can exit now */ if (debug > 2) (void) printf("db_add_secondary: no next link\n"); free(newlinkp); return (0); } /* * Update the linked list to point to new head: replace head of * list in the primary record, then update previous secondary record * to point to new head */ new_fhrecp = create_primary_struct(dbp, dfh, name, fh, fhflags, new_fhrecp, &error); if (new_fhrecp == NULL) { if (debug > 2) (void) printf( "db_add_secondary: replace primary failed\n"); free(newlinkp); return (error); } else if (fhrecp == NULL) { free(new_fhrecp); } /* * newlink is the new head of the list, with its "next" pointing to * the old head, and its "prev" pointing to NULL. We now need to * modify the "next" entry to have its "prev" point to the new entry. */ nextaddr = LN_NEXT(newlinkp); if (debug > 2) { debug_print_key(stderr, "db_add_secondary", "next key\n ", nextaddr, nextsize); } /* Get the next link entry from the database */ nextlinkp = fetch_record(dbp, nextaddr, nextsize, (void *)&nextlink, &error, "db_add_secondary next link"); if (nextlinkp == NULL) { if (debug > 2) (void) printf( "db_add_secondary: fetch next link failed\n"); free(newlinkp); return (error); } /* * since the "prev" field is the only field to be changed, and it's * the last in the link record, we only need to modify it (and reclen). * Re-use link to update the next record. */ len = fill_link_key(LN_PREV(nextlinkp), dfh, name); nextlinkp->reclen = nextlinkp->prev_offset + len; error = store_record(dbp, nextaddr, nextsize, nextlinkp, nextlinkp->reclen, "db_add_secondary"); if (debug > 2) (void) printf( "db_add_secondary exits(%d): name '%s'\n", error, name); free(newlinkp); return (error); } /* * Update the next link to point to the new prev. * Return 0 for success, error code otherwise. * If successful, and nextlinkpp is non-null, * *nextlinkpp contains the record for the next link, since * we may will it if the primary record should be updated. */ static linkinfo_ent * update_next_link(struct db_list *dbp, char *nextkey, int nextsize, char *prevkey, int prevsize, int *errorp) { linkinfo_ent *nextlinkp, *linkp1; if ((nextlinkp = malloc(sizeof (linkinfo_ent))) == NULL) { *errorp = errno; syslog(LOG_ERR, gettext( "update_next_link: malloc next Error %s"), strerror(*errorp)); return (NULL); } linkp1 = nextlinkp; nextlinkp = fetch_record(dbp, nextkey, nextsize, nextlinkp, errorp, "update next"); /* if there is no next record - ok */ if (nextlinkp == NULL) { /* Return no error */ *errorp = 0; free(linkp1); return (NULL); } /* Set its prev to the prev of the deleted record */ nextlinkp->reclen = ROUNDUP32(nextlinkp->reclen - LN_PREV_LEN(nextlinkp) + prevsize); /* Change the len and set prev */ if (prevsize > 0) { (void) memcpy(LN_PREV(nextlinkp), prevkey, prevsize); } /* No other changes needed because prev is last field */ *errorp = store_record(dbp, nextkey, nextsize, nextlinkp, nextlinkp->reclen, "update_next"); if (*errorp != 0) { free(nextlinkp); nextlinkp = NULL; } return (nextlinkp); } /* * Update the prev link to point to the new next. * Return 0 for success, error code otherwise. */ static int update_prev_link(struct db_list *dbp, char *nextkey, int nextsize, char *prevkey, int prevsize) { linkinfo_ent prevlink, *prevlinkp; int diff, error; /* Update its next to the given one */ prevlinkp = fetch_record(dbp, prevkey, prevsize, &prevlink, &error, "update prev"); /* if error there is no next record - ok */ if (prevlinkp == NULL) { return (0); } diff = nextsize - LN_NEXT_LEN(prevlinkp); prevlinkp->reclen = ROUNDUP32(prevlinkp->reclen + diff); /* Change the len and set next - may push prev */ if (diff != 0) { char *ptr = LN_PREV(prevlinkp); prevlinkp->prev_offset += diff; (void) memcpy(LN_PREV(prevlinkp), ptr, LN_PREV_LEN(prevlinkp)); } if (nextsize > 0) { (void) memcpy(LN_NEXT(prevlinkp), nextkey, nextsize); } /* Store updated record */ error = store_record(dbp, prevkey, prevsize, prevlinkp, prevlinkp->reclen, "update_prev"); return (error); } /* * update_linked_list - update the next link to point back to prev, and vice * versa. Normally called by delete_link to drop the deleted link from the * linked list of hard links for the file. next and prev are the keys of next * and previous links for the deleted link in the list (could be NULL). * Return 0 for success, error code otherwise. * If successful, and nextlinkpp is non-null, * return the record for the next link, since * if the primary record should be updated we'll need it. In this case, * actually allocate the space for it because we can't tell otherwise. */ static linkinfo_ent * update_linked_list(struct db_list *dbp, char *nextkey, int nextsize, char *prevkey, int prevsize, int *errorp) { linkinfo_ent *nextlinkp = NULL; *errorp = 0; if (nextsize > 0) { nextlinkp = update_next_link(dbp, nextkey, nextsize, prevkey, prevsize, errorp); if (nextlinkp == NULL) { /* not an error if no next link */ if (*errorp != 0) { if (debug > 1) { (void) fprintf(stderr, "update_next_link Error %s\n", ((*errorp >= 0) ? strerror(*errorp) : "Unknown")); } return (NULL); } } } if (prevsize > 0) { *errorp = update_prev_link(dbp, nextkey, nextsize, prevkey, prevsize); if (*errorp != 0) { if (debug > 1) { (void) fprintf(stderr, "update_prev_link Error %s\n", ((*errorp >= 0) ? strerror(*errorp) : "Unknown")); } if (nextlinkp != NULL) free(nextlinkp); nextlinkp = NULL; } } return (nextlinkp); } /* * db_update_primary_new_head - Update a primary record that the head of * the list is deleted. Similar to db_add_primary, but the primary record * must exist, and is always replaced with one pointing to the new link, * unless it does not point to the deleted link. If the link we deleted * was the last link, the delete the primary record as well. * Return 0 for success, error code otherwise. */ static int db_update_primary_new_head(struct db_list *dbp, linkinfo_ent *dellinkp, linkinfo_ent *nextlinkp, fhlist_ent *fhrecp) { int error; char *name, *next_name; fhandle_t *dfh; fh_primary_key fhkey; dfh = &dellinkp->dfh; name = LN_NAME(dellinkp); /* If the deleted link was not the head of the list, we are done */ if (memcmp(&fhrecp->dfh, dfh, sizeof (*dfh)) || strcmp(fhrecp->name, name)) { /* should never be here... */ if (debug > 1) { (void) fprintf(stderr, "db_update_primary_new_head: primary " "is for [%s,", name); debug_opaque_print(stderr, (void *)dfh, sizeof (*dfh)); (void) fprintf(stderr, "], not [%s,", fhrecp->name); debug_opaque_print(stderr, (void *)&fhrecp->dfh, sizeof (fhrecp->dfh)); (void) fprintf(stderr, "]\n"); } return (0); /* not head of list so done */ } /* Set the head to nextkey if exists. Otherwise, mark file as deleted */ bcopy(&fhrecp->fh.fh_data, fhkey, fhrecp->fh.fh_len); if (nextlinkp == NULL) { /* last link */ /* remove primary record from database */ (void) delete_record(dbp, fhkey, fhrecp->fh.fh_len, "db_update_primary_new_head: fh delete"); return (0); } else { /* * There are still "live" links, so update the primary record. */ next_name = LN_NAME(nextlinkp); fhrecp->reclen = ROUNDUP32(offsetof(fhlist_ent, name) + strlen(next_name) + 1); /* Replace link data with the info for the next link */ (void) memcpy(&fhrecp->dfh, &nextlinkp->dfh, sizeof (nextlinkp->dfh)); (void) strcpy(fhrecp->name, next_name); } /* not last link */ fhrecp->mtime = time(0); fhrecp->atime = fhrecp->mtime; error = store_record(dbp, fhkey, fhrecp->fh.fh_len, fhrecp, fhrecp->reclen, "db_update_primary_new_head: fh"); return (error); } /* * Exported functions */ /* * db_add - add record to the database. If dfh, fh and name are all here, * add both primary and secondary records. If fh is not available, don't * add anything... * Assumes this is a new file, not yet in the database and that the record * for fh is already in. * Return 0 for success, error code otherwise. */ int db_add(char *fhpath, fhandle_t *dfh, char *name, fhandle_t *fh, uint_t flags) { struct db_list *dbp = NULL; fhlist_ent fhrec, *fhrecp; int error = 0; if (fh == NULL) { /* nothing to add */ return (EINVAL); } if (fh == &public_fh) { dbp = db_get_all_databases(fhpath, FALSE); } else { dbp = db_get_db(fhpath, &fh->fh_fsid, &error, O_CREAT); } for (; dbp != NULL; dbp = ((fh != &public_fh) ? NULL : dbp->next)) { if (debug > 3) { (void) printf("db_add: name '%s', db '%s'\n", name, dbp->path); } fhrecp = db_add_primary(dbp, dfh, name, fh, flags, &fhrec, &error); if (fhrecp == NULL) { continue; } if ((dfh == NULL) || (name == NULL)) { /* Can't add link information */ syslog(LOG_ERR, gettext( "db_add: dfh %p, name %p - invalid"), (void *)dfh, (void *)name); error = EINVAL; continue; } if (fh == &public_fh) { while ((fhrecp != NULL) && strcmp(name, fhrecp->name)) { /* Replace the public fh rather than add link */ error = db_delete_link(fhpath, dfh, fhrecp->name); fhrecp = db_add_primary(dbp, dfh, name, fh, flags, &fhrec, &error); } if (fhrecp == NULL) { continue; } } error = db_add_secondary(dbp, dfh, name, fh, fhrecp); if (fhrecp != &fhrec) { free(fhrecp); } } return (error); } /* * db_lookup - search the database for the file identified by fh. * Return the entry in *fhrecpp if found, or NULL with error set otherwise. */ fhlist_ent * db_lookup(char *fhpath, fhandle_t *fh, fhlist_ent *fhrecp, int *errorp) { struct db_list *dbp; fh_primary_key fhkey; if ((fhpath == NULL) || (fh == NULL) || (errorp == NULL)) { if (errorp != NULL) *errorp = EINVAL; return (NULL); } *errorp = 0; if (fh == &public_fh) { dbp = db_get_all_databases(fhpath, FALSE); } else { dbp = db_get_db(fhpath, &fh->fh_fsid, errorp, O_CREAT); } if (dbp == NULL) { /* Could not get or create database */ return (NULL); } bcopy(&fh->fh_data, fhkey, fh->fh_len); fhrecp = fetch_record(dbp, fhkey, fh->fh_len, fhrecp, errorp, "db_lookup"); /* Update fhrec atime if needed */ if (fhrecp != NULL) { *errorp = db_update_fhrec(dbp, fhkey, fh->fh_len, fhrecp, "db_lookup"); } return (fhrecp); } /* * db_lookup_link - search the database for the file identified by (dfh,name). * If the link was found, use it to search for the primary record. * Return 0 and set the entry in *fhrecpp if found, return error otherwise. */ fhlist_ent * db_lookup_link(char *fhpath, fhandle_t *dfh, char *name, fhlist_ent *fhrecp, int *errorp) { struct db_list *dbp; fh_secondary_key linkkey; linkinfo_ent *linkp; int linksize, fhkeysize; char *fhkey; if ((fhpath == NULL) || (dfh == NULL) || (name == NULL) || (errorp == NULL)) { if (errorp != NULL) *errorp = EINVAL; return (NULL); } *errorp = 0; if (dfh == &public_fh) { dbp = db_get_all_databases(fhpath, FALSE); } else { dbp = db_get_db(fhpath, &dfh->fh_fsid, errorp, O_CREAT); } if (dbp == NULL) { /* Could not get or create database */ return (NULL); } /* Get the link record */ linksize = fill_link_key(linkkey, dfh, name); linkp = fetch_record(dbp, linkkey, linksize, NULL, errorp, "db_lookup_link link"); if (linkp != NULL) { /* Now use link to search for fh entry */ fhkeysize = LN_FHKEY_LEN(linkp); fhkey = LN_FHKEY(linkp); fhrecp = fetch_record(dbp, fhkey, fhkeysize, (void *)fhrecp, errorp, "db_lookup_link fh"); /* Update fhrec atime if needed */ if (fhrecp != NULL) { *errorp = db_update_fhrec(dbp, fhkey, fhkeysize, fhrecp, "db_lookup_link fhrec"); } /* Update link atime if needed */ *errorp = db_update_linkinfo(dbp, linkkey, linksize, linkp, "db_lookup_link link"); free(linkp); } else { fhrecp = NULL; } return (fhrecp); } /* * delete_link - delete the requested link from the database. If it's the * last link in the database for that file then remove the primary record * as well. *errorp contains the returned error code. * Return ENOENT if link not in database and 0 otherwise. */ static int delete_link_by_key(struct db_list *dbp, char *linkkey, int *linksizep, int *errorp, char *errstr) { int nextsize, prevsize, fhkeysize, linksize; char *nextkey, *prevkey, *fhkey; linkinfo_ent *dellinkp, *nextlinkp; fhlist_ent *fhrecp, fhrec; *errorp = 0; linksize = *linksizep; /* Get the link record */ dellinkp = fetch_record(dbp, linkkey, linksize, NULL, errorp, errstr); if (dellinkp == NULL) { /* * Link not in database. */ if (debug > 2) { debug_print_key(stderr, errstr, "link not in database\n", linkkey, linksize); } *linksizep = 0; return (ENOENT); } /* * Possibilities: * 1. Normal case - only one link to delete: the link next and * prev should be NULL, and fhrec's name/dfh are same * as the link. Remove the link and fhrec. * 2. Multiple hard links, and the deleted link is the head of * the list. Remove the link and replace the link key in * the primary record to point to the new head. * 3. Multiple hard links, and the deleted link is not the * head of the list (not the same as in fhrec) - just * delete the link and update the previous and next records * in the links linked list. */ /* Get next and prev keys for linked list updates */ nextsize = LN_NEXT_LEN(dellinkp); nextkey = ((nextsize > 0) ? LN_NEXT(dellinkp) : NULL); prevsize = LN_PREV_LEN(dellinkp); prevkey = ((prevsize > 0) ? LN_PREV(dellinkp) : NULL); /* Update the linked list for the file */ nextlinkp = update_linked_list(dbp, nextkey, nextsize, prevkey, prevsize, errorp); if ((nextlinkp == NULL) && (*errorp != 0)) { free(dellinkp); *linksizep = 0; return (0); } /* Delete link record */ *errorp = delete_record(dbp, linkkey, linksize, errstr); /* Get the primary key */ fhkeysize = LN_FHKEY_LEN(dellinkp); fhkey = LN_FHKEY(dellinkp); fhrecp = fetch_record(dbp, fhkey, fhkeysize, &fhrec, errorp, errstr); if (fhrecp == NULL) { /* Should never happen */ if (debug > 1) { debug_print_key(stderr, errstr, "fetch primary for ", linkkey, linksize); (void) fprintf(stderr, " Error %s\n", ((*errorp >= 0) ? strerror(*errorp) : "Unknown")); } } else if ((*errorp == 0) && (prevsize <= 0)) { /* This is the head of the list update primary record */ *errorp = db_update_primary_new_head(dbp, dellinkp, nextlinkp, fhrecp); } else { /* Update fhrec atime if needed */ *errorp = db_update_fhrec(dbp, fhkey, fhkeysize, fhrecp, errstr); } *linksizep = nextsize; if (nextsize > 0) (void) memcpy(linkkey, nextkey, nextsize); if (nextlinkp != NULL) free(nextlinkp); free(dellinkp); return (0); } /* * delete_link - delete the requested link from the database. If it's the * last link in the database for that file then remove the primary record * as well. If nextlinkkey/sizep are non-null, copy the key and key size of * the next link in the chain into them (this would save a dbm_fetch op). * Return ENOENT if link not in database and 0 otherwise, with *errorp * containing the returned error if any from the delete_link ops. */ static int delete_link(struct db_list *dbp, fhandle_t *dfh, char *name, char *nextlinkkey, int *nextlinksizep, int *errorp, char *errstr) { int linkerr; *errorp = 0; if ((nextlinkkey != NULL) && (nextlinksizep != NULL)) { *nextlinksizep = fill_link_key(nextlinkkey, dfh, name); linkerr = delete_link_by_key(dbp, nextlinkkey, nextlinksizep, errorp, errstr); } else { int linksize; fh_secondary_key linkkey; linksize = fill_link_key(linkkey, dfh, name); linkerr = delete_link_by_key(dbp, linkkey, &linksize, errorp, errstr); } return (linkerr); } /* * db_delete_link - search the database for the file system for link. * Delete the link from the database. If this is the "primary" link, * set the primary record for the next link. If it's the last one, * delete the primary record. * Return 0 for success, error code otherwise. */ int db_delete_link(char *fhpath, fhandle_t *dfh, char *name) { struct db_list *dbp; int error = 0; if ((fhpath == NULL) || (dfh == NULL) || (name == NULL)) { return (EINVAL); } if (dfh == &public_fh) { dbp = db_get_all_databases(fhpath, TRUE); } else { dbp = db_get_db(fhpath, &dfh->fh_fsid, &error, O_CREAT); } for (; dbp != NULL; dbp = ((dfh == &public_fh) ? dbp->next : NULL)) { (void) delete_link(dbp, dfh, name, NULL, NULL, &error, "db_delete_link link"); } return (error); } #ifdef DEBUG /* * db_delete - Deletes the fhrec corresponding to the fh. Use only * for repairing the fhtable, not for normal handling. * Return 0 for success, error code otherwise. */ int db_delete(char *fhpath, fhandle_t *fh) { struct db_list *dbp; int error = 0; if ((fhpath == NULL) || (fh == NULL)) { return (EINVAL); } if (fh == &public_fh) { dbp = db_get_all_databases(fhpath, TRUE); } else { dbp = db_get_db(fhpath, &fh->fh_fsid, &error, O_CREAT); } for (; dbp != NULL; dbp = ((fh == &public_fh) ? dbp->next : NULL)) { /* Get the link record */ (void) delete_record(dbp, &fh->fh_data, fh->fh_len, "db_delete: fh delete"); } return (error); } #endif /* DEBUG */ /* * db_rename_link - search the database for the file system for link. * Add the new link and delete the old link from the database. * Return 0 for success, error code otherwise. */ int db_rename_link(char *fhpath, fhandle_t *from_dfh, char *from_name, fhandle_t *to_dfh, char *to_name) { int error; struct db_list *dbp; fhlist_ent fhrec, *fhrecp; if ((fhpath == NULL) || (from_dfh == NULL) || (from_name == NULL) || (to_dfh == NULL) || (to_name == NULL)) { return (EINVAL); } if (from_dfh == &public_fh) { dbp = db_get_all_databases(fhpath, FALSE); } else { dbp = db_get_db(fhpath, &from_dfh->fh_fsid, &error, O_CREAT); } for (; dbp != NULL; dbp = ((from_dfh != &public_fh) ? NULL : dbp->next)) { /* find existing link */ fhrecp = db_lookup_link(fhpath, from_dfh, from_name, &fhrec, &error); if (fhrecp == NULL) { /* Could not find the link */ continue; } /* Delete the old link (if last primary record not deleted) */ error = db_delete_link(fhpath, from_dfh, from_name); if (error == 0) { error = db_add(fhpath, to_dfh, to_name, &fhrecp->fh, fhrecp->flags); } } return (error); } /* * db_print_all_keys: prints all keys for a given filesystem. If fsidp is * NULL, print for all filesystems covered by fhpath. */ void db_print_all_keys(char *fhpath, fsid_t *fsidp, FILE *fp) { struct db_list *dbp; datum key; int error, len; char strkey[NFS_FHMAXDATA + MAXNAMELEN]; db_record rec; void *ptr; if ((fhpath == NULL) || ((fsidp != NULL) && (fsidp == &public_fh.fh_fsid))) return; if (fsidp == NULL) { (void) db_get_all_databases(fhpath, TRUE); dbp = db_fs_list; } else { dbp = db_get_db(fhpath, fsidp, &error, 0); } if (dbp == NULL) { /* Could not get or create database */ return; } len = strlen(fhpath); for (; dbp != NULL; dbp = ((fsidp != NULL) ? NULL : dbp->next)) { if (strncmp(fhpath, dbp->path, len)) continue; (void) fprintf(fp, "\nStart print database for fsid 0x%x 0x%x\n", dbp->fsid.val[0], dbp->fsid.val[1]); (void) fprintf(fp, "=============================\n"); for (key = dbm_firstkey(dbp->db); key.dptr != NULL; key = dbm_nextkey(dbp->db)) { (void) memcpy(strkey, key.dptr, key.dsize); debug_print_key(fp, "", "", strkey, key.dsize); if (debug < 2) continue; ptr = fetch_record(dbp, key.dptr, key.dsize, (void *)&rec, &error, "db_prt_keys"); if (ptr == NULL) continue; if (key.dsize == NFS_FHMAXDATA) { /* fhrec */ debug_print_fhlist(fp, &rec.fhlist_rec); } else if (key.dsize > NFS_FHMAXDATA) { /* linkinfo */ debug_print_linkinfo(fp, &rec.link_rec); } (void) fprintf(fp, "-----------------------------\n"); } (void) fprintf(fp, "End print database for fsid 0x%x 0x%x\n", dbp->fsid.val[0], dbp->fsid.val[1]); } } void debug_opaque_print(FILE *fp, void *buf, int size) { int bufoffset = 0; char debug_str[200]; if ((buf == NULL) || (size <= 0)) return; nfslog_opaque_print_buf(buf, size, debug_str, &bufoffset, 200); (void) fprintf(fp, debug_str); } /* * links_timedout() takes a primary records and searches all of its * links to see if they all have access times that are older than * the 'prune_timeout' value. TRUE if all links are old and FALSE * if there is just one link that has an access time which is recent. */ static int links_timedout(struct db_list *pdb, fhlist_ent *pfe, time_t ts) { fh_secondary_key linkkey; linkinfo_ent *linkp, link_st; int error; int linksize; void *cookie; /* Get the link record */ linksize = fill_link_key(linkkey, &pfe->dfh, pfe->name); cookie = NULL; do { linkp = get_next_link(pdb, linkkey, &linksize, &link_st, &cookie, &error, "links_timedout"); if ((linkp != NULL) && (difftime(ts, linkp->atime) <= prune_timeout)) { /* update primary record to have an uptodate time */ pfe = fetch_record(pdb, (void *)&pfe->fh.fh_data, pfe->fh.fh_len, NULL, &error, "links_timedout"); if (pfe == NULL) { syslog(LOG_ERR, gettext( "links_timedout: fetch fhrec error %s\n"), strerror(error)); } else { if (difftime(pfe->atime, linkp->atime) < 0) { /* update fhrec atime */ pfe->atime = linkp->atime; (void) store_record(pdb, (void *)&pfe->fh.fh_data, pfe->fh.fh_len, pfe, pfe->reclen, "links_timedout"); } free(pfe); } free_link_cookies(cookie); return (FALSE); } } while (linksize > 0); free_link_cookies(cookie); return (TRUE); } /* * prune_dbs() will search all of the open databases looking for records * that have not been accessed in the last 'prune_timeout' seconds. * This search is done on the primary records and a list of potential * timeout candidates is built. The reason for doing this is to not * disturb the underlying dbm_firstkey()/dbm_nextkey() sequence; we * want to search all of the records in the database. * Once we have our candidate list built, we examine each of those * item's links to check if the links have been accessed within the * 'prune_timeout' seconds. If neither the primary nor any its links * have been accessed, then all of those records are removed/deleted * from the database. */ int prune_dbs(char *fhpath) { struct db_list *pdb; datum key; db_record *ptr; struct fhlist_ent *pfe; int error, linkerr, linksize; time_t cur_time = time(0); fh_secondary_key linkkey; struct thelist { struct thelist *next; db_record *ptr; } thelist, *ptl; int cnt = 0; if (fhpath != NULL) (void) db_get_all_databases(fhpath, TRUE); thelist.next = NULL; /* * Search each of the open databases */ for (pdb = db_fs_list; pdb; pdb = pdb->next) { do { /* Check each record in the database */ for (key = dbm_firstkey(pdb->db); key.dptr != NULL; key = dbm_nextkey(pdb->db)) { /* We're only interested in primary records */ if (key.dsize != NFS_FHMAXDATA) continue; /* probably a link record */ ptr = fetch_record(pdb, key.dptr, key.dsize, NULL, &error, "dump_db"); if (ptr == NULL) continue; /* * If this record is a primary record and it is * not an export point or a public file handle path, * check it for a ancient access time. */ if ((ptr->fhlist_rec.flags & (EXPORT_POINT | PUBLIC_PATH)) || (difftime(cur_time, ptr->fhlist_rec.atime) <= prune_timeout)) { /* Keep this record in the database */ free(ptr); } else { /* Found one? Save off info about it */ ptl = malloc(sizeof (struct thelist)); if (ptl == NULL) { syslog(LOG_ERR, gettext( "prune_dbs: malloc failed, error %s\n"), strerror(errno)); break; } ptl->ptr = ptr; ptl->next = thelist.next; thelist.next = ptl; cnt++; /* count how many records allocated */ if (cnt > MAX_PRUNE_REC_CNT) { /* Limit number of records malloc'd */ if (debug) (void) fprintf(stderr, "prune_dbs: halt search - too many records\n"); break; } } } /* * Take the saved records and check their links to make * sure that they have not been accessed as well. */ for (ptl = thelist.next; ptl; ptl = thelist.next) { thelist.next = ptl->next; /* Everything timed out? */ pfe = &(ptl->ptr->fhlist_rec); if (links_timedout(pdb, pfe, cur_time)) { /* * Iterate until we run out of links. * We have to do this since there can be * multiple links to a primary record and * we need to delete one at a time. */ /* Delete the link and get the next */ linkerr = delete_link(pdb, &pfe->dfh, pfe->name, linkkey, &linksize, &error, "dump_db"); while ((linksize > 0) && !(error || linkerr)) { /* Delete the link and get the next */ linkerr = delete_link_by_key(pdb, linkkey, &linksize, &error, "dump_db"); if (error || linkerr) { break; } } if (linkerr) { /* link not in database, primary is */ /* Should never happen */ if (debug > 1) { (void) fprintf(stderr, "prune_dbs: Error primary exists "); debug_opaque_print(stderr, (void *)&pfe->fh, sizeof (pfe->fh)); (void) fprintf(stderr, "\n"); } if (debug) syslog(LOG_ERR, gettext( "prune_dbs: Error primary exists\n")); (void) delete_record(pdb, &pfe->fh.fh_data, pfe->fh.fh_len, "prune_dbs: fh delete"); } } /* Make sure to free the pointers used in the list */ free(ptl->ptr); free(ptl); cnt--; } thelist.next = NULL; } while (key.dptr != NULL); } return (0); } /* * 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 #include #include #include #include #include #include #include #include #include #include #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); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FHTAB_H #define _FHTAB_H /* * Support for the fh mapping file for nfslog. */ #ifdef __cplusplus extern "C" { #endif /* * RPC dispatch table for file handles * Indexed by program, version, proc * Based on NFS dispatch table. * Differences: no xdr of args/res. */ struct nfsl_fh_proc_disp { void (*nfsl_dis_args)(); /* dispatch routine for proc */ bool_t (*xdr_args)(); /* XDR function for arguments */ bool_t (*xdr_res)(); /* XDR function for results */ int args_size; /* size of arguments struct */ int res_size; /* size of results struct */ }; struct nfsl_fh_vers_disp { int nfsl_dis_nprocs; /* number of procs */ struct nfsl_fh_proc_disp *nfsl_dis_proc_table; /* proc array */ }; struct nfsl_fh_prog_disp { int nfsl_dis_prog; /* program number */ int nfsl_dis_versmin; /* minimum version number */ int nfsl_dis_nvers; /* number of version values */ struct nfsl_fh_vers_disp *nfsl_dis_vers_table; /* versions array */ }; /* key comprised of inode/gen, currenly 8 or 10 bytes */ #define PRIMARY_KEY_LEN_MAX 16 typedef char fh_primary_key[PRIMARY_KEY_LEN_MAX]; /* link key - directory primary key plus name (upto 2 components) */ #define SECONDARY_KEY_LEN_MAX (PRIMARY_KEY_LEN_MAX + MAXPATHLEN) typedef char fh_secondary_key[SECONDARY_KEY_LEN_MAX]; /* * This is the runtime filehandle table entry. Because an fhandle_t is * used for both Version 2 and Version 3, we don't need two different types * of entries in the table. */ typedef struct fhlist_ent { fhandle_t fh; /* filehandle for this component */ time32_t mtime; /* modification time of entry */ time32_t atime; /* access time of entry */ fhandle_t dfh; /* parent filehandle for this component */ ushort_t flags; short reclen; /* length of record */ char name[MAXPATHLEN]; /* variable record */ } fhlist_ent; /* flags values */ #define EXPORT_POINT 0x01 /* if this is export point */ #define NAME_DELETED 0x02 /* is the dir info still valid for this fh? */ #define PUBLIC_PATH 0x04 /* is the dir info still valid for this fh? */ /* * Information maintained for the secondary key * Note that this is a variable length record with 4 variable size fields: * fhkey - primary key (must be there) * name - component name (must be there) * next - next link in list (could be null) * prev - previous link in list (could be null) */ #define MAX_LINK_VARBUF (3 * SECONDARY_KEY_LEN_MAX) typedef struct linkinfo_ent { fhandle_t dfh; /* directory filehandle */ time32_t mtime; /* modification time of entry */ time32_t atime; /* access time of entry */ ushort_t flags; short reclen; /* Actual record length */ short fhkey_offset; /* offset of fhkey, from head of record */ short name_offset; /* offset of name */ short next_offset; /* offset of next link key */ short prev_offset; /* offset of prev link key */ char varbuf[MAX_LINK_VARBUF]; /* max size for above */ } linkinfo_ent; /* Macros for lengths of the various fields */ #define LN_FHKEY_LEN(link) ((link)->name_offset - (link)->fhkey_offset) #define LN_NAME_LEN(link) ((link)->next_offset - (link)->name_offset) #define LN_NEXT_LEN(link) ((link)->prev_offset - (link)->next_offset) #define LN_PREV_LEN(link) ((link)->reclen - (link)->prev_offset) /* Macros for address of the various fields */ #define LN_FHKEY(link) (char *)((uintptr_t)(link) + (link)->fhkey_offset) #define LN_NAME(link) (char *)((uintptr_t)(link) + (link)->name_offset) #define LN_NEXT(link) (char *)((uintptr_t)(link) + (link)->next_offset) #define LN_PREV(link) (char *)((uintptr_t)(link) + (link)->prev_offset) /* Which record can reside in database */ typedef union { fhlist_ent fhlist_rec; linkinfo_ent link_rec; } db_record; void debug_opaque_print(FILE *, void *buf, int size); int db_add(char *fhpath, fhandle_t *dfh, char *name, fhandle_t *fh, uint_t flags); fhlist_ent *db_lookup(char *fhpath, fhandle_t *fh, fhlist_ent *fhrecp, int *errorp); fhlist_ent *db_lookup_link(char *fhpath, fhandle_t *dfh, char *name, fhlist_ent *fhrecp, int *errorp); int db_delete(char *fhpath, fhandle_t *fh); int db_delete_link(char *fhpath, fhandle_t *dfh, char *name); int db_rename_link(char *fhpath, fhandle_t *from_dfh, char *from_name, fhandle_t *to_dfh, char *to_name); void db_print_all_keys(char *fhpath, fsid_t *fsidp, FILE *fp); char *nfslog_get_path(fhandle_t *fh, char *name, char *fhpath, char *prtstr); extern fhandle_t public_fh; /* * Macro to determine which fhandle to use - input or public fh */ #define NFSLOG_GET_FHANDLE2(fh) \ (((fh)->fh_len > 0) ? fh : &public_fh) /* * Macro to determine which fhandle to use - input or public fh */ #define NFSLOG_GET_FHANDLE3(fh3) \ (((fh3)->fh3_length == sizeof (fhandle_t)) ? \ (fhandle_t *)&(fh3)->fh3_u.data : &public_fh) #ifdef __cplusplus } #endif #endif /* _FHTAB_H */ /* * 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. */ #include #include static bool_t xdr_timestruc32_t(XDR *, timestruc32_t *); static bool_t xdr_nfs2_timeval(XDR *, nfs2_timeval *); static bool_t xdr_ftype3(XDR *, ftype3 *); static bool_t xdr_stable_how(XDR *, stable_how *); static bool_t xdr_createmode3(XDR *, createmode3 *); static bool_t xdr_size3(XDR *, size3 *); static bool_t xdr_count3(XDR *, count3 *); static bool_t xdr_set_size3(XDR *, set_size3 *); static bool_t xdr_offset3(XDR *, offset3 *); static bool_t xdr_post_op_fh3(XDR *, post_op_fh3 *); static bool_t xdr_nfsreadargs(XDR *, struct nfsreadargs *); static bool_t xdr_nfslog_record_header(XDR *, nfslog_record_header *); static bool_t xdr_nfslog_drok(XDR *, nfslog_drok *); static bool_t xdr_nfslog_rrok(XDR *, nfslog_rrok *); static bool_t xdr_nfslog_sattr(XDR *, nfslog_sattr *); static bool_t xdr_nfslog_rdok(XDR *, nfslog_rdok *); static bool_t xdr_nfslog_createhow3(XDR *, nfslog_createhow3 *); static bool_t xdr_nfslog_CREATE3resok(XDR *, nfslog_CREATE3resok *); static bool_t xdr_nfslog_READ3resok(XDR *, nfslog_READ3resok *); static bool_t xdr_nfslog_WRITE3resok(XDR *, nfslog_WRITE3resok *); static bool_t xdr_nfslog_entryplus3(XDR *, nfslog_entryplus3 *); static bool_t xdr_nfslog_dirlistplus3(XDR *, nfslog_dirlistplus3 *); static bool_t xdr_nfslog_READDIRPLUS3resok(XDR *, nfslog_READDIRPLUS3resok *); static bool_t xdr_timestruc32_t(XDR *xdrs, timestruc32_t *objp) { if (!xdr_int(xdrs, &objp->tv_sec)) return (FALSE); if (!xdr_int(xdrs, &objp->tv_nsec)) return (FALSE); return (TRUE); } static bool_t xdr_nfs2_timeval(XDR *xdrs, nfs2_timeval *objp) { if (!xdr_u_int(xdrs, &objp->tv_sec)) return (FALSE); if (!xdr_u_int(xdrs, &objp->tv_usec)) return (FALSE); return (TRUE); } bool_t xdr_nfsstat(XDR *xdrs, nfsstat *objp) { if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_uint64(XDR *xdrs, uint64 *objp) { return (xdr_u_longlong_t(xdrs, objp)); } bool_t xdr_uint32(XDR *xdrs, uint32 *objp) { return (xdr_u_int(xdrs, objp)); } static bool_t xdr_ftype3(XDR *xdrs, ftype3 *objp) { return (xdr_enum(xdrs, (enum_t *)objp)); } static bool_t xdr_stable_how(XDR *xdrs, stable_how *objp) { return (xdr_enum(xdrs, (enum_t *)objp)); } static bool_t xdr_createmode3(XDR *xdrs, createmode3 *objp) { return (xdr_enum(xdrs, (enum_t *)objp)); } static bool_t xdr_size3(XDR *xdrs, size3 *objp) { return (xdr_uint64(xdrs, objp)); } static bool_t xdr_count3(XDR *xdrs, count3 *objp) { return (xdr_uint32(xdrs, objp)); } static bool_t xdr_set_size3(XDR *xdrs, set_size3 *objp) { if (!xdr_bool(xdrs, &objp->set_it)) return (FALSE); switch (objp->set_it) { case TRUE: if (!xdr_size3(xdrs, &objp->size)) return (FALSE); break; } return (TRUE); } static bool_t xdr_offset3(XDR *xdrs, offset3 *objp) { return (xdr_uint64(xdrs, objp)); } bool_t xdr_fhandle(XDR *xdrs, fhandle_t *fh) { if (xdrs->x_op == XDR_FREE) return (TRUE); return (xdr_opaque(xdrs, (caddr_t)fh, NFS_FHSIZE)); } bool_t xdr_nfs_fh3(XDR *xdrs, nfs_fh3 *objp) { if (!xdr_u_int(xdrs, &objp->fh3_length)) return (FALSE); if (objp->fh3_length > NFS3_FHSIZE) return (FALSE); if (xdrs->x_op == XDR_DECODE || xdrs->x_op == XDR_ENCODE) return (xdr_opaque(xdrs, objp->fh3_u.data, objp->fh3_length)); if (xdrs->x_op == XDR_FREE) return (TRUE); return (FALSE); } static bool_t xdr_post_op_fh3(XDR *xdrs, post_op_fh3 *objp) { if (!xdr_bool(xdrs, &objp->handle_follows)) return (FALSE); switch (objp->handle_follows) { case TRUE: if (!xdr_nfs_fh3(xdrs, &objp->handle)) return (FALSE); break; case FALSE: break; default: return (FALSE); } return (TRUE); } bool_t xdr_nfsstat3(XDR *xdrs, nfsstat3 *objp) { return (xdr_enum(xdrs, (enum_t *)objp)); } static bool_t xdr_nfsreadargs(XDR *xdrs, struct nfsreadargs *ra) { if (xdr_fhandle(xdrs, &ra->ra_fhandle) && xdr_u_int(xdrs, &ra->ra_offset) && xdr_u_int(xdrs, &ra->ra_count) && xdr_u_int(xdrs, &ra->ra_totcount)) { return (TRUE); } return (FALSE); } bool_t xdr_nfslog_buffer_header(XDR *xdrs, nfslog_buffer_header *objp) { if (!xdr_u_int(xdrs, &objp->bh_length)) return (FALSE); if (!xdr_rpcvers(xdrs, &objp->bh_version)) return (FALSE); if (objp->bh_version > 1) { if (!xdr_u_longlong_t(xdrs, &objp->bh_offset)) return (FALSE); if (!xdr_u_int(xdrs, &objp->bh_flags)) return (FALSE); } else { uint_t bh_offset; if (!xdr_u_int(xdrs, &objp->bh_flags)) return (FALSE); if (xdrs->x_op == XDR_ENCODE) bh_offset = (uint_t)objp->bh_offset; if (!xdr_u_int(xdrs, &bh_offset)) return (FALSE); if (xdrs->x_op == XDR_DECODE) objp->bh_offset = (u_offset_t)bh_offset; } if (!xdr_timestruc32_t(xdrs, &objp->bh_timestamp)) return (FALSE); return (TRUE); } static bool_t xdr_nfslog_record_header(XDR *xdrs, nfslog_record_header *objp) { if (!xdr_u_int(xdrs, &objp->rh_reclen)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rh_rec_id)) return (FALSE); if (!xdr_rpcprog(xdrs, &objp->rh_prognum)) return (FALSE); if (!xdr_rpcproc(xdrs, &objp->rh_procnum)) return (FALSE); if (!xdr_rpcvers(xdrs, &objp->rh_version)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rh_auth_flavor)) return (FALSE); if (!xdr_timestruc32_t(xdrs, &objp->rh_timestamp)) return (FALSE); if (!xdr_uid_t(xdrs, &objp->rh_uid)) return (FALSE); if (!xdr_gid_t(xdrs, &objp->rh_gid)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_request_record(XDR *xdrs, nfslog_request_record *objp) { if (!xdr_nfslog_record_header(xdrs, &objp->re_header)) return (FALSE); if (!xdr_string(xdrs, &objp->re_principal_name, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->re_netid, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->re_tag, ~0)) return (FALSE); if (!xdr_netbuf(xdrs, &objp->re_ipaddr)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_sharefsargs(XDR *xdrs, nfslog_sharefsargs *objp) { if (!xdr_int(xdrs, &objp->sh_flags)) return (FALSE); if (!xdr_u_int(xdrs, &objp->sh_anon)) return (FALSE); if (!xdr_string(xdrs, &objp->sh_path, ~0)) return (FALSE); if (!xdr_fhandle(xdrs, &objp->sh_fh_buf)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_sharefsres(XDR *xdrs, nfslog_sharefsres *objp) { if (!xdr_nfsstat(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_getfhargs(XDR *xdrs, nfslog_getfhargs *objp) { if (!xdr_fhandle(xdrs, &objp->gfh_fh_buf)) return (FALSE); if (!xdr_string(xdrs, &objp->gfh_path, ~0)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_diropargs(XDR *xdrs, nfslog_diropargs *objp) { if (!xdr_fhandle(xdrs, &objp->da_fhandle)) return (FALSE); if (!xdr_string(xdrs, &objp->da_name, ~0)) return (FALSE); return (TRUE); } static bool_t xdr_nfslog_drok(XDR *xdrs, nfslog_drok *objp) { if (!xdr_fhandle(xdrs, &objp->drok_fhandle)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_diropres(XDR *xdrs, nfslog_diropres *objp) { if (!xdr_nfsstat(xdrs, &objp->dr_status)) return (FALSE); switch (objp->dr_status) { case NFS_OK: if (!xdr_nfslog_drok(xdrs, &objp->nfslog_diropres_u.dr_ok)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_nfsreadargs(XDR *xdrs, nfslog_nfsreadargs *objp) { if (!xdr_nfsreadargs(xdrs, objp)) return (FALSE); return (TRUE); } static bool_t xdr_nfslog_rrok(XDR *xdrs, nfslog_rrok *objp) { if (!xdr_u_int(xdrs, &objp->filesize)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rrok_count)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_rdresult(XDR *xdrs, nfslog_rdresult *objp) { if (!xdr_nfsstat(xdrs, &objp->r_status)) return (FALSE); switch (objp->r_status) { case NFS_OK: if (!xdr_nfslog_rrok(xdrs, &objp->nfslog_rdresult_u.r_ok)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_writeargs(XDR *xdrs, nfslog_writeargs *objp) { if (!xdr_fhandle(xdrs, &objp->waargs_fhandle)) return (FALSE); if (!xdr_u_int(xdrs, &objp->waargs_begoff)) return (FALSE); if (!xdr_u_int(xdrs, &objp->waargs_offset)) return (FALSE); if (!xdr_u_int(xdrs, &objp->waargs_totcount)) return (FALSE); if (!xdr_u_int(xdrs, &objp->waargs_count)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_writeresult(XDR *xdrs, nfslog_writeresult *objp) { if (!xdr_nfsstat(xdrs, &objp->wr_status)) return (FALSE); switch (objp->wr_status) { case NFS_OK: if (!xdr_u_int(xdrs, &objp->nfslog_writeresult_u.wr_size)) return (FALSE); break; } return (TRUE); } static bool_t xdr_nfslog_sattr(XDR *xdrs, nfslog_sattr *objp) { if (!xdr_u_int(xdrs, &objp->sa_mode)) return (FALSE); if (!xdr_u_int(xdrs, &objp->sa_uid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->sa_gid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->sa_size)) return (FALSE); if (!xdr_nfs2_timeval(xdrs, (nfs2_timeval *)&objp->sa_atime)) return (FALSE); if (!xdr_nfs2_timeval(xdrs, (nfs2_timeval *)&objp->sa_mtime)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_createargs(XDR *xdrs, nfslog_createargs *objp) { if (!xdr_nfslog_sattr(xdrs, &objp->ca_sa)) return (FALSE); if (!xdr_nfslog_diropargs(xdrs, &objp->ca_da)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_setattrargs(XDR *xdrs, nfslog_setattrargs *objp) { if (!xdr_fhandle(xdrs, &objp->saa_fh)) return (FALSE); if (!xdr_nfslog_sattr(xdrs, &objp->saa_sa)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_rdlnres(XDR *xdrs, nfslog_rdlnres *objp) { if (!xdr_nfsstat(xdrs, &objp->rl_status)) return (FALSE); switch (objp->rl_status) { case NFS_OK: if (!xdr_string(xdrs, &objp->nfslog_rdlnres_u.rl_ok, ~0)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_rnmargs(XDR *xdrs, nfslog_rnmargs *objp) { if (!xdr_nfslog_diropargs(xdrs, &objp->rna_from)) return (FALSE); if (!xdr_nfslog_diropargs(xdrs, &objp->rna_to)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_linkargs(XDR *xdrs, nfslog_linkargs *objp) { if (!xdr_fhandle(xdrs, &objp->la_from)) return (FALSE); if (!xdr_nfslog_diropargs(xdrs, &objp->la_to)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_symlinkargs(XDR *xdrs, nfslog_symlinkargs *objp) { if (!xdr_nfslog_diropargs(xdrs, &objp->sla_from)) return (FALSE); if (!xdr_string(xdrs, &objp->sla_tnm, ~0)) return (FALSE); if (!xdr_nfslog_sattr(xdrs, &objp->sla_sa)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_rddirargs(XDR *xdrs, nfslog_rddirargs *objp) { if (!xdr_fhandle(xdrs, &objp->rda_fh)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rda_offset)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rda_count)) return (FALSE); return (TRUE); } static bool_t xdr_nfslog_rdok(XDR *xdrs, nfslog_rdok *objp) { if (!xdr_u_int(xdrs, &objp->rdok_offset)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rdok_size)) return (FALSE); if (!xdr_bool(xdrs, &objp->rdok_eof)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_rddirres(XDR *xdrs, nfslog_rddirres *objp) { if (!xdr_nfsstat(xdrs, &objp->rd_status)) return (FALSE); switch (objp->rd_status) { case NFS_OK: if (!xdr_nfslog_rdok(xdrs, &objp->nfslog_rddirres_u.rd_ok)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_diropargs3(XDR *xdrs, nfslog_diropargs3 *objp) { if (!xdr_nfs_fh3(xdrs, &objp->dir)) return (FALSE); if (!xdr_string(xdrs, &objp->name, ~0)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_LOOKUP3res(XDR *xdrs, nfslog_LOOKUP3res *objp) { if (!xdr_nfsstat3(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: if (!xdr_nfs_fh3(xdrs, &objp->nfslog_LOOKUP3res_u.object)) return (FALSE); break; } return (TRUE); } static bool_t xdr_nfslog_createhow3(XDR *xdrs, nfslog_createhow3 *objp) { if (!xdr_createmode3(xdrs, &objp->mode)) return (FALSE); switch (objp->mode) { case UNCHECKED: case GUARDED: if (!xdr_set_size3(xdrs, &objp->nfslog_createhow3_u.size)) return (FALSE); break; case EXCLUSIVE: break; default: return (FALSE); } return (TRUE); } bool_t xdr_nfslog_CREATE3args(XDR *xdrs, nfslog_CREATE3args *objp) { if (!xdr_nfslog_diropargs3(xdrs, &objp->where)) return (FALSE); if (!xdr_nfslog_createhow3(xdrs, &objp->how)) return (FALSE); return (TRUE); } static bool_t xdr_nfslog_CREATE3resok(XDR *xdrs, nfslog_CREATE3resok *objp) { if (!xdr_post_op_fh3(xdrs, &objp->obj)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_CREATE3res(XDR *xdrs, nfslog_CREATE3res *objp) { if (!xdr_nfsstat3(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: if (!xdr_nfslog_CREATE3resok( xdrs, &objp->nfslog_CREATE3res_u.ok)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_SETATTR3args(XDR *xdrs, nfslog_SETATTR3args *objp) { if (!xdr_nfs_fh3(xdrs, &objp->object)) return (FALSE); if (!xdr_set_size3(xdrs, &objp->size)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_READLINK3res(XDR *xdrs, nfslog_READLINK3res *objp) { if (!xdr_nfsstat3(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: if (!xdr_string(xdrs, &objp->nfslog_READLINK3res_u.data, ~0)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_READ3args(XDR *xdrs, nfslog_READ3args *objp) { if (!xdr_nfs_fh3(xdrs, &objp->file)) return (FALSE); if (!xdr_offset3(xdrs, &objp->offset)) return (FALSE); if (!xdr_count3(xdrs, &objp->count)) return (FALSE); return (TRUE); } static bool_t xdr_nfslog_READ3resok(XDR *xdrs, nfslog_READ3resok *objp) { if (!xdr_size3(xdrs, &objp->filesize)) return (FALSE); if (!xdr_count3(xdrs, &objp->count)) return (FALSE); if (!xdr_bool(xdrs, &objp->eof)) return (FALSE); if (!xdr_u_int(xdrs, &objp->size)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_READ3res(XDR *xdrs, nfslog_READ3res *objp) { if (!xdr_nfsstat3(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: if (!xdr_nfslog_READ3resok(xdrs, &objp->nfslog_READ3res_u.ok)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_WRITE3args(XDR *xdrs, nfslog_WRITE3args *objp) { if (!xdr_nfs_fh3(xdrs, &objp->file)) return (FALSE); if (!xdr_offset3(xdrs, &objp->offset)) return (FALSE); if (!xdr_count3(xdrs, &objp->count)) return (FALSE); if (!xdr_stable_how(xdrs, &objp->stable)) return (FALSE); return (TRUE); } static bool_t xdr_nfslog_WRITE3resok(XDR *xdrs, nfslog_WRITE3resok *objp) { if (!xdr_size3(xdrs, &objp->filesize)) return (FALSE); if (!xdr_count3(xdrs, &objp->count)) return (FALSE); if (!xdr_stable_how(xdrs, &objp->committed)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_WRITE3res(XDR *xdrs, nfslog_WRITE3res *objp) { if (!xdr_nfsstat3(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: if (!xdr_nfslog_WRITE3resok(xdrs, &objp->nfslog_WRITE3res_u.ok)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_MKDIR3args(XDR *xdrs, nfslog_MKDIR3args *objp) { if (!xdr_nfslog_diropargs3(xdrs, &objp->where)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_MKDIR3res(XDR *xdrs, nfslog_MKDIR3res *objp) { if (!xdr_nfsstat3(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: if (!xdr_post_op_fh3(xdrs, &objp->nfslog_MKDIR3res_u.obj)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_SYMLINK3args(XDR *xdrs, nfslog_SYMLINK3args *objp) { if (!xdr_nfslog_diropargs3(xdrs, &objp->where)) return (FALSE); if (!xdr_string(xdrs, &objp->symlink_data, ~0)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_SYMLINK3res(XDR *xdrs, nfslog_SYMLINK3res *objp) { if (!xdr_nfsstat3(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: if (!xdr_post_op_fh3(xdrs, &objp->nfslog_SYMLINK3res_u.obj)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_MKNOD3args(XDR *xdrs, nfslog_MKNOD3args *objp) { if (!xdr_nfslog_diropargs3(xdrs, &objp->where)) return (FALSE); if (!xdr_ftype3(xdrs, &objp->type)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_MKNOD3res(XDR *xdrs, nfslog_MKNOD3res *objp) { if (!xdr_nfsstat3(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: if (!xdr_post_op_fh3(xdrs, &objp->nfslog_MKNOD3res_u.obj)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_REMOVE3args(XDR *xdrs, nfslog_REMOVE3args *objp) { if (!xdr_nfslog_diropargs3(xdrs, &objp->object)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_RMDIR3args(XDR *xdrs, nfslog_RMDIR3args *objp) { if (!xdr_nfslog_diropargs3(xdrs, &objp->object)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_RENAME3args(XDR *xdrs, nfslog_RENAME3args *objp) { if (!xdr_nfslog_diropargs3(xdrs, &objp->from)) return (FALSE); if (!xdr_nfslog_diropargs3(xdrs, &objp->to)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_LINK3args(XDR *xdrs, nfslog_LINK3args *objp) { if (!xdr_nfs_fh3(xdrs, &objp->file)) return (FALSE); if (!xdr_nfslog_diropargs3(xdrs, &objp->link)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_READDIRPLUS3args(XDR *xdrs, nfslog_READDIRPLUS3args *objp) { if (!xdr_nfs_fh3(xdrs, &objp->dir)) return (FALSE); if (!xdr_count3(xdrs, &objp->dircount)) return (FALSE); if (!xdr_count3(xdrs, &objp->maxcount)) return (FALSE); return (TRUE); } static bool_t xdr_nfslog_entryplus3(XDR *xdrs, nfslog_entryplus3 *objp) { if (!xdr_post_op_fh3(xdrs, &objp->name_handle)) return (FALSE); if (!xdr_string(xdrs, &objp->name, ~0)) return (FALSE); if (!xdr_pointer(xdrs, (char **)&objp->nextentry, sizeof (nfslog_entryplus3), (xdrproc_t)xdr_nfslog_entryplus3)) return (FALSE); return (TRUE); } static bool_t xdr_nfslog_dirlistplus3(XDR *xdrs, nfslog_dirlistplus3 *objp) { if (!xdr_pointer(xdrs, (char **)&objp->entries, sizeof (nfslog_entryplus3), (xdrproc_t)xdr_nfslog_entryplus3)) return (FALSE); if (!xdr_bool(xdrs, &objp->eof)) return (FALSE); return (TRUE); } static bool_t xdr_nfslog_READDIRPLUS3resok(XDR *xdrs, nfslog_READDIRPLUS3resok *objp) { if (!xdr_nfslog_dirlistplus3(xdrs, &objp->reply)) return (FALSE); return (TRUE); } bool_t xdr_nfslog_READDIRPLUS3res(XDR *xdrs, nfslog_READDIRPLUS3res *objp) { if (!xdr_nfsstat3(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: if (!xdr_nfslog_READDIRPLUS3resok( xdrs, &objp->nfslog_READDIRPLUS3res_u.ok)) return (FALSE); break; } return (TRUE); } bool_t xdr_nfslog_COMMIT3args(XDR *xdrs, nfslog_COMMIT3args *objp) { if (!xdr_nfs_fh3(xdrs, &objp->file)) return (FALSE); if (!xdr_offset3(xdrs, &objp->offset)) return (FALSE); if (!xdr_count3(xdrs, &objp->count)) return (FALSE); return (TRUE); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * nfs log - read buffer file and print structs in user-readable form */ #define _REENTRANT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fhtab.h" #include "nfslogd.h" static char empty_name[4] = "-"; static char ftype3_names[NF3FIFO + 1][20] = { "\"none\"", "\"file\"", "\"dir\"", "\"blk device\"", "\"chr device\"", "\"link\"", "\"socket\"", "\"fifo\"" }; #define NFSL_FTYPE3(ftype) \ ((((ftype) >= 0) && ((ftype) <= NF3FIFO)) ? \ ftype3_names[ftype] : empty_name) static char createmode3_names[EXCLUSIVE + 1][20] = { "\"unchecked", "\"guarded\"", "\"exclusive\"" }; #define NFSL_CREATEMODE3(createmode) \ ((((createmode) >= 0) && ((createmode) <= EXCLUSIVE)) ? \ createmode3_names[createmode] : empty_name) static char auth_flavor_name[RPCSEC_GSS + 1][20] = { "\"auth_null\"", "\"auth_unix\"", "\"auth_short\"", "\"auth_des\"", "\"auth_kerb\"", "\"none\"", "\"rpcsec_gss\"" }; #define NFSL_AUTH_FLAVOR_PRINT(auth_flavor) \ (((auth_flavor) <= RPCSEC_GSS) ? \ auth_flavor_name[auth_flavor] : empty_name) #define NFSL_ERR_CNT 31 /* Actual err numbers */ /* * Two arrays - one short ints containing err codes, the other the strings * (merged codes for both v2 and v3 */ static char nfsl_status_name[NFSL_ERR_CNT][30] = { "\"ok\"", "\"perm\"", "\"noent\"", "\"io\"", "\"nxio\"", "\"access\"", "\"exist\"", "\"xdev\"", "\"nodev\"", "\"notdir\"", "\"isdir\"", "\"inval\"", "\"fbig\"", "\"nospc\"", "\"rofs\"", "\"mlink\"", "\"notsupp\"", "\"nametoolong\"", "\"notempty\"", "\"dquot\"", "\"stale\"", "\"remote\"", "\"wflush\"", "\"badhandle\"", "\"not_sync\"", "\"bad_cookie\"", "\"notsupp\"", "\"toosmall\"", "\"serverfault\"", "\"badtype\"", "\"jukebox\"", }; static short nfsl_status[NFSL_ERR_CNT] = { 0, 1, 2, 5, 6, 13, 17, 18, 19, 20, 21, 22, 27, 28, 30, 31, 45, 63, 66, 69, 70, 71, 99, 10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008 }; /* list of open elf files */ static struct nfsl_log_file *elf_file_list = NULL; /* Imported functions */ extern void bcopy(const void *s1, void *s2, size_t n); /* Static functions */ static void nfsl_log_file_free(struct nfsl_log_file *elfrec); static void nfsl_log_file_add(struct nfsl_log_file *elfrec, struct nfsl_log_file **elf_listp); static struct nfsl_log_file *nfsl_log_file_find(struct nfsl_log_file *elfrec, struct nfsl_log_file *elf_list); static struct nfsl_log_file *nfsl_log_file_del(struct nfsl_log_file *elfrec, struct nfsl_log_file **elf_listp); static char *nfsl_get_time(time_t tt); static char *nfsl_get_date(time_t tt); static char *nfsl_get_date_nq(time_t tt); static int nfsl_write_elfbuf(struct nfsl_log_file *elfrec); static void nfsl_ipaddr_print(struct nfsl_log_file *, struct netbuf *); static void nfsl_elf_record_header_print(struct nfsl_log_file *, nfslog_record_header *, char *, char *, struct nfsl_proc_disp *, char *); static void nfsl_elf_buffer_header_print(struct nfsl_log_file *, nfslog_buffer_header *); static struct nfsl_proc_disp *nfsl_find_elf_dispatch( nfslog_request_record *, char **); static void nfsl_elf_rpc_print(struct nfsl_log_file *, nfslog_request_record *, struct nfsl_proc_disp *, char *, char *, char *); static void nfslog_size3_print(struct nfsl_log_file *, set_size3 *); static void nfslog_null_args(struct nfsl_log_file *, caddr_t *); static void nfslog_null_res(struct nfsl_log_file *, caddr_t *); /* * NFS VERSION 2 */ /* Functions for elf print of the arguments */ static void nfslog_fhandle_print(struct nfsl_log_file *, fhandle_t *); static void nfslog_diropargs_print(struct nfsl_log_file *, nfslog_diropargs *); static void nfslog_setattrargs_print(struct nfsl_log_file *, nfslog_setattrargs *); static void nfslog_sattr_print(struct nfsl_log_file *, nfslog_sattr *); static void nfslog_nfsreadargs_print(struct nfsl_log_file *, nfslog_nfsreadargs *); static void nfslog_writeargs_print(struct nfsl_log_file *, nfslog_writeargs *); static void nfslog_writeresult_print(struct nfsl_log_file *, nfslog_writeresult *, bool_t); static void nfslog_creatargs_print(struct nfsl_log_file *, nfslog_createargs *); static void nfslog_rddirargs_print(struct nfsl_log_file *, nfslog_rddirargs *); static void nfslog_linkargs_print(struct nfsl_log_file *, nfslog_linkargs *); static void nfslog_rnmargs_print(struct nfsl_log_file *, nfslog_rnmargs *); static void nfslog_symlinkargs_print(struct nfsl_log_file *, nfslog_symlinkargs *); static void nfslog_sharefsargs_print(struct nfsl_log_file *, nfslog_sharefsargs *); static void nfslog_getfhargs_print(struct nfsl_log_file *, nfslog_getfhargs *); /* Functions for elf print of the response */ static void nfslog_nfsstat_print(struct nfsl_log_file *, enum nfsstat *, bool_t); static void nfslog_diropres_print(struct nfsl_log_file *, nfslog_diropres *, bool_t); static void nfslog_rdlnres_print(struct nfsl_log_file *, nfslog_rdlnres *, bool_t); static void nfslog_rdresult_print(struct nfsl_log_file *, nfslog_rdresult *, bool_t); static void nfslog_rddirres_print(struct nfsl_log_file *, nfslog_rddirres *, bool_t); /* * NFS VERSION 3 */ /* Functions for elf print of the arguments */ static void nfslog_fh3_print(struct nfsl_log_file *, nfs_fh3 *); static void nfslog_diropargs3_print(struct nfsl_log_file *, nfslog_diropargs3 *); static void nfslog_SETATTR3args_print(struct nfsl_log_file *, nfslog_SETATTR3args *); static void nfslog_READ3args_print(struct nfsl_log_file *, nfslog_READ3args *); static void nfslog_WRITE3args_print(struct nfsl_log_file *, nfslog_WRITE3args *); static void nfslog_CREATE3args_print(struct nfsl_log_file *, nfslog_CREATE3args *); static void nfslog_MKDIR3args_print(struct nfsl_log_file *, nfslog_MKDIR3args *); static void nfslog_SYMLINK3args_print(struct nfsl_log_file *, nfslog_SYMLINK3args *); static void nfslog_MKNOD3args_print(struct nfsl_log_file *, nfslog_MKNOD3args *); static void nfslog_REMOVE3args_print(struct nfsl_log_file *, nfslog_REMOVE3args *); static void nfslog_RMDIR3args_print(struct nfsl_log_file *, nfslog_RMDIR3args *); static void nfslog_RENAME3args_print(struct nfsl_log_file *, nfslog_RENAME3args *); static void nfslog_LINK3args_print(struct nfsl_log_file *, nfslog_LINK3args *); static void nfslog_COMMIT3args_print(struct nfsl_log_file *, nfslog_COMMIT3args *); static void nfslog_READDIRPLUS3args_print(struct nfsl_log_file *, nfslog_READDIRPLUS3args *); /* Functions for elf print of the response */ static void nfslog_nfsstat3_print(struct nfsl_log_file *, nfsstat3 *, bool_t); static void nfslog_LOOKUP3res_print(struct nfsl_log_file *, nfslog_LOOKUP3res *, bool_t); static void nfslog_READLINK3res_print(struct nfsl_log_file *, nfslog_READLINK3res *, bool_t); static void nfslog_READ3res_print(struct nfsl_log_file *, nfslog_READ3res *, bool_t); static void nfslog_WRITE3res_print(struct nfsl_log_file *, nfslog_WRITE3res *, bool_t); static void nfslog_CREATE3res_print(struct nfsl_log_file *, nfslog_CREATE3res *, bool_t); static void nfslog_MKDIR3res_print(struct nfsl_log_file *, nfslog_MKDIR3res *, bool_t); static void nfslog_SYMLINK3res_print(struct nfsl_log_file *, nfslog_SYMLINK3res *, bool_t); static void nfslog_MKNOD3res_print(struct nfsl_log_file *, nfslog_MKNOD3res *, bool_t); static void nfslog_READDIRPLUS3res_print(struct nfsl_log_file *, nfslog_READDIRPLUS3res *, bool_t); extern int debug; static bool_t nfsl_print_fh = FALSE; /* print file handles? */ #define DFLT_BUFFERSIZE 8192 #define DFLT_OVFSIZE 3072 /* Maximum logged or buffered size */ static char hostname[MAXHOSTNAMELEN]; /* name of host */ /* * 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_proc_disp nfsl_elf_proc_v2[] = { /* * NFS VERSION 2 */ /* RFS_NULL = 0 */ {nfslog_null_args, nfslog_null_res, "\"null\""}, /* RFS_GETATTR = 1 */ {nfslog_fhandle_print, nfslog_nfsstat_print, "\"getattr\""}, /* RFS_SETATTR = 2 */ {nfslog_setattrargs_print, nfslog_nfsstat_print, "\"setattr\""}, /* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */ {nfslog_null_args, nfslog_null_res, "\"root\""}, /* RFS_LOOKUP = 4 */ {nfslog_diropargs_print, nfslog_diropres_print, "\"lookup\""}, /* RFS_READLINK = 5 */ {nfslog_fhandle_print, nfslog_rdlnres_print, "\"readlink\""}, /* RFS_READ = 6 */ {nfslog_nfsreadargs_print, nfslog_rdresult_print, "\"read\""}, /* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */ {nfslog_null_args, nfslog_null_res, "\"writecache\""}, /* RFS_WRITE = 8 */ {nfslog_writeargs_print, nfslog_writeresult_print, "\"write\""}, /* RFS_CREATE = 9 */ {nfslog_creatargs_print, nfslog_diropres_print, "\"create\""}, /* RFS_REMOVE = 10 */ {nfslog_diropargs_print, nfslog_nfsstat_print, "\"remove\""}, /* RFS_RENAME = 11 */ {nfslog_rnmargs_print, nfslog_nfsstat_print, "\"rename\""}, /* RFS_LINK = 12 */ {nfslog_linkargs_print, nfslog_nfsstat_print, "\"link\""}, /* RFS_SYMLINK = 13 */ {nfslog_symlinkargs_print, nfslog_nfsstat_print, "\"symlink\""}, /* RFS_MKDIR = 14 */ {nfslog_creatargs_print, nfslog_diropres_print, "\"mkdir\""}, /* RFS_RMDIR = 15 */ {nfslog_diropargs_print, nfslog_nfsstat_print, "\"rmdir\""}, /* RFS_READDIR = 16 */ {nfslog_rddirargs_print, nfslog_rddirres_print, "\"readdir\""}, /* RFS_STATFS = 17 */ {nfslog_fhandle_print, nfslog_nfsstat_print, "\"statfs\""}, }; /* * NFS VERSION 3 */ static struct nfsl_proc_disp nfsl_elf_proc_v3[] = { /* NFSPROC3_NULL = 0 */ {nfslog_null_args, nfslog_null_res, "\"null\""}, /* NFSPROC3_GETATTR = 1 */ {nfslog_fh3_print, nfslog_nfsstat3_print, "\"getattr\""}, /* NFSPROC3_SETATTR = 2 */ {nfslog_SETATTR3args_print, nfslog_nfsstat3_print, "\"setattr\""}, /* NFSPROC3_LOOKUP = 3 */ {nfslog_diropargs3_print, nfslog_LOOKUP3res_print, "\"lookup\""}, /* NFSPROC3_ACCESS = 4 */ {nfslog_fh3_print, nfslog_nfsstat3_print, "\"access\""}, /* NFSPROC3_READLINK = 5 */ {nfslog_fh3_print, nfslog_READLINK3res_print, "\"readlink\""}, /* NFSPROC3_READ = 6 */ {nfslog_READ3args_print, nfslog_READ3res_print, "\"read\""}, /* NFSPROC3_WRITE = 7 */ {nfslog_WRITE3args_print, nfslog_WRITE3res_print, "\"write\""}, /* NFSPROC3_CREATE = 8 */ {nfslog_CREATE3args_print, nfslog_CREATE3res_print, "\"create\""}, /* NFSPROC3_MKDIR = 9 */ {nfslog_MKDIR3args_print, nfslog_MKDIR3res_print, "\"mkdir\""}, /* NFSPROC3_SYMLINK = 10 */ {nfslog_SYMLINK3args_print, nfslog_SYMLINK3res_print, "\"symlink\""}, /* NFSPROC3_MKNOD = 11 */ {nfslog_MKNOD3args_print, nfslog_MKNOD3res_print, "\"mknod\""}, /* NFSPROC3_REMOVE = 12 */ {nfslog_REMOVE3args_print, nfslog_nfsstat3_print, "\"remove\""}, /* NFSPROC3_RMDIR = 13 */ {nfslog_RMDIR3args_print, nfslog_nfsstat3_print, "\"rmdir\""}, /* NFSPROC3_RENAME = 14 */ {nfslog_RENAME3args_print, nfslog_nfsstat3_print, "\"rename\""}, /* NFSPROC3_LINK = 15 */ {nfslog_LINK3args_print, nfslog_nfsstat3_print, "\"link\""}, /* NFSPROC3_READDIR = 16 */ {nfslog_fh3_print, nfslog_nfsstat3_print, "\"readdir\""}, /* NFSPROC3_READDIRPLUS = 17 */ {nfslog_READDIRPLUS3args_print, nfslog_READDIRPLUS3res_print, "\"readdirplus\""}, /* NFSPROC3_FSSTAT = 18 */ {nfslog_fh3_print, nfslog_nfsstat3_print, "\"fsstat\""}, /* NFSPROC3_FSINFO = 19 */ {nfslog_fh3_print, nfslog_nfsstat3_print, "\"fsinfo\""}, /* NFSPROC3_PATHCONF = 20 */ {nfslog_fh3_print, nfslog_nfsstat3_print, "\"pathconf\""}, /* NFSPROC3_COMMIT = 21 */ {nfslog_COMMIT3args_print, nfslog_nfsstat3_print, "\"commit\""}, }; /* * NFSLOG VERSION 1 */ static struct nfsl_proc_disp nfsl_log_elf_proc_v1[] = { /* NFSLOG_NULL = 0 */ {nfslog_null_args, nfslog_null_res, "\"null\""}, /* NFSLOG_SHARE = 1 */ {nfslog_sharefsargs_print, nfslog_nfsstat_print, "\"log_share\""}, /* NFSLOG_UNSHARE = 2 */ {nfslog_sharefsargs_print, nfslog_nfsstat_print, "\"log_unshare\""}, /* NFSLOG_LOOKUP = 3 */ {nfslog_diropargs3_print, nfslog_LOOKUP3res_print, "\"lookup\""}, /* NFSLOG_GETFH = 4 */ {nfslog_getfhargs_print, nfslog_nfsstat_print, "\"log_getfh\""}, }; static struct nfsl_vers_disp nfsl_elf_vers_disptable[] = { {sizeof (nfsl_elf_proc_v2) / sizeof (nfsl_elf_proc_v2[0]), nfsl_elf_proc_v2}, {sizeof (nfsl_elf_proc_v3) / sizeof (nfsl_elf_proc_v3[0]), nfsl_elf_proc_v3}, }; static struct nfsl_vers_disp nfsl_log_elf_vers_disptable[] = { {sizeof (nfsl_log_elf_proc_v1) / sizeof (nfsl_log_elf_proc_v1[0]), nfsl_log_elf_proc_v1}, }; static struct nfsl_prog_disp nfsl_elf_dispatch_table[] = { {NFS_PROGRAM, NFS_VERSMIN, sizeof (nfsl_elf_vers_disptable) / sizeof (nfsl_elf_vers_disptable[0]), nfsl_elf_vers_disptable, "nfs"}, {NFSLOG_PROGRAM, NFSLOG_VERSMIN, sizeof (nfsl_log_elf_vers_disptable) / sizeof (nfsl_log_elf_vers_disptable[0]), nfsl_log_elf_vers_disptable, "nfslog"}, }; static int nfsl_elf_dispatch_table_arglen = sizeof (nfsl_elf_dispatch_table) / sizeof (nfsl_elf_dispatch_table[0]); static char * nfslog_get_status(short status) { int low, mid, high; short errstat; /* Usually status is 0... */ if (status == 0) return (nfsl_status_name[0]); low = 0; high = NFSL_ERR_CNT; mid = NFSL_ERR_CNT / 2; /* binary search for status string */ while (((errstat = nfsl_status[mid]) != status) && (low < mid) && (mid < high)) { if (errstat > status) { /* search bottom half */ high = mid; } else { /* search upper half */ low = mid; } mid = low + ((high - low) / 2); } if (errstat == status) { /* found it */ return (nfsl_status_name[mid]); } return (NULL); } /* nfsl_get_time - return string with time formatted as hh:mm:ss */ static char * nfsl_get_time(time_t tt) { static char timestr[20]; static time_t lasttime; struct tm tmst; if (tt == lasttime) return (timestr); if (localtime_r(&tt, &tmst) == NULL) { return (empty_name); } (void) sprintf(timestr, "%02d:%02d:%02d", tmst.tm_hour, tmst.tm_min, tmst.tm_sec); lasttime = tt; return (timestr); } /* nfsl_get_date - return date string formatted as "yyyy-mm-dd hh:mm:ss" */ static char * nfsl_get_date(time_t tt) { static char timestr[30]; static time_t lasttime; struct tm tmst; if (tt == lasttime) return (timestr); if (localtime_r(&tt, &tmst) == NULL) { return (empty_name); } (void) sprintf(timestr, "\"%04d-%02d-%02d %02d:%02d:%02d\"", tmst.tm_year + 1900, tmst.tm_mon + 1, tmst.tm_mday, tmst.tm_hour, tmst.tm_min, tmst.tm_sec); lasttime = tt; return (timestr); } /* * nfsl_get_date_nq - return date string formatted as yyyy-mm-dd hh:mm:ss * (no quotes) */ static char * nfsl_get_date_nq(time_t tt) { static char timestr[30]; static time_t lasttime; struct tm tmst; if (tt == lasttime) return (timestr); if (localtime_r(&tt, &tmst) == NULL) { return (empty_name); } (void) sprintf(timestr, "%04d-%02d-%02d %02d:%02d:%02d", tmst.tm_year + 1900, tmst.tm_mon + 1, tmst.tm_mday, tmst.tm_hour, tmst.tm_min, tmst.tm_sec); return (timestr); } /* write log buffer out to file */ static int nfsl_write_elfbuf(struct nfsl_log_file *elfrec) { int rc; char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; if (debug > 1) (void) printf("nfsl_write_elfbuf: bufoffset %d\n", elfbufoffset); if (elfbufoffset <= 0) return (0); elfbuf[elfbufoffset] = '\0'; if ((rc = fputs(elfbuf, elfrec->fp)) < 0) { syslog(LOG_ERR, gettext("Write to %s failed: %s\n"), elfrec->path, strerror(errno)); return (-1); } if (rc != elfbufoffset) { syslog(LOG_ERR, gettext("Write %d bytes to %s returned %d\n"), elfbufoffset, elfrec->path, rc); return (-1); } elfrec->bufoffset = 0; return (0); } /*ARGSUSED*/ static void nfslog_null_args(struct nfsl_log_file *elfrec, caddr_t *nfsl_args) { } /*ARGSUSED*/ static void nfslog_null_res(struct nfsl_log_file *elfrec, caddr_t *nfsl_res) { } static void nfslog_fh3_print(struct nfsl_log_file *elfrec, nfs_fh3 *fh3) { if (!nfsl_print_fh) return; if (fh3->fh3_length == sizeof (fhandle_t)) { nfslog_fhandle_print(elfrec, (fhandle_t *)&fh3->fh3_u.data); } else { nfslog_opaque_print_buf(fh3->fh3_u.data, fh3->fh3_length, elfrec->buf, &elfrec->bufoffset, DFLT_BUFFERSIZE + DFLT_OVFSIZE); } } /* * NFS VERSION 2 */ /* Functions that elf print the arguments */ static void nfslog_fhandle_print(struct nfsl_log_file *elfrec, fhandle_t *args) { if (!nfsl_print_fh) return; nfslog_opaque_print_buf(args, sizeof (*args), elfrec->buf, &elfrec->bufoffset, DFLT_BUFFERSIZE + DFLT_OVFSIZE); } static void nfslog_diropargs_print(struct nfsl_log_file *elfrec, nfslog_diropargs *args) { char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; if (nfsl_print_fh) { nfslog_fhandle_print(elfrec, &args->da_fhandle); elfbufoffset = elfrec->bufoffset; if (args->da_name != NULL) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"", args->da_name); } else { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); } } elfrec->bufoffset = elfbufoffset; } static void nfslog_sattr_print(struct nfsl_log_file *elfrec, nfslog_sattr *args) { char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; /* BEGIN CSTYLED */ if (args->sa_mode != (uint32_t)-1) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"mode=0%o\"", args->sa_mode); } if (args->sa_uid != (uint32_t)-1) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"uid=0x%x\"", args->sa_uid); } if (args->sa_gid != (uint32_t)-1) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"gid=0x%x\"", args->sa_gid); } if (args->sa_size != (uint32_t)-1) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"size=0x%x\"", args->sa_size); } if (args->sa_atime.tv_sec != (uint32_t)-1) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"atime=%s\"", nfsl_get_date_nq((time_t)args->sa_atime.tv_sec)); } if (args->sa_mtime.tv_sec != (uint32_t)-1) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"mtime=%s\"", nfsl_get_date_nq((time_t)args->sa_mtime.tv_sec)); } /* END CSTYLED */ elfrec->bufoffset = elfbufoffset; } static void nfslog_setattrargs_print(struct nfsl_log_file *elfrec, nfslog_setattrargs *args) { nfslog_fhandle_print(elfrec, &args->saa_fh); nfslog_sattr_print(elfrec, &args->saa_sa); } static void nfslog_nfsreadargs_print(struct nfsl_log_file *elfrec, nfslog_nfsreadargs *args) { char *elfbuf = elfrec->buf; int elfbufoffset; nfslog_fhandle_print(elfrec, &args->ra_fhandle); elfbufoffset = elfrec->bufoffset; elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->ra_offset); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->ra_count); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->ra_totcount); elfrec->bufoffset = elfbufoffset; } static void nfslog_writeargs_print(struct nfsl_log_file *elfrec, nfslog_writeargs *args) { char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; nfslog_fhandle_print(elfrec, &args->waargs_fhandle); elfbufoffset = elfrec->bufoffset; elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->waargs_begoff); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->waargs_offset); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->waargs_totcount); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x", args->waargs_count); } static void nfslog_writeresult_print(struct nfsl_log_file *elfrec, nfslog_writeresult *res, bool_t print_status) { char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; if (print_status) { nfslog_nfsstat_print(elfrec, &res->wr_status, print_status); } else if (res->wr_status == NFS_OK) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", res->nfslog_writeresult_u.wr_size); elfrec->bufoffset = elfbufoffset; } } static void nfslog_creatargs_print(struct nfsl_log_file *elfrec, nfslog_createargs *args) { nfslog_diropargs_print(elfrec, &args->ca_da); nfslog_sattr_print(elfrec, &args->ca_sa); } static void nfslog_rddirargs_print(struct nfsl_log_file *elfrec, nfslog_rddirargs *args) { char *elfbuf = elfrec->buf; int elfbufoffset; nfslog_fhandle_print(elfrec, &args->rda_fh); elfbufoffset = elfrec->bufoffset; elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->rda_offset); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->rda_count); elfrec->bufoffset = elfbufoffset; } static void nfslog_rnmargs_print(struct nfsl_log_file *elfrec, nfslog_rnmargs *args) { nfslog_diropargs_print(elfrec, &args->rna_from); nfslog_diropargs_print(elfrec, &args->rna_to); } static void nfslog_linkargs_print(struct nfsl_log_file *elfrec, nfslog_linkargs *args) { nfslog_fhandle_print(elfrec, &args->la_from); nfslog_diropargs_print(elfrec, &args->la_to); } static void nfslog_symlinkargs_print(struct nfsl_log_file *elfrec, nfslog_symlinkargs *args) { char *elfbuf = elfrec->buf; int elfbufoffset; nfslog_diropargs_print(elfrec, &args->sla_from); elfbufoffset = elfrec->bufoffset; elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"", args->sla_tnm); elfrec->bufoffset = elfbufoffset; nfslog_sattr_print(elfrec, &args->sla_sa); } /* * SHARE/UNSHARE fs log args copy */ static void nfslog_sharefsargs_print(struct nfsl_log_file *elfrec, nfslog_sharefsargs *args) { unsigned int elfbufoffset; char *elfbuf = elfrec->buf; nfslog_fhandle_print(elfrec, &args->sh_fh_buf); elfbufoffset = elfrec->bufoffset; elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->sh_flags); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->sh_anon); if (nfsl_print_fh) { if (args->sh_path != NULL) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"", args->sh_path); } else { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); } } elfrec->bufoffset = elfbufoffset; } static void nfslog_getfhargs_print(struct nfsl_log_file *elfrec, nfslog_getfhargs *args) { unsigned int elfbufoffset; char *elfbuf = elfrec->buf; nfslog_fhandle_print(elfrec, &args->gfh_fh_buf); elfbufoffset = elfrec->bufoffset; if (nfsl_print_fh) { if (args->gfh_path != NULL) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"", args->gfh_path); } else { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); } } elfrec->bufoffset = elfbufoffset; } static void nfslog_nfsstat_print(struct nfsl_log_file *elfrec, enum nfsstat *res, bool_t print_status) { if (print_status) { char *statp = nfslog_get_status((short)(*res)); if (statp != NULL) elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s", statp); else elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %5d", *res); } } static void nfslog_diropres_print(struct nfsl_log_file *elfrec, nfslog_diropres *res, bool_t print_status) { if (print_status) { nfslog_nfsstat_print(elfrec, &res->dr_status, print_status); } else if (res->dr_status == NFS_OK) { nfslog_fhandle_print(elfrec, &res->nfslog_diropres_u.dr_ok.drok_fhandle); } } static void nfslog_rdlnres_print(struct nfsl_log_file *elfrec, nfslog_rdlnres *res, bool_t print_status) { if (print_status) { nfslog_nfsstat_print(elfrec, &res->rl_status, print_status); } else if (res->rl_status == NFS_OK) { elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " \"%s\"", res->nfslog_rdlnres_u.rl_ok); } } static void nfslog_rdresult_print(struct nfsl_log_file *elfrec, nfslog_rdresult *res, bool_t print_status) { if (print_status) { nfslog_nfsstat_print(elfrec, &res->r_status, print_status); } else if (res->r_status == NFS_OK) { elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x", res->nfslog_rdresult_u.r_ok.filesize); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x", res->nfslog_rdresult_u.r_ok.rrok_count); } } static void nfslog_rddirres_print(struct nfsl_log_file *elfrec, nfslog_rddirres *res, bool_t print_status) { if (print_status) { nfslog_nfsstat_print(elfrec, &res->rd_status, print_status); } else if (res->rd_status == NFS_OK) { char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", res->nfslog_rddirres_u.rd_ok.rdok_offset); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", res->nfslog_rddirres_u.rd_ok.rdok_size); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", res->nfslog_rddirres_u.rd_ok.rdok_eof); elfrec->bufoffset = elfbufoffset; } } /* * NFS VERSION 3 */ static void nfslog_diropargs3_print(struct nfsl_log_file *elfrec, nfslog_diropargs3 *args) { if (nfsl_print_fh) { nfslog_fh3_print(elfrec, &args->dir); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " \"%s\"", args->name); } } static void nfslog_size3_print(struct nfsl_log_file *elfrec, set_size3 *args) { char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; if (args->set_it) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], /* CSTYLED */ " \"size=0x%llx\"", args->size); } elfrec->bufoffset = elfbufoffset; } static void nfslog_SETATTR3args_print(struct nfsl_log_file *elfrec, nfslog_SETATTR3args *args) { nfslog_fh3_print(elfrec, &args->object); nfslog_size3_print(elfrec, &args->size); } static void nfslog_READ3args_print(struct nfsl_log_file *elfrec, nfslog_READ3args *args) { nfslog_fh3_print(elfrec, &args->file); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%llx", args->offset); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x", args->count); } static void nfslog_WRITE3args_print(struct nfsl_log_file *elfrec, nfslog_WRITE3args *args) { char *elfbuf = elfrec->buf; int elfbufoffset; nfslog_fh3_print(elfrec, &args->file); elfbufoffset = elfrec->bufoffset; elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%llx", args->offset); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->count); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", args->stable); elfrec->bufoffset = elfbufoffset; } static void nfslog_CREATE3args_print(struct nfsl_log_file *elfrec, nfslog_CREATE3args *args) { nfslog_diropargs3_print(elfrec, &args->where); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s", NFSL_CREATEMODE3(args->how.mode)); if (args->how.mode != EXCLUSIVE) { nfslog_size3_print(elfrec, &args->how.nfslog_createhow3_u.size); } } static void nfslog_MKDIR3args_print(struct nfsl_log_file *elfrec, nfslog_MKDIR3args *args) { nfslog_diropargs3_print(elfrec, &args->where); } static void nfslog_SYMLINK3args_print(struct nfsl_log_file *elfrec, nfslog_SYMLINK3args *args) { nfslog_diropargs3_print(elfrec, &args->where); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " \"%s\"", args->symlink_data); } static void nfslog_MKNOD3args_print(struct nfsl_log_file *elfrec, nfslog_MKNOD3args *args) { nfslog_diropargs3_print(elfrec, &args->where); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s", NFSL_FTYPE3(args->type)); } static void nfslog_REMOVE3args_print(struct nfsl_log_file *elfrec, nfslog_REMOVE3args *args) { nfslog_diropargs3_print(elfrec, &args->object); } static void nfslog_RMDIR3args_print(struct nfsl_log_file *elfrec, nfslog_RMDIR3args *args) { nfslog_diropargs3_print(elfrec, &args->object); } static void nfslog_RENAME3args_print(struct nfsl_log_file *elfrec, nfslog_RENAME3args *args) { nfslog_diropargs3_print(elfrec, &args->from); nfslog_diropargs3_print(elfrec, &args->to); } static void nfslog_LINK3args_print(struct nfsl_log_file *elfrec, nfslog_LINK3args *args) { nfslog_fh3_print(elfrec, &args->file); nfslog_diropargs3_print(elfrec, &args->link); } static void nfslog_READDIRPLUS3args_print(struct nfsl_log_file *elfrec, nfslog_READDIRPLUS3args *args) { nfslog_fh3_print(elfrec, &args->dir); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x", args->dircount); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x", args->maxcount); } static void nfslog_COMMIT3args_print(struct nfsl_log_file *elfrec, nfslog_COMMIT3args *args) { nfslog_fh3_print(elfrec, &args->file); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%llx", args->offset); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x", args->count); } static void nfslog_nfsstat3_print(struct nfsl_log_file *elfrec, enum nfsstat3 *res, bool_t print_status) { if (print_status) { char *statp = nfslog_get_status((short)(*res)); if (statp != NULL) elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s", statp); else elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %5d", *res); } } static void nfslog_LOOKUP3res_print(struct nfsl_log_file *elfrec, nfslog_LOOKUP3res *res, bool_t print_status) { if (print_status) { nfslog_nfsstat3_print(elfrec, &res->status, print_status); } else if (res->status == NFS3_OK) { nfslog_fh3_print(elfrec, &res->nfslog_LOOKUP3res_u.object); } } static void nfslog_READLINK3res_print(struct nfsl_log_file *elfrec, nfslog_READLINK3res *res, bool_t print_status) { if (print_status) { nfslog_nfsstat3_print(elfrec, &res->status, print_status); } else if (res->status == NFS3_OK) { elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s", res->nfslog_READLINK3res_u.data); } } static void nfslog_READ3res_print(struct nfsl_log_file *elfrec, nfslog_READ3res *res, bool_t print_status) { if (print_status) { nfslog_nfsstat3_print(elfrec, &res->status, print_status); } else if (res->status == NFS3_OK) { char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%llx", res->nfslog_READ3res_u.ok.filesize); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", res->nfslog_READ3res_u.ok.count); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", res->nfslog_READ3res_u.ok.eof); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", res->nfslog_READ3res_u.ok.size); elfrec->bufoffset = elfbufoffset; } } static void nfslog_WRITE3res_print(struct nfsl_log_file *elfrec, nfslog_WRITE3res *res, bool_t print_status) { if (print_status) { nfslog_nfsstat3_print(elfrec, &res->status, print_status); } else if (res->status == NFS3_OK) { char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%llx", res->nfslog_WRITE3res_u.ok.filesize); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", res->nfslog_WRITE3res_u.ok.count); elfbufoffset += sprintf(&elfrec->buf[elfbufoffset], " 0x%x", res->nfslog_WRITE3res_u.ok.committed); elfrec->bufoffset = elfbufoffset; } } static void nfslog_CREATE3res_print(struct nfsl_log_file *elfrec, nfslog_CREATE3res *res, bool_t print_status) { if (print_status) { nfslog_nfsstat3_print(elfrec, &res->status, print_status); } else if (res->status == NFS3_OK) { if (res->nfslog_CREATE3res_u.ok.obj.handle_follows) { nfslog_fh3_print(elfrec, &res->nfslog_CREATE3res_u.ok.obj.handle); } } } static void nfslog_MKDIR3res_print(struct nfsl_log_file *elfrec, nfslog_MKDIR3res *res, bool_t print_status) { if (print_status) { nfslog_nfsstat3_print(elfrec, &res->status, print_status); } else if (res->status == NFS3_OK) { if (res->nfslog_MKDIR3res_u.obj.handle_follows) { nfslog_fh3_print(elfrec, &res->nfslog_MKDIR3res_u.obj.handle); } } } static void nfslog_SYMLINK3res_print(struct nfsl_log_file *elfrec, nfslog_SYMLINK3res *res, bool_t print_status) { if (print_status) { nfslog_nfsstat3_print(elfrec, &res->status, print_status); } else if (res->status == NFS3_OK) { if (res->nfslog_SYMLINK3res_u.obj.handle_follows) { nfslog_fh3_print(elfrec, &res->nfslog_SYMLINK3res_u.obj.handle); } } } static void nfslog_MKNOD3res_print(struct nfsl_log_file *elfrec, nfslog_MKNOD3res *res, bool_t print_status) { if (print_status) { nfslog_nfsstat3_print(elfrec, &res->status, print_status); } else if (res->status == NFS3_OK) { if (res->nfslog_MKNOD3res_u.obj.handle_follows) { nfslog_fh3_print(elfrec, &res->nfslog_MKNOD3res_u.obj.handle); } } } static void nfslog_READDIRPLUS3res_print(struct nfsl_log_file *elfrec, nfslog_READDIRPLUS3res *res, bool_t print_status) { if (print_status) { nfslog_nfsstat3_print(elfrec, &res->status, print_status); } } /* * **** End of table functions for logging specific procs **** * * Hereafter are the general logging management and dispatcher. */ /* * nfsl_ipaddr_print - extracts sender ip address from transport struct * and prints it in legible form. */ static void nfsl_ipaddr_print(struct nfsl_log_file *elfrec, struct netbuf *ptr) { struct hostent *hp; extern char *inet_ntop(); int size, sin_family, error; char *elfbuf = elfrec->buf; char *addrp; int elfbufoffset = elfrec->bufoffset; if (ptr->len == 0) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); elfrec->bufoffset = elfbufoffset; return; } elfbufoffset += sprintf(&elfbuf[elfbufoffset], " "); /* LINTED */ sin_family = ((struct sockaddr_in *)ptr->buf)->sin_family; switch (sin_family) { case (AF_INET): /* LINTED */ addrp = (char *)&((struct sockaddr_in *)ptr->buf)->sin_addr; size = sizeof (struct in_addr); break; case (AF_INET6): /* LINTED */ addrp = (char *)&((struct sockaddr_in6 *)ptr->buf)->sin6_addr; size = sizeof (struct in6_addr); break; default: /* unknown protocol: print address in hex form */ for (size = ptr->len, addrp = ptr->buf; size > 0; size--) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], "%02x", *addrp); } elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); elfrec->bufoffset = elfbufoffset; return; } if (inet_ntop(sin_family, addrp, &elfbuf[elfbufoffset], (size_t)(DFLT_BUFFERSIZE + DFLT_OVFSIZE - elfbufoffset)) == NULL) { /* Not enough space to print - should never happen */ elfbuf[elfrec->bufoffset] = '\0'; /* just in case */ return; } /* inet_ntop copied address into elfbuf, so update offset */ elfbufoffset += strlen(&elfbuf[elfbufoffset]); /* get host name and log it as well */ hp = getipnodebyaddr(addrp, size, sin_family, &error); if (hp != NULL) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"", hp->h_name); } else { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); } elfrec->bufoffset = elfbufoffset; } static void nfsl_elf_record_header_print(struct nfsl_log_file *elfrec, nfslog_record_header *lhp, char *principal_name, char *tag, struct nfsl_proc_disp *disp, char *progname) { struct passwd *pwp = NULL; char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; /* * Fields: time bytes tag rpc-program rpc-version rpc-procedure * auth-flavor s-user-name s-uid uid u-name gid net-id * c-ip c-dns s-dns status rpcarg-path */ elfbufoffset += sprintf(&elfbuf[elfbufoffset], "%s", nfsl_get_time((time_t)lhp->rh_timestamp.tv_sec)); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x", lhp->rh_reclen); if ((tag != NULL) && (tag[0] != '\0')) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"", tag); } else { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); } elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s 0x%x %s", progname, lhp->rh_version, disp->procname); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", NFSL_AUTH_FLAVOR_PRINT(lhp->rh_auth_flavor)); if ((principal_name != NULL) && (principal_name[0] != '\0')) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"", principal_name); if ((pwp = getpwnam(principal_name)) != NULL) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%lx", pwp->pw_uid); } else { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); } } else { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); } elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%lx", lhp->rh_uid); if (((pwp = getpwuid(lhp->rh_uid)) != NULL) && (pwp->pw_name != NULL)) { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"", pwp->pw_name); } else { elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s", empty_name); } elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%lx", lhp->rh_gid); elfrec->bufoffset = elfbufoffset; } static void nfsl_elf_buffer_header_print(struct nfsl_log_file *elfrec, nfslog_buffer_header *bufhdr) { int rc; struct utsname name; char *elfbuf = elfrec->buf; int elfbufoffset = elfrec->bufoffset; rc = uname(&name); elfbufoffset += sprintf(&elfbuf[elfbufoffset], "#Version %d.0\n#Software \"%s\"\n", bufhdr->bh_version, ((rc >= 0) ? name.sysname : empty_name)); elfbufoffset += sprintf(&elfbuf[elfbufoffset], "#Date %s\n", nfsl_get_date((time_t)bufhdr->bh_timestamp.tv_sec)); elfbufoffset += sprintf(&elfbuf[elfbufoffset], "#Remark %s\n", empty_name); elfbufoffset += sprintf(&elfbuf[elfbufoffset], "#Fields: time bytes tag"); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " rpc-program rpc-version rpc-procedure"); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " auth-flavor s-user-name s-uid uid u-name gid net-id c-ip"); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " c-dns s-dns status rpcarg-path"); elfbufoffset += sprintf(&elfbuf[elfbufoffset], " rpc-arguments... rpc-response...\n"); elfrec->bufoffset = elfbufoffset; } /* * nfsl_find_elf_dispatch - get the dispatch struct for this request */ static struct nfsl_proc_disp * nfsl_find_elf_dispatch(nfslog_request_record *logrec, char **prognamep) { nfslog_record_header *logrechdr = &logrec->re_header; struct nfsl_prog_disp *progtable; /* prog struct */ struct nfsl_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_elf_dispatch_table_arglen) && (logrechdr->rh_prognum != nfsl_elf_dispatch_table[i].nfsl_dis_prog); i++); if (i >= nfsl_elf_dispatch_table_arglen) { /* program not logged */ /* not an error */ return (NULL); } progtable = &nfsl_elf_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 */ *prognamep = progtable->progname; return (&verstable->nfsl_dis_proc_table[logrechdr->rh_procnum]); } /* * nfsl_elf_rpc_print - Print the record buffer. */ static void nfsl_elf_rpc_print(struct nfsl_log_file *elfrec, nfslog_request_record *logrec, struct nfsl_proc_disp *disp, char *progname, char *path1, char *path2) { if (debug > 1) { (void) printf("%s %d %s", progname, logrec->re_header.rh_version, disp->procname); (void) printf(": '%s', '%s'\n", ((path1 != NULL) ? path1 : empty_name), ((path2 != NULL) ? path2 : empty_name)); } /* * XXXX programs using this file to get a usable record should * take "record" struct. */ /* * Print the variable fields: * principal name * netid * ip address * rpc args * rpc res * Use the displacements calculated earlier... */ /* * Fields: time bytes tag rpc-program rpc-version rpc-procedure * auth-flavor s-user-name s-uid uid u-name gid net-id c-ip * c-dns s-dns status rpcarg-path */ nfsl_elf_record_header_print(elfrec, &logrec->re_header, logrec->re_principal_name, logrec->re_tag, disp, progname); if ((logrec->re_netid != NULL) && (logrec->re_netid[0] != '\0')) { elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " \"%s\"", logrec->re_netid); } else { elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s", empty_name); } nfsl_ipaddr_print(elfrec, &logrec->re_ipaddr); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " \"%s\"", hostname); /* Next is return status */ (*disp->nfsl_dis_res)(elfrec, logrec->re_rpc_res, TRUE); /* Next is argpath */ if (path1 != NULL) { elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " \"%s\"", path1); } else { elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s", empty_name); } /* * path2 is non-empty for rename/link type operations. If it is non- * empty print it here as it's a part of the args */ if (path2 != NULL) { elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " \"%s\"", path2); } /* Next print formatted rpc args */ (*disp->nfsl_dis_args)(elfrec, logrec->re_rpc_arg); /* Next print formatted rpc res (minus status) */ (*disp->nfsl_dis_res)(elfrec, logrec->re_rpc_res, FALSE); elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], "\n"); } /* * nfsl_log_file_add - add a new record to the list */ static void nfsl_log_file_add(struct nfsl_log_file *elfrec, struct nfsl_log_file **elf_listp) { elfrec->next = *elf_listp; elfrec->prev = NULL; if (*elf_listp != NULL) { (*elf_listp)->prev = elfrec; } *elf_listp = elfrec; } /* * nfsl_log_file_find - finds a record in the list, given a cookie (== elfrec) * Returns the record. */ static struct nfsl_log_file * nfsl_log_file_find(struct nfsl_log_file *elfrec, struct nfsl_log_file *elf_list) { struct nfsl_log_file *rec; for (rec = elf_list; (rec != NULL) && (rec != elfrec); rec = rec->next); return (rec); } /* * nfsl_log_file_del - delete a record from the list, does not free rec. * Returns the deleted record. */ static struct nfsl_log_file * nfsl_log_file_del(struct nfsl_log_file *elfrec, struct nfsl_log_file **elf_listp) { struct nfsl_log_file *rec; if ((rec = nfsl_log_file_find(elfrec, *elf_listp)) == NULL) { return (NULL); } if (rec->prev != NULL) { rec->prev->next = rec->next; } else { *elf_listp = rec->next; } if (rec->next != NULL) { rec->next->prev = rec->prev; } return (rec); } /* * nfsl_log_file_free - frees a record */ static void nfsl_log_file_free(struct nfsl_log_file *elfrec) { if (elfrec == NULL) return; if (elfrec->path != NULL) free(elfrec->path); if (elfrec->buf != NULL) free(elfrec->buf); free(elfrec); } /* * Exported Functions */ /* * nfslog_open_elf_file - open the output elf file and mallocs needed buffers * Returns a pointer to the nfsl_log_file on success, NULL on error. * * *error contains the last error encountered on this object, It can * be used to avoid reporting the same error endlessly, by comparing * the current error to the last error. It is reset to the current error * code on return. */ void * nfslog_open_elf_file(char *elfpath, nfslog_buffer_header *bufhdr, int *error) { struct nfsl_log_file *elfrec; struct stat stat_buf; int preverror = *error; if ((elfrec = malloc(sizeof (*elfrec))) == NULL) { *error = errno; if (*error != preverror) { syslog(LOG_ERR, gettext("nfslog_open_elf_file: %s"), strerror(*error)); } return (NULL); } bzero(elfrec, sizeof (*elfrec)); elfrec->buf = (char *)malloc(DFLT_BUFFERSIZE + DFLT_OVFSIZE); if (elfrec->buf == NULL) { *error = errno; if (*error != preverror) { syslog(LOG_ERR, gettext("nfslog_open_elf_file: %s"), strerror(*error)); } nfsl_log_file_free(elfrec); return (NULL); } if ((elfrec->path = strdup(elfpath)) == NULL) { *error = errno; if (*error != preverror) { syslog(LOG_ERR, gettext("nfslog_open_elf_file: %s"), strerror(*error)); } nfsl_log_file_free(elfrec); return (NULL); } if ((elfrec->fp = fopen(elfpath, "a")) == NULL) { *error = errno; if (*error != preverror) { syslog(LOG_ERR, gettext("Cannot open '%s': %s"), elfpath, strerror(*error)); } nfsl_log_file_free(elfrec); return (NULL); } if (stat(elfpath, &stat_buf) == -1) { *error = errno; if (*error != preverror) { syslog(LOG_ERR, gettext("Cannot stat '%s': %s"), elfpath, strerror(*error)); } (void) fclose(elfrec->fp); nfsl_log_file_free(elfrec); return (NULL); } nfsl_log_file_add(elfrec, &elf_file_list); if (stat_buf.st_size == 0) { /* * Print header unto logfile */ nfsl_elf_buffer_header_print(elfrec, bufhdr); } if (hostname[0] == '\0') { (void) gethostname(hostname, MAXHOSTNAMELEN); } return (elfrec); } /* * nfslog_close_elf_file - close elffile and write out last buffer */ void nfslog_close_elf_file(void **elfcookie) { struct nfsl_log_file *elfrec; if ((*elfcookie == NULL) || ((elfrec = nfsl_log_file_del( *elfcookie, &elf_file_list)) == NULL)) { *elfcookie = NULL; return; } if (elfrec->fp != NULL) { /* Write the last output buffer to disk */ (void) nfsl_write_elfbuf(elfrec); (void) fclose(elfrec->fp); } nfsl_log_file_free(elfrec); *elfcookie = NULL; } /* * nfslog_process_elf_rec - processes the record in the buffer and outputs * to the elf log. * Return 0 for success, errno else. */ int nfslog_process_elf_rec(void *elfcookie, nfslog_request_record *logrec, char *path1, char *path2) { struct nfsl_log_file *elfrec; struct nfsl_proc_disp *disp; char *progname; if ((elfrec = nfsl_log_file_find(elfcookie, elf_file_list)) == NULL) { return (EINVAL); } /* Make sure there is room */ if (elfrec->bufoffset > DFLT_BUFFERSIZE) { if (nfsl_write_elfbuf(elfrec) < 0) { return (errno); } } if ((disp = nfsl_find_elf_dispatch(logrec, &progname)) != NULL) { nfsl_elf_rpc_print(elfrec, logrec, disp, progname, path1, path2); } return (0); } /* * 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) 1991, 1999 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Note: If making changes to this file, check also the file * cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c * as it has the same functions there. */ static jmp_buf nisjmp; #define MAXHASH 1024 /* must be a power of 2 */ struct hostdata { struct hostdata *h_next; char *h_hostname; int h_pktsout; int h_pktsin; }; struct hostdata4 { struct hostdata4 *h4_next; char *h4_hostname; int h4_pktsout; int h4_pktsin; struct in_addr h4_addr; }; struct hostdata6 { struct hostdata6 *h6_next; char *h6_hostname; int h6_pktsout; int h6_pktsin; struct in6_addr h6_addr; }; static struct hostdata *addhost(int, void *, char *); static struct hostdata4 *h_table4[MAXHASH]; static struct hostdata6 *h_table6[MAXHASH]; #define iphash(e) ((e) & (MAXHASH-1)) /* ARGSUSED */ static void wakeup(int n) { longjmp(nisjmp, 1); } extern char *inet_ntoa(); static struct hostdata * iplookup(ipaddr) struct in_addr *ipaddr; { register struct hostdata4 *h; struct hostent *hp = NULL; struct netent *np; int error_num; for (h = h_table4[iphash(ipaddr->s_addr)]; h; h = h->h4_next) { if (h->h4_addr.s_addr == ipaddr->s_addr) return ((struct hostdata *)h); } /* not found. Put it in */ if (ipaddr->s_addr == htonl(INADDR_BROADCAST)) return (addhost(AF_INET, ipaddr, "BROADCAST")); if (ipaddr->s_addr == htonl(INADDR_ANY)) return (addhost(AF_INET, ipaddr, "OLD-BROADCAST")); /* * Set an alarm here so we don't get held up by * an unresponsive name server. * Give it 3 sec to do its work. */ if (setjmp(nisjmp) == 0) { (void) signal(SIGALRM, wakeup); (void) alarm(3); hp = getipnodebyaddr((char *)ipaddr, sizeof (struct in_addr), AF_INET, &error_num); if (hp == NULL && inet_lnaof(*ipaddr) == 0) { np = getnetbyaddr(inet_netof(*ipaddr), AF_INET); if (np) return (addhost(AF_INET, ipaddr, np->n_name)); } (void) alarm(0); } else { hp = NULL; } return (addhost(AF_INET, ipaddr, hp ? hp->h_name : inet_ntoa(*ipaddr))); } static struct hostdata * ip6lookup(ip6addr) struct in6_addr *ip6addr; { struct hostdata6 *h; struct hostent *hp = NULL; int error_num; char addrstr[INET6_ADDRSTRLEN]; char *addname; struct hostdata *retval; for (h = h_table6[iphash(((uint32_t *)ip6addr)[3])]; h; h = h->h6_next) { if (IN6_ARE_ADDR_EQUAL(&h->h6_addr, ip6addr)) return ((struct hostdata *)h); } /* not in the hash table, put it in */ if (IN6_IS_ADDR_UNSPECIFIED(ip6addr)) return (addhost(AF_INET6, ip6addr, "UNSPECIFIED")); /* * Set an alarm here so we don't get held up by * an unresponsive name server. * Give it 3 sec to do its work. */ if (setjmp(nisjmp) == 0) { (void) signal(SIGALRM, wakeup); (void) alarm(3); hp = getipnodebyaddr(ip6addr, sizeof (struct in6_addr), AF_INET6, &error_num); (void) alarm(0); } else { hp = NULL; } if (hp != NULL) addname = hp->h_name; else { (void) inet_ntop(AF_INET6, ip6addr, addrstr, INET6_ADDRSTRLEN); addname = addrstr; } retval = addhost(AF_INET6, ip6addr, addname); freehostent(hp); return (retval); } static struct hostdata * addhost(family, ipaddr, name) int family; void *ipaddr; char *name; { register struct hostdata **hp, *n; int hashval; switch (family) { case AF_INET: n = (struct hostdata *)malloc(sizeof (struct hostdata4)); if (n == NULL) goto alloc_failed; (void) memset(n, 0, sizeof (struct hostdata4)); n->h_hostname = strdup(name); if (n->h_hostname == NULL) goto alloc_failed; ((struct hostdata4 *)n)->h4_addr = *(struct in_addr *)ipaddr; hashval = ((struct in_addr *)ipaddr)->s_addr; hp = (struct hostdata **)&h_table4[iphash(hashval)]; break; case AF_INET6: n = (struct hostdata *)malloc(sizeof (struct hostdata6)); if (n == NULL) goto alloc_failed; (void) memset(n, 0, sizeof (struct hostdata6)); n->h_hostname = strdup(name); if (n->h_hostname == NULL) goto alloc_failed; (void) memcpy(&((struct hostdata6 *)n)->h6_addr, ipaddr, sizeof (struct in6_addr)); hashval = ((int *)ipaddr)[3]; hp = (struct hostdata **)&h_table6[iphash(hashval)]; break; default: (void) fprintf(stderr, "nfslog: addhost ERROR: Unknown address family: %d", family); return (NULL); } n->h_next = *hp; *hp = n; return (n); alloc_failed: (void) fprintf(stderr, "addhost: no mem\n"); if (n != NULL) free(n); return (NULL); } char * addrtoname(void *sockp) { struct hostdata *hostp; int family = ((struct sockaddr_in *)sockp)->sin_family; switch (family) { case AF_INET: hostp = iplookup(&((struct sockaddr_in *)sockp)->sin_addr); break; case AF_INET6: hostp = ip6lookup(&((struct sockaddr_in6 *)sockp)->sin6_addr); break; default: (void) fprintf(stderr, "nfslog: ERROR: unknown address " \ "family: %d\n", family); hostp = NULL; } return ((hostp != NULL) ? hostp->h_hostname : NULL); } /* * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fhtab.h" #include "nfslogd.h" /* * How long should an entry stay in the list before being forced * out and a trans log entry printed */ #define TRANS_ENTRY_TIMEOUT 60 extern char *addrtoname(void *); struct transentry { struct transentry *next; struct transentry *prev; timestruc32_t starttime; /* when did transaction start? */ timestruc32_t lastupdate; /* last operation for this entry */ #define TRANS_OPER_READ 1 #define TRANS_OPER_WRITE 2 #define TRANS_OPER_SETATTR 3 #define TRANS_OPER_REMOVE 4 #define TRANS_OPER_MKDIR 5 #define TRANS_OPER_CREATE 6 #define TRANS_OPER_RMDIR 7 #define TRANS_OPER_RENAME 8 #define TRANS_OPER_MKNOD 9 #define TRANS_OPER_LINK 10 #define TRANS_OPER_SYMLINK 11 uchar_t optype; /* read, write, ...? */ #define TRANS_DATATYPE_NA /* not applicable data type */ #define TRANS_DATATYPE_ASCII 0 /* transfer done as ascii */ #define TRANS_DATATYPE_BINARY 1 /* transfer done as binary */ uchar_t datatype; /* * Action taken by server before transfer was made -- noaction, * compressed, tar or uncompressed. */ #define TRANS_OPTION_NOACTION 0 uchar_t transoption; char *pathname; struct netbuf *pnb; uid_t uid; int nfsvers; char *netid; char *principal_name; uint64_t totalbytes; /* total operated upon in history */ union { fhandle_t fh; nfs_fh3 fh3; } fh_u; }; struct nfslog_trans_file { struct nfslog_trans_file *next; /* next file in list */ struct nfslog_trans_file *prev; /* next file in list */ int refcnt; /* number of references to this struct */ char *path; /* pathname of file */ FILE *fp; /* file pointer */ /* timestamp of the last transaction processed for this file */ timestruc32_t lasttrans_timestamp; /* 'current' time that last trans was processed */ time_t last_trans_read; uint32_t trans_to_log; /* transactions that are to be logged */ uint32_t trans_output_type; struct transentry *te_list_v3_read; struct transentry *te_list_v3_write; struct transentry *te_list_v2_read; struct transentry *te_list_v2_write; }; static struct nfslog_trans_file *trans_file_head = NULL; static void nfslog_print_trans_logentry(struct transentry *, struct nfslog_trans_file *); static struct netbuf * netbufdup(struct netbuf *pnb) { struct netbuf *pnewnb; uint32_t size; size = offsetof(struct netbuf, buf); size += pnb->len; if ((pnewnb = (struct netbuf *)malloc(sizeof (*pnewnb))) == NULL) return (NULL); if ((pnewnb->buf = malloc(pnb->len)) == NULL) { free(pnewnb); return (NULL); } pnewnb->maxlen = pnb->maxlen; pnewnb->len = pnb->len; bcopy(pnb->buf, pnewnb->buf, pnb->len); return (pnewnb); } static void freenetbuf(struct netbuf *pnb) { free(pnb->buf); free(pnb); } static struct transentry * create_te() { struct transentry *pte; if ((pte = (struct transentry *)calloc(1, sizeof (*pte))) == NULL) { /* failure message or action */ return (NULL); } pte->next = pte->prev = NULL; return (pte); } static struct transentry * insert_te( struct transentry *te_list, struct transentry *entry) { struct transentry *pte; /* * First check for any non-filehandle comparisons that may be needed. */ switch (entry->optype) { case TRANS_OPER_REMOVE: case TRANS_OPER_RENAME: for (pte = te_list->next; pte != te_list; pte = pte->next) { /* if path names match, then return */ if (strcmp(pte->pathname, entry->pathname) == 0) { return (pte); } } return (NULL); default: break; } for (pte = te_list->next; pte != te_list; pte = pte->next) { /* If the file handles match, then we have a hit */ if (entry->nfsvers == NFS_VERSION) { if (bcmp(&(pte->fh_u.fh), &(entry->fh_u.fh), sizeof (fhandle_t)) == 0) { switch (entry->optype) { case TRANS_OPER_READ: case TRANS_OPER_WRITE: if (pte->uid == entry->uid) { return (pte); } break; default: return (pte); } } } else { if (pte->fh_u.fh3.fh3_length == entry->fh_u.fh3.fh3_length && bcmp(pte->fh_u.fh3.fh3_u.data, entry->fh_u.fh3.fh3_u.data, pte->fh_u.fh3.fh3_length) == 0) switch (entry->optype) { case TRANS_OPER_READ: case TRANS_OPER_WRITE: if (pte->uid == entry->uid) { return (pte); } break; default: return (pte); } } } /* * XXX - should compare more of the information to make sure * it is a match. */ /* * other operation types do not generate an entry for * further analysis */ switch (entry->optype) { case TRANS_OPER_READ: case TRANS_OPER_WRITE: break; default: return (NULL); } insque(entry, te_list); return (NULL); /* NULL signifies insertion and no record found */ } static void remove_te(struct transentry *pte) { if (pte->next) remque(pte); if (pte->principal_name) free(pte->principal_name); if (pte->pathname) free(pte->pathname); if (pte->pnb) freenetbuf(pte->pnb); if (pte->netid) free(pte->netid); free(pte); } /* * nfslog_trans_file_free - frees a record */ static void nfslog_trans_file_free(struct nfslog_trans_file *transrec) { if (transrec == NULL) return; if (transrec->path != NULL) { if (debug) (void) printf("freeing transpath '%s'\n", transrec->path); free(transrec->path); } free(transrec); } /* * On success returns a pointer to the trans_file that matches * 'path', 'output_type' and 'transtolog'. The reference count for this * object is incremented as well. * Returns NULL if it is not in the list. */ static struct nfslog_trans_file * nfslog_trans_file_find( char *path, uint32_t output_type, uint32_t transtolog) { struct nfslog_trans_file *tfp; for (tfp = trans_file_head; tfp != NULL; tfp = tfp->next) { if ((strcmp(path, tfp->path) == 0) && (output_type == tfp->trans_output_type) && (transtolog == tfp->trans_to_log)) { if (debug) (void) printf("Found transfile '%s'\n", path); (tfp->refcnt)++; return (tfp); } } return (NULL); } /* * nfslog_close_trans_file - decrements the reference count on * this object. On last reference it closes transfile and * frees resources */ static void nfslog_close_trans_file(struct nfslog_trans_file *tf) { assert(tf != NULL); assert(tf->refcnt > 0); if (tf->refcnt > 1) { (tf->refcnt)--; return; } if (tf->fp != NULL) { (void) fsync(fileno(tf->fp)); (void) fclose(tf->fp); } /* * Disconnect from list */ tf->prev->next = tf->next; if (tf->next != NULL) tf->next->prev = tf->prev; /* * Adjust the head of the list if appropriate */ if (tf == trans_file_head) trans_file_head = tf->next; nfslog_trans_file_free(tf); } /* * nfslog_open_trans_file - open the output trans file and mallocs. * The object is then inserted at the beginning of the global * transfile list. * Returns 0 for success, error else. * * *error contains the last error encountered on this object. It can * be used to avoid reporting the same error endlessly, by comparing * the current error to the last error. It is reset to the current error * code on return. */ void * nfslog_open_trans_file( char *transpath, uint32_t output_type, uint32_t transtolog, int *error) { int preverror = *error; struct nfslog_trans_file *transrec; transrec = nfslog_trans_file_find(transpath, output_type, transtolog); if (transrec != NULL) return (transrec); if ((transrec = malloc(sizeof (*transrec))) == NULL) { *error = errno; if (*error != preverror) { syslog(LOG_ERR, gettext("nfslog_open_trans_file: %s"), strerror(*error)); } return (NULL); } bzero(transrec, sizeof (*transrec)); if ((transrec->path = strdup(transpath)) == NULL) { *error = errno; if (*error != preverror) { syslog(LOG_ERR, gettext("nfslog_open_trans_file: %s"), strerror(*error)); } nfslog_trans_file_free(transrec); return (NULL); } if ((transrec->fp = fopen(transpath, "a")) == NULL) { *error = errno; if (*error != preverror) { syslog(LOG_ERR, gettext("Cannot open '%s': %s"), transpath, strerror(*error)); } nfslog_trans_file_free(transrec); return (NULL); } transrec->te_list_v3_read = (struct transentry *)malloc(sizeof (struct transentry)); transrec->te_list_v3_write = (struct transentry *)malloc(sizeof (struct transentry)); transrec->te_list_v2_read = (struct transentry *)malloc(sizeof (struct transentry)); transrec->te_list_v2_write = (struct transentry *)malloc(sizeof (struct transentry)); if (transrec->te_list_v3_read == NULL || transrec->te_list_v3_write == NULL || transrec->te_list_v2_read == NULL || transrec->te_list_v2_write == NULL) { if (transrec->te_list_v3_read) free(transrec->te_list_v3_read); if (transrec->te_list_v3_write) free(transrec->te_list_v3_write); if (transrec->te_list_v2_read) free(transrec->te_list_v2_read); if (transrec->te_list_v2_write) free(transrec->te_list_v2_write); nfslog_close_trans_file(transrec); return (NULL); } transrec->te_list_v3_read->next = transrec->te_list_v3_read->prev = transrec->te_list_v3_read; transrec->te_list_v3_write->next = transrec->te_list_v3_write->prev = transrec->te_list_v3_write; transrec->te_list_v2_read->next = transrec->te_list_v2_read->prev = transrec->te_list_v2_read; transrec->te_list_v2_write->next = transrec->te_list_v2_write->prev = transrec->te_list_v2_write; /* * Indicate what transaction types to log */ transrec->trans_to_log = transtolog; /* * Indicate whether to print 'full' or 'basic' version * of the transactions */ transrec->trans_output_type = output_type; /* * Insert at the beginning of the list. */ transrec->next = trans_file_head; if (trans_file_head != NULL) trans_file_head->prev = transrec; trans_file_head = transrec->prev = transrec; transrec->refcnt = 1; transrec->lasttrans_timestamp.tv_sec = 0; transrec->lasttrans_timestamp.tv_nsec = 0; transrec->last_trans_read = time(0); if (debug) (void) printf("New transfile '%s'\n", transrec->path); return (transrec); } void nfslog_process_trans_timeout( struct nfslog_trans_file *tf, uint32_t force_flush) { struct transentry *pte; time_t cur_time = time(0); /* * If we have not seen a transaction on this file for * a long time, then we need to flush everything out since * we may not be getting anything else in for awhile. */ if (difftime(cur_time, tf->last_trans_read) > (2 * MAX(TRANS_ENTRY_TIMEOUT, idle_time))) force_flush = TRUE; restart1: for (pte = tf->te_list_v3_read->next; pte != tf->te_list_v3_read; pte = pte->next) { if (force_flush == TRUE || (difftime(tf->lasttrans_timestamp.tv_sec, pte->lastupdate.tv_sec) > MAX(TRANS_ENTRY_TIMEOUT, idle_time))) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); goto restart1; } } restart2: for (pte = tf->te_list_v3_write->next; pte != tf->te_list_v3_write; pte = pte->next) { if (force_flush == TRUE || (difftime(tf->lasttrans_timestamp.tv_sec, pte->lastupdate.tv_sec) > MAX(TRANS_ENTRY_TIMEOUT, idle_time))) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); goto restart2; } } restart3: for (pte = tf->te_list_v2_read->next; pte != tf->te_list_v2_read; pte = pte->next) { if (force_flush == TRUE || (difftime(tf->lasttrans_timestamp.tv_sec, pte->lastupdate.tv_sec) > MAX(TRANS_ENTRY_TIMEOUT, idle_time))) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); goto restart3; } } restart4: for (pte = tf->te_list_v2_write->next; pte != tf->te_list_v2_write; pte = pte->next) { if (force_flush == TRUE || (difftime(tf->lasttrans_timestamp.tv_sec, pte->lastupdate.tv_sec) > MAX(TRANS_ENTRY_TIMEOUT, idle_time))) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); goto restart4; } } (void) fflush(tf->fp); } /* * Flushes outstanding transactions to disk, and closes * the transaction log. */ void nfslog_close_transactions(void **transcookie) { assert(*transcookie != NULL); nfslog_process_trans_timeout( (struct nfslog_trans_file *)(*transcookie), TRUE); nfslog_close_trans_file((struct nfslog_trans_file *)(*transcookie)); *transcookie = NULL; } static struct transentry * trans_read( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_nfsreadargs *args = (nfslog_nfsreadargs *)logrec->re_rpc_arg; /* LINTED */ nfslog_rdresult *res = (nfslog_rdresult *)logrec->re_rpc_res; if (res->r_status != NFS_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { newte->pathname = nfslog_get_path(&args->ra_fhandle, NULL, fhpath, "trans_read"); } else { newte->pathname = strdup(path1); } /* prep the struct for insertion */ newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_READ; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_VERSION; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = res->nfslog_rdresult_u.r_ok.rrok_count; newte->fh_u.fh = *(NFSLOG_GET_FHANDLE2(&args->ra_fhandle)); if (res->nfslog_rdresult_u.r_ok.rrok_count < res->nfslog_rdresult_u.r_ok.filesize) { if (pte = insert_te(tf->te_list_v2_read, newte)) { /* free this since entry was found (not inserted) */ remove_te(newte); pte->totalbytes += res->nfslog_rdresult_u.r_ok.rrok_count; if (pte->lastupdate.tv_sec <= logrec->re_header.rh_timestamp.tv_sec) pte->lastupdate = logrec->re_header.rh_timestamp; if (pte->totalbytes < res->nfslog_rdresult_u.r_ok.filesize) { pte = NULL; /* prevent printing of log entry */ } } } else { pte = newte; /* print a log record - complete file read */ } return (pte); } static struct transentry * trans_write( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_writeargs *args = (nfslog_writeargs *)logrec->re_rpc_arg; /* LINTED */ nfslog_writeresult *res = (nfslog_writeresult *)logrec->re_rpc_res; if (res->wr_status != NFS_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { newte->pathname = nfslog_get_path(&args->waargs_fhandle, NULL, fhpath, "trans_write"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_WRITE; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_VERSION; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = args->waargs_totcount; newte->fh_u.fh = *(NFSLOG_GET_FHANDLE2(&args->waargs_fhandle)); if (pte = insert_te(tf->te_list_v2_write, newte)) { /* * if the write would have increased the total byte count * over the filesize, then generate a log entry and remove * the write record and insert the new one. */ if (pte->totalbytes + args->waargs_totcount > res->nfslog_writeresult_u.wr_size) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); (void) insert_te(tf->te_list_v2_write, newte); pte = NULL; } else { /* free this since entry was found (not inserted) */ remove_te(newte); pte->totalbytes += args->waargs_totcount; if (pte->lastupdate.tv_sec <= logrec->re_header.rh_timestamp.tv_sec) { pte->lastupdate = logrec->re_header.rh_timestamp; } pte = NULL; /* prevent printing of log entry */ } } return (pte); } static struct transentry * trans_setattr( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_setattrargs *args = (nfslog_setattrargs *)logrec->re_rpc_arg; /* LINTED */ nfsstat *res = (nfsstat *)logrec->re_rpc_res; if (*res != NFS_OK) return (NULL); if (args->saa_sa.sa_size == (uint32_t)-1) return (NULL); /* * should check the size of the file to see if it * is being truncated below current eof. if so * a record should be generated.... XXX */ if (args->saa_sa.sa_size != 0) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { newte->pathname = nfslog_get_path(&args->saa_fh, NULL, fhpath, "trans_setattr2"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_SETATTR; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_VERSION; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh = *(NFSLOG_GET_FHANDLE2(&args->saa_fh)); if (pte = insert_te(tf->te_list_v2_write, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } if (pte = insert_te(tf->te_list_v2_read, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } return (newte); } static struct transentry * trans_create( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_createargs *args = (nfslog_createargs *)logrec->re_rpc_arg; /* LINTED */ nfslog_diropres *res = (nfslog_diropres *)logrec->re_rpc_res; if (res->dr_status != NFS_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { newte->pathname = nfslog_get_path(&args->ca_da.da_fhandle, args->ca_da.da_name, fhpath, "trans_create2"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_CREATE; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_VERSION; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; if (args->ca_sa.sa_size == (uint32_t)-1) newte->totalbytes = 0; else newte->totalbytes = args->ca_sa.sa_size; newte->fh_u.fh = *(NFSLOG_GET_FHANDLE2( &res->nfslog_diropres_u.dr_ok.drok_fhandle)); /* * if the file is being truncated on create, we need to flush * any outstanding read/write transactions */ if (args->ca_sa.sa_size != (uint32_t)-1) { if (pte = insert_te(tf->te_list_v2_write, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } if (pte = insert_te(tf->te_list_v2_read, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } } return (newte); } static struct transentry * trans_remove( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_diropargs *args = (nfslog_diropargs *)logrec->re_rpc_arg; /* LINTED */ nfsstat *res = (nfsstat *)logrec->re_rpc_res; if (*res != NFS_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { char *name = args->da_name; fhandle_t *dfh = &args->da_fhandle; newte->pathname = nfslog_get_path(dfh, name, fhpath, "trans_remove2"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_REMOVE; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_VERSION; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh = *(NFSLOG_GET_FHANDLE2(&args->da_fhandle)); if (pte = insert_te(tf->te_list_v2_write, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } if (pte = insert_te(tf->te_list_v2_read, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } if (pte = insert_te(tf->te_list_v3_write, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } if (pte = insert_te(tf->te_list_v3_read, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } return (newte); } static struct transentry * trans_mkdir( nfslog_request_record *logrec, char *fhpath, char *path1) { struct transentry *newte; /* LINTED */ nfslog_createargs *args = (nfslog_createargs *)logrec->re_rpc_arg; /* LINTED */ nfslog_diropres *res = (nfslog_diropres *)logrec->re_rpc_res; if (res->dr_status != NFS_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { nfslog_diropargs *dargs = &args->ca_da; char *name = dargs->da_name; fhandle_t *dfh = &dargs->da_fhandle; newte->pathname = nfslog_get_path(dfh, name, fhpath, "trans_mkdir2"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_MKDIR; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_VERSION; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh = *(NFSLOG_GET_FHANDLE2(&args->ca_da.da_fhandle)); return (newte); } static struct transentry * trans_rmdir( nfslog_request_record *logrec, char *fhpath, char *path1) { struct transentry *newte; /* LINTED */ nfslog_diropargs *args = (nfslog_diropargs *)logrec->re_rpc_arg; /* LINTED */ nfsstat *res = (nfsstat *)logrec->re_rpc_res; if (*res != NFS_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { char *name = args->da_name; fhandle_t *dfh = &args->da_fhandle; newte->pathname = nfslog_get_path(dfh, name, fhpath, "trans_rmdir2"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_RMDIR; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_VERSION; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh = *(NFSLOG_GET_FHANDLE2(&args->da_fhandle)); return (newte); } static struct transentry * trans_rename( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1, char *path2) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_rnmargs *args = (nfslog_rnmargs *)logrec->re_rpc_arg; /* LINTED */ nfsstat *res = (nfsstat *)logrec->re_rpc_res; char *tpath1 = NULL; char *tpath2 = NULL; if (*res != NFS_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { char *from_name, *to_name; fhandle_t *from_dfh, *to_dfh; 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; path1 = tpath1 = nfslog_get_path(from_dfh, from_name, fhpath, "trans_rename from"); path2 = tpath2 = nfslog_get_path(to_dfh, to_name, fhpath, "trans_rename to"); } newte->pathname = path1; /* no need to strdup here */ newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_RENAME; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_VERSION; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh = *(NFSLOG_GET_FHANDLE2(&args->rna_from.da_fhandle)); /* switch path names for the file for renames */ if (pte = insert_te(tf->te_list_v2_write, newte)) { free(pte->pathname); pte->pathname = strdup(path2); } if (pte = insert_te(tf->te_list_v2_read, newte)) { free(pte->pathname); pte->pathname = strdup(path2); } if (pte = insert_te(tf->te_list_v3_write, newte)) { free(pte->pathname); pte->pathname = strdup(path2); } if (pte = insert_te(tf->te_list_v3_read, newte)) { free(pte->pathname); pte->pathname = strdup(path2); } newte->pathname = (char *)malloc(strlen(path1) + strlen(path2) + 3); /* check for NULL malloc */ (void) sprintf(newte->pathname, "%s->%s", path1, path2); if (tpath1) { free(tpath1); free(tpath2); } return (newte); } static struct transentry * trans_link( nfslog_request_record *logrec, char *fhpath, char *path1, char *path2) { struct transentry *newte; /* LINTED */ nfslog_linkargs *args = (nfslog_linkargs *)logrec->re_rpc_arg; /* LINTED */ nfsstat *res = (nfsstat *)logrec->re_rpc_res; char *tpath1 = NULL; char *tpath2 = NULL; if (*res != NFS_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { fhandle_t *fh = &args->la_from; char *name = args->la_to.da_name; fhandle_t *dfh = &args->la_to.da_fhandle; path1 = tpath1 = nfslog_get_path(fh, NULL, fhpath, "trans_link from"); path2 = tpath2 = nfslog_get_path(dfh, name, fhpath, "trans_link to"); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_LINK; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_VERSION; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh = *(NFSLOG_GET_FHANDLE2(&args->la_from)); newte->pathname = (char *)malloc(strlen(path1) + strlen(path2) + 3); /* check for NULL malloc */ (void) sprintf(newte->pathname, "%s->%s", path1, path2); if (tpath1) { free(tpath1); free(tpath2); } return (newte); } static struct transentry * trans_symlink( nfslog_request_record *logrec, char *fhpath, char *path1) { struct transentry *newte; /* LINTED */ nfslog_symlinkargs *args = (nfslog_symlinkargs *)logrec->re_rpc_arg; /* LINTED */ nfsstat *res = (nfsstat *)logrec->re_rpc_res; char *tpath1 = NULL; if (*res != NFS_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { char *name = args->sla_from.da_name; fhandle_t *dfh = &args->sla_from.da_fhandle; path1 = tpath1 = nfslog_get_path(dfh, name, fhpath, "trans_symlink"); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_SYMLINK; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_VERSION; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh = *(NFSLOG_GET_FHANDLE2(&args->sla_from.da_fhandle)); newte->pathname = (char *)malloc(strlen(path1) + strlen(args->sla_tnm) + 3); (void) sprintf(newte->pathname, "%s->%s", path1, args->sla_tnm); if (tpath1) free(tpath1); return (newte); } static struct transentry * trans_read3( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_READ3args *args = (nfslog_READ3args *)logrec->re_rpc_arg; /* LINTED */ nfslog_READ3res *res = (nfslog_READ3res *)logrec->re_rpc_res; if (res->status != NFS3_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { fhandle_t *fh = NFSLOG_GET_FHANDLE3(&args->file); newte->pathname = nfslog_get_path(fh, NULL, fhpath, "trans_read3"); } else { newte->pathname = strdup(path1); } /* prep the struct for insertion */ newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_READ; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = res->nfslog_READ3res_u.ok.count; newte->fh_u.fh3 = args->file; if (res->nfslog_READ3res_u.ok.count < res->nfslog_READ3res_u.ok.filesize) { if (pte = insert_te(tf->te_list_v3_read, newte)) { /* free this since entry was found (not inserted) */ remove_te(newte); pte->totalbytes += res->nfslog_READ3res_u.ok.count; if (pte->lastupdate.tv_sec <= logrec->re_header.rh_timestamp.tv_sec) pte->lastupdate = logrec->re_header.rh_timestamp; if (pte->totalbytes < res->nfslog_READ3res_u.ok.filesize) { pte = NULL; /* prevent printing of log entry */ } } } else { pte = newte; /* print a log record - complete file read */ } return (pte); } static struct transentry * trans_write3( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_WRITE3args *args = (nfslog_WRITE3args *)logrec->re_rpc_arg; /* LINTED */ nfslog_WRITE3res *res = (nfslog_WRITE3res *)logrec->re_rpc_res; if (res->status != NFS3_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { fhandle_t *fh = NFSLOG_GET_FHANDLE3(&args->file); newte->pathname = nfslog_get_path(fh, NULL, fhpath, "trans_write3"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_WRITE; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = res->nfslog_WRITE3res_u.ok.count; newte->fh_u.fh3 = args->file; if (pte = insert_te(tf->te_list_v3_write, newte)) { /* * if the write would have increased the total byte count * over the filesize, then generate a log entry and remove * the write record and insert the new one. */ if (pte->totalbytes + res->nfslog_WRITE3res_u.ok.count > res->nfslog_WRITE3res_u.ok.filesize) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); (void) insert_te(tf->te_list_v3_write, newte); pte = NULL; } else { /* free this since entry was found (not inserted) */ remove_te(newte); pte->totalbytes += res->nfslog_WRITE3res_u.ok.count; if (pte->lastupdate.tv_sec <= logrec->re_header.rh_timestamp.tv_sec) { pte->lastupdate = logrec->re_header.rh_timestamp; } pte = NULL; /* prevent printing of log entry */ } } return (pte); } static struct transentry * trans_setattr3( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_SETATTR3args *args = (nfslog_SETATTR3args *)logrec->re_rpc_arg; /* LINTED */ nfsstat3 *res = (nfsstat3 *)logrec->re_rpc_res; if (*res != NFS3_OK) return (NULL); if (!args->size.set_it) return (NULL); /* * should check the size of the file to see if it * is being truncated below current eof. if so * a record should be generated.... XXX */ if (args->size.size != 0) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { fhandle_t *fh = NFSLOG_GET_FHANDLE3(&args->object); newte->pathname = nfslog_get_path(fh, NULL, fhpath, "trans_setattr3"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_SETATTR; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh3 = args->object; if (pte = insert_te(tf->te_list_v3_write, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } if (pte = insert_te(tf->te_list_v3_read, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } return (newte); } static struct transentry * trans_create3( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_CREATE3args *args = (nfslog_CREATE3args *)logrec->re_rpc_arg; /* LINTED */ nfslog_CREATE3res *res = (nfslog_CREATE3res *)logrec->re_rpc_res; if (res->status != NFS3_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { newte->pathname = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->where.dir), args->where.name, fhpath, "trans_create3"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_CREATE; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; if (!args->how.nfslog_createhow3_u.size.set_it) newte->totalbytes = 0; else newte->totalbytes = args->how.nfslog_createhow3_u.size.size; newte->fh_u.fh3 = args->where.dir; if (args->how.nfslog_createhow3_u.size.set_it) { if (pte = insert_te(tf->te_list_v3_write, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } if (pte = insert_te(tf->te_list_v3_read, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } } return (newte); } static struct transentry * trans_remove3( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_REMOVE3args *args = (nfslog_REMOVE3args *)logrec->re_rpc_arg; /* LINTED */ nfsstat3 *res = (nfsstat3 *)logrec->re_rpc_res; if (*res != NFS3_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { newte->pathname = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->object.dir), args->object.name, fhpath, "trans_remove3"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_REMOVE; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh3 = args->object.dir; if (pte = insert_te(tf->te_list_v3_write, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } if (pte = insert_te(tf->te_list_v3_read, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } if (pte = insert_te(tf->te_list_v2_write, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } if (pte = insert_te(tf->te_list_v2_read, newte)) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } return (newte); } static struct transentry * trans_mkdir3( nfslog_request_record *logrec, char *fhpath, char *path1) { struct transentry *newte; /* LINTED */ nfslog_MKDIR3args *args = (nfslog_MKDIR3args *)logrec->re_rpc_arg; /* LINTED */ nfslog_MKDIR3res *res = (nfslog_MKDIR3res *)logrec->re_rpc_res; if (res->status != NFS3_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { newte->pathname = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->where.dir), args->where.name, fhpath, "trans_mkdir3"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_MKDIR; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh3 = args->where.dir; return (newte); } static struct transentry * trans_rmdir3( nfslog_request_record *logrec, char *fhpath, char *path1) { struct transentry *newte; /* LINTED */ nfslog_RMDIR3args *args = (nfslog_RMDIR3args *)logrec->re_rpc_arg; /* LINTED */ nfsstat3 *res = (nfsstat3 *)logrec->re_rpc_res; if (*res != NFS3_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { newte->pathname = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->object.dir), args->object.name, fhpath, "trans_rmdir3"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_RMDIR; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh3 = args->object.dir; return (newte); } static struct transentry * trans_rename3( nfslog_request_record *logrec, struct nfslog_trans_file *tf, char *fhpath, char *path1, char *path2) { struct transentry *newte; struct transentry *pte = NULL; /* LINTED */ nfslog_RENAME3args *args = (nfslog_RENAME3args *)logrec->re_rpc_arg; /* LINTED */ nfsstat3 *res = (nfsstat3 *)logrec->re_rpc_res; char *tpath1 = NULL; char *tpath2 = NULL; if (*res != NFS3_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { path1 = tpath1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->from.dir), args->from.name, fhpath, "trans_rename3 from"); path2 = tpath2 = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->to.dir), args->to.name, fhpath, "trans_rename3 to"); } newte->pathname = path1; /* no need to strdup here */ newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_RENAME; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh3 = args->from.dir; /* switch path names for the file for renames */ if (pte = insert_te(tf->te_list_v3_write, newte)) { free(pte->pathname); pte->pathname = strdup(path2); } if (pte = insert_te(tf->te_list_v3_read, newte)) { free(pte->pathname); pte->pathname = strdup(path2); } if (pte = insert_te(tf->te_list_v2_write, newte)) { free(pte->pathname); pte->pathname = strdup(path2); } if (pte = insert_te(tf->te_list_v2_read, newte)) { free(pte->pathname); pte->pathname = strdup(path2); } newte->pathname = (char *)malloc(strlen(path1) + strlen(path2) + 3); /* check for NULL malloc */ (void) sprintf(newte->pathname, "%s->%s", path1, path2); if (tpath1) { free(tpath1); free(tpath2); } return (newte); } static struct transentry * trans_mknod3( nfslog_request_record *logrec, char *fhpath, char *path1) { struct transentry *newte; /* LINTED */ nfslog_MKNOD3args *args = (nfslog_MKNOD3args *)logrec->re_rpc_arg; /* LINTED */ nfslog_MKNOD3res *res = (nfslog_MKNOD3res *)logrec->re_rpc_res; if (res->status != NFS3_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { newte->pathname = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->where.dir), args->where.name, fhpath, "trans_mknod3"); } else { newte->pathname = strdup(path1); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_MKNOD; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh3 = args->where.dir; return (newte); } static struct transentry * trans_link3( nfslog_request_record *logrec, char *fhpath, char *path1, char *path2) { struct transentry *newte; /* LINTED */ nfslog_LINK3args *args = (nfslog_LINK3args *)logrec->re_rpc_arg; /* LINTED */ nfsstat3 *res = (nfsstat3 *)logrec->re_rpc_res; char *tpath1 = NULL; char *tpath2 = NULL; if (*res != NFS3_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (!path1) { tpath1 = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->file), NULL, fhpath, "trans_link3 from"); tpath2 = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->link.dir), args->link.name, fhpath, "trans_link3 to"); path1 = tpath1; path2 = tpath2; } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_LINK; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh3 = args->file; newte->pathname = (char *)malloc(strlen(path1) + strlen(path2) + 3); /* check for NULL malloc */ (void) sprintf(newte->pathname, "%s->%s", path1, path2); if (tpath1) { free(tpath1); free(tpath2); } return (newte); } static struct transentry * trans_symlink3( nfslog_request_record *logrec, char *fhpath, char *path1) { struct transentry *newte; /* LINTED */ nfslog_SYMLINK3args *args = (nfslog_SYMLINK3args *)logrec->re_rpc_arg; /* LINTED */ nfslog_SYMLINK3res *res = (nfslog_SYMLINK3res *)logrec->re_rpc_res; char *name; if (res->status != NFS3_OK) return (NULL); if ((newte = create_te()) == NULL) return (NULL); if (path1) { name = strdup(path1); } else { name = nfslog_get_path(NFSLOG_GET_FHANDLE3(&args->where.dir), args->where.name, fhpath, "trans_symlink3"); } newte->starttime = logrec->re_header.rh_timestamp; newte->lastupdate = logrec->re_header.rh_timestamp; newte->optype = TRANS_OPER_SYMLINK; newte->datatype = TRANS_DATATYPE_BINARY; newte->transoption = TRANS_OPTION_NOACTION; newte->pnb = netbufdup(&(logrec->re_ipaddr)); newte->uid = logrec->re_header.rh_uid; newte->nfsvers = NFS_V3; newte->netid = strdup(logrec->re_netid); if (logrec->re_principal_name) newte->principal_name = strdup(logrec->re_principal_name); else newte->principal_name = NULL; newte->totalbytes = 0; newte->fh_u.fh3 = args->where.dir; newte->pathname = (char *)malloc(strlen(name) + strlen(args->symlink_data) + 3); /* check for NULL malloc */ (void) sprintf(newte->pathname, "%s->%s", name, args->symlink_data); free(name); return (newte); } /* * nfslog_process_trans_rec - processes the record in the buffer and outputs * to the trans log. * Return 0 for success, errno else. */ int nfslog_process_trans_rec(void *transcookie, nfslog_request_record *logrec, char *fhpath, char *path1, char *path2) { struct transentry *pte = NULL; struct nfslog_trans_file *tf = (struct nfslog_trans_file *)transcookie; /* ignore programs other than nfs */ if (logrec->re_header.rh_prognum != NFS_PROGRAM) return (0); /* update the timestamp for use later in the timeout sequences */ if (tf->lasttrans_timestamp.tv_sec < logrec->re_header.rh_timestamp.tv_sec) tf->lasttrans_timestamp = logrec->re_header.rh_timestamp; /* current time of this processing */ tf->last_trans_read = time(0); /* ignore anything that is not a read or write */ switch (logrec->re_header.rh_version) { case NFS_VERSION: switch (logrec->re_header.rh_procnum) { case RFS_READ: if (tf->trans_to_log & TRANSTOLOG_OPER_READ) pte = trans_read(logrec, tf, fhpath, path1); break; case RFS_WRITE: if (tf->trans_to_log & TRANSTOLOG_OPER_WRITE) pte = trans_write(logrec, tf, fhpath, path1); break; case RFS_SETATTR: if (tf->trans_to_log & TRANSTOLOG_OPER_SETATTR) pte = trans_setattr(logrec, tf, fhpath, path1); break; case RFS_REMOVE: if (tf->trans_to_log & TRANSTOLOG_OPER_REMOVE) pte = trans_remove(logrec, tf, fhpath, path1); break; case RFS_MKDIR: if (tf->trans_to_log & TRANSTOLOG_OPER_MKDIR) pte = trans_mkdir(logrec, fhpath, path1); break; case RFS_RMDIR: if (tf->trans_to_log & TRANSTOLOG_OPER_RMDIR) pte = trans_rmdir(logrec, fhpath, path1); break; case RFS_CREATE: if (tf->trans_to_log & TRANSTOLOG_OPER_CREATE) pte = trans_create(logrec, tf, fhpath, path1); break; case RFS_RENAME: if (tf->trans_to_log & TRANSTOLOG_OPER_RENAME) pte = trans_rename(logrec, tf, fhpath, path1, path2); break; case RFS_LINK: if (tf->trans_to_log & TRANSTOLOG_OPER_LINK) pte = trans_link(logrec, fhpath, path1, path2); break; case RFS_SYMLINK: if (tf->trans_to_log & TRANSTOLOG_OPER_SYMLINK) pte = trans_symlink(logrec, fhpath, path1); break; default: break; } break; case NFS_V3: switch (logrec->re_header.rh_procnum) { case NFSPROC3_READ: if (tf->trans_to_log & TRANSTOLOG_OPER_READ) pte = trans_read3(logrec, tf, fhpath, path1); break; case NFSPROC3_WRITE: if (tf->trans_to_log & TRANSTOLOG_OPER_WRITE) pte = trans_write3(logrec, tf, fhpath, path1); break; case NFSPROC3_SETATTR: if (tf->trans_to_log & TRANSTOLOG_OPER_SETATTR) pte = trans_setattr3(logrec, tf, fhpath, path1); break; case NFSPROC3_REMOVE: if (tf->trans_to_log & TRANSTOLOG_OPER_REMOVE) pte = trans_remove3(logrec, tf, fhpath, path1); break; case NFSPROC3_MKDIR: if (tf->trans_to_log & TRANSTOLOG_OPER_MKDIR) pte = trans_mkdir3(logrec, fhpath, path1); break; case NFSPROC3_RMDIR: if (tf->trans_to_log & TRANSTOLOG_OPER_RMDIR) pte = trans_rmdir3(logrec, fhpath, path1); break; case NFSPROC3_CREATE: if (tf->trans_to_log & TRANSTOLOG_OPER_CREATE) pte = trans_create3(logrec, tf, fhpath, path1); break; case NFSPROC3_RENAME: if (tf->trans_to_log & TRANSTOLOG_OPER_RENAME) pte = trans_rename3(logrec, tf, fhpath, path1, path2); break; case NFSPROC3_MKNOD: if (tf->trans_to_log & TRANSTOLOG_OPER_MKNOD) pte = trans_mknod3(logrec, fhpath, path1); break; case NFSPROC3_LINK: if (tf->trans_to_log & TRANSTOLOG_OPER_LINK) pte = trans_link3(logrec, fhpath, path1, path2); break; case NFSPROC3_SYMLINK: if (tf->trans_to_log & TRANSTOLOG_OPER_SYMLINK) pte = trans_symlink3(logrec, fhpath, path1); break; default: break; } break; default: break; } if (pte != NULL) { nfslog_print_trans_logentry(pte, tf); remove_te(pte); } return (0); } static void nfslog_print_trans_logentry(struct transentry *pte, struct nfslog_trans_file *tf) { char *remotehost; char datatype; char transoption; char *optype; char *prin; int prinid; char nfs_ident[32]; remotehost = addrtoname(pte->pnb->buf); datatype = (pte->datatype == TRANS_DATATYPE_BINARY ? 'b' : 'a'); transoption = (pte->transoption == TRANS_OPTION_NOACTION ? '_' : '?'); if (tf->trans_output_type == TRANSLOG_BASIC) { (void) strcpy(nfs_ident, "nfs"); } else { (void) strcpy(nfs_ident, (pte->nfsvers == NFS_V3 ? "nfs3-" : "nfs-")); (void) strcat(nfs_ident, pte->netid); } switch (pte->optype) { case TRANS_OPER_READ: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "read" : "o"); break; case TRANS_OPER_WRITE: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "write" : "i"); break; case TRANS_OPER_REMOVE: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "remove" : "?"); break; case TRANS_OPER_MKDIR: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "mkdir" : "?"); break; case TRANS_OPER_CREATE: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "create" : "?"); break; case TRANS_OPER_RMDIR: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "rmdir" : "?"); break; case TRANS_OPER_SETATTR: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "setattr" : "?"); break; case TRANS_OPER_RENAME: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "rename" : "?"); break; case TRANS_OPER_MKNOD: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "mknod" : "?"); break; case TRANS_OPER_LINK: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "link" : "?"); break; case TRANS_OPER_SYMLINK: optype = (tf->trans_output_type == TRANSLOG_EXTENDED ? "symlink" : "?"); break; default: optype = "?"; break; } if (strcmp(pte->principal_name, "") == 0) { prinid = 0; prin = "*"; } else { prinid = 1; prin = pte->principal_name; } (void) fprintf(tf->fp, "%.24s %d %s %d %s %c %c %s %c %ld %s %d %s\n", ctime((time_t *)&pte->starttime.tv_sec), pte->lastupdate.tv_sec - pte->starttime.tv_sec, remotehost, (uint32_t)pte->totalbytes, pte->pathname, datatype, transoption, optype, 'r', /* anonymous == 'a', guest == 'g', real == 'r'), */ pte->uid, nfs_ident, /* authenticated - fill in kerb/security? */ prinid, /* authenticated ? authuser : "*" */ prin); } /* * 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-2000 by Sun Microsystems, Inc. * All rights reserved. */ /* * Postprocessor for NFS server logging. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fhtab.h" #include "nfslogd.h" #include "buffer_list.h" #include "../lib/nfslog_config.h" #include "../lib/nfslogtab.h" enum pidfile_operation { PID_STARTUP, PID_SHUTDOWN }; static int nfslogtab_deactivate_after_boot(void); static int nfslogtab_remove(struct buffer_ent **, struct buffer_ent **, boolean_t); static int cycle_logs(nfsl_config_t *, int); static void enable_logcycling(void); static int process_pidfile(enum pidfile_operation); static void short_cleanup(void); static void full_cleanup(void); static void transactions_timeout(nfsl_config_t *); static void close_all_translogs(nfsl_config_t *); int cycle_log(char *, int); static boolean_t is_cycle_needed(char *, void **, boolean_t, int *); /* * Configuration information. */ int debug = 0; boolean_t test = B_FALSE; time_t mapping_update_interval = MAPPING_UPDATE_INTERVAL; /* prune_timeout measures how old a database entry must be to be pruned */ time_t prune_timeout = (SECSPERHOUR * 7 * 24); int max_logs_preserve = MAX_LOGS_PRESERVE; uint_t idle_time = IDLE_TIME; static mode_t Umask = NFSLOG_UMASK; static long cycle_frequency = CYCLE_FREQUENCY; /* prune_frequency measures how often should prune_dbs be called */ static long prune_frequency = (SECSPERHOUR * 24); static int min_size = MIN_PROCESSING_SIZE; static volatile bool_t need2cycle = FALSE; static volatile bool_t need2prune = FALSE; boolean_t keep_running = B_TRUE; boolean_t quick_cleaning = B_FALSE; /*ARGSUSED*/ int main(int argc, char **argv) { struct rlimit rl; int error = 0; char *defp; pid_t pid; timestruc_t logtab_update; time_t process_start, last_prune = time(0); time_t last_cycle = time(0); /* last time logs were cycled */ int processed, buffers_processed; struct buffer_ent *buffer_list = NULL, *bep, *next; nfsl_config_t *config_list = NULL; char *fhtable_to_prune = NULL; /* * Check to make sure user is root. */ if (geteuid() != 0) { (void) fprintf(stderr, gettext("%s must be run as root\n"), argv[0]); exit(1); } /* * Read defaults file. */ if (defopen(NFSLOG_OPTIONS_FILE) == 0) { if ((defp = defread("DEBUG=")) != NULL) { debug = atoi(defp); if (debug > 0) (void) printf("debug=%d\n", debug); } if ((defp = defread("TEST=")) != NULL) { if (strcmp(defp, "TRUE") == 0) test = B_TRUE; if (debug > 0) { if (test) (void) printf("test=TRUE\n"); else (void) printf("test=FALSE\n"); } } /* * Set Umask for log and fhtable creation. */ if ((defp = defread("UMASK=")) != NULL) { if (sscanf(defp, "%lo", &Umask) != 1) Umask = NFSLOG_UMASK; } /* * Minimum size buffer should reach before processing. */ if ((defp = defread("MIN_PROCESSING_SIZE=")) != NULL) { min_size = atoi(defp); if (debug > 0) (void) printf("min_size=%d\n", min_size); } /* * Number of seconds the daemon should * sleep waiting for more work. */ if ((defp = defread("IDLE_TIME=")) != NULL) { idle_time = (uint_t)atoi(defp); if (debug > 0) (void) printf("idle_time=%d\n", idle_time); } /* * Maximum number of logs to preserve. */ if ((defp = defread("MAX_LOGS_PRESERVE=")) != NULL) { max_logs_preserve = atoi(defp); if (debug > 0) { (void) printf("max_logs_preserve=%d\n", max_logs_preserve); } } /* * Frequency of atime updates. */ if ((defp = defread("MAPPING_UPDATE_INTERVAL=")) != NULL) { mapping_update_interval = atoi(defp); if (debug > 0) { (void) printf("mapping_update_interval=%ld\n", mapping_update_interval); } } /* * Time to remove entries */ if ((defp = defread("PRUNE_TIMEOUT=")) != NULL) { /* * Prune timeout is in hours but we want * deal with the time in seconds internally. */ prune_timeout = atoi(defp); prune_timeout *= SECSPERHOUR; if (prune_timeout < prune_frequency) prune_frequency = prune_timeout; if (debug > 0) { (void) printf("prune_timeout=%ld\n", prune_timeout); } } /* * fhtable to prune when start (for debug/test purposes) */ if ((defp = defread("PRUNE_FHTABLE=")) != NULL) { /* * Specify full pathname of fhtable to prune before * any processing is to be done */ if (fhtable_to_prune = malloc(strlen(defp) + 1)) { (void) strcpy(fhtable_to_prune, defp); if (debug > 0) { (void) printf("fhtable to prune=%s\n", fhtable_to_prune); } } else { syslog(LOG_ERR, gettext( "malloc fhtable_to_prune error %s\n"), strerror(errno)); } } /* * Log cycle frequency. */ if ((defp = defread("CYCLE_FREQUENCY=")) != NULL) { cycle_frequency = atol(defp); if (debug > 0) { (void) printf("cycle_frequency=%ld\n", cycle_frequency); } } /* * defopen of NULL closes the open defaults file. */ (void) defopen((char *)NULL); } if (Umask > ((mode_t)0777)) Umask = NFSLOG_UMASK; (void) umask(Umask); if (getrlimit(RLIMIT_FSIZE, &rl) < 0) { error = errno; (void) fprintf(stderr, gettext( "getrlimit failed error is %d - %s\n"), error, strerror(error)); exit(1); } if (min_size < 0 || min_size > rl.rlim_cur) { (void) fprintf(stderr, gettext( "MIN_PROCESSING_SIZE out of range, should be >= 0 and " "< %d. Check %s.\n"), rl.rlim_cur, NFSLOG_OPTIONS_FILE); exit(1); } if (idle_time > INT_MAX) { (void) fprintf(stderr, gettext( "IDLE_TIME out of range, should be >= 0 and " "< %d. Check %s.\n"), INT_MAX, NFSLOG_OPTIONS_FILE); exit(1); } if (max_logs_preserve < 0 || max_logs_preserve > INT_MAX) { (void) fprintf(stderr, gettext( "MAX_LOGS_PRESERVE out of range, should be >= 0 and " "< %d. Check %s.\n"), INT_MAX, NFSLOG_OPTIONS_FILE); exit(1); } if (mapping_update_interval < 0|| mapping_update_interval > INT_MAX) { (void) fprintf(stderr, gettext( "MAPPING_UPDATE_INTERVAL out of range, " "should be >= 0 and " "< %d. Check %s.\n"), INT_MAX, NFSLOG_OPTIONS_FILE); exit(1); } if (cycle_frequency < 0 || cycle_frequency > INT_MAX) { (void) fprintf(stderr, gettext( "CYCLE_FREQUENCY out of range, should be >= 0 and " "< %d. Check %s.\n"), INT_MAX, NFSLOG_OPTIONS_FILE); exit(1); } /* get value in seconds */ cycle_frequency = cycle_frequency * 60 * 60; /* * If we dump core, it will be /core */ if (chdir("/") < 0) (void) fprintf(stderr, gettext("chdir /: %s"), strerror(errno)); /* * Config errors to stderr */ nfsl_errs_to_syslog = B_FALSE; #ifndef DEBUG pid = fork(); if (pid == -1) { (void) fprintf(stderr, gettext("%s: fork failure\n"), argv[0]); exit(1); } if (pid != 0) exit(0); /* * Config errors to syslog */ nfsl_errs_to_syslog = B_TRUE; #endif /* DEBUG */ (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); /* * Check to see if nfslogd is already running. */ if (process_pidfile(PID_STARTUP) != 0) { exit(1); } (void) sigset(SIGUSR1, (void (*)(int))enable_logcycling); (void) sigset(SIGHUP, (void (*)(int))full_cleanup); (void) sigset(SIGTERM, (void(*)(int))short_cleanup); #ifndef DEBUG /* * Close existing file descriptors, open "/dev/null" as * standard input, output, and error, and detach from * controlling terminal. */ if (!debug && !test) { closefrom(0); (void) open("/dev/null", O_RDONLY); (void) open("/dev/null", O_WRONLY); (void) dup(1); } (void) setsid(); #endif /* DEBUG */ openlog(argv[0], LOG_PID, LOG_DAEMON); public_fh.fh_len = NFS_FHMAXDATA; public_fh.fh_xlen = NFS_FHMAXDATA; /* * Call once at startup to handle the nfslogtab */ if (nfslogtab_deactivate_after_boot() == -1) exit(1); /* * Get a list of buffers that need to be processed. */ if (error = getbuffer_list(&buffer_list, &logtab_update)) { syslog(LOG_ERR, gettext("Could not read %s: %s"), NFSLOGTAB, strerror(error)); goto done; } /* * Get the configuration list. */ if (error = nfsl_getconfig_list(&config_list)) { syslog(LOG_ERR, gettext( "Could not obtain configuration list: %s"), strerror(error)); goto done; } /* * loop to process the work being generated by the NFS server */ while (keep_running) { buffers_processed = 0; (void) checkbuffer_list(&buffer_list, &logtab_update); while (buffer_list == NULL) { /* * Nothing to do */ (void) sleep(idle_time); if (!keep_running) { /* * We have been interrupted and asked to * flush our transactions and exit. */ close_all_translogs(config_list); goto done; } (void) checkbuffer_list(&buffer_list, &logtab_update); } process_start = time(0); if (error = nfsl_checkconfig_list(&config_list, NULL)) { syslog(LOG_ERR, gettext( "Could not update configuration list: %s"), strerror(error)); nfsl_freeconfig_list(&config_list); goto done; } if (difftime(time(0), last_cycle) > cycle_frequency) need2cycle = TRUE; if (need2cycle) { error = cycle_logs(config_list, max_logs_preserve); if (error) { syslog(LOG_WARNING, gettext( "One or more logfiles couldn't be cycled, " "continuing regular processing")); } need2cycle = FALSE; last_cycle = time(0); } if (difftime(time(0), last_prune) > prune_frequency) need2prune = TRUE; if (need2prune || fhtable_to_prune) { error = prune_dbs(fhtable_to_prune); if (error) { syslog(LOG_WARNING, gettext( "Error in cleaning database files")); } need2prune = FALSE; last_prune = time(0); /* After the first time, use the normal procedure */ free(fhtable_to_prune); fhtable_to_prune = NULL; } for (bep = buffer_list; bep != NULL; bep = next) { next = bep->be_next; processed = 0; error = process_buffer(bep, &config_list, min_size, idle_time, &processed); if (error == 0 && processed) { if (bep->be_error) { syslog(LOG_ERR, gettext( "Buffer file '%s' " "processed successfully."), bep->be_name); } error = nfslogtab_remove(&buffer_list, &bep, B_FALSE); } else if (error == ENOENT) { syslog(LOG_ERR, gettext("Removed entry" "\t\"%s\t%s\t%d\" from %s"), bep->be_name, bep->be_sharepnt->se_name, bep->be_sharepnt->se_state, NFSLOGTAB); error = nfslogtab_remove(&buffer_list, &bep, B_TRUE); } else if (error && error != bep->be_error) { /* * An error different from what we've reported * before occured. */ syslog(LOG_ERR, gettext( "Cannot process buffer file '%s' - " "will retry on every iteration."), bep->be_name); } if (bep != NULL) bep->be_error = error; buffers_processed += processed; } transactions_timeout(config_list); if (keep_running) { uint_t process_time; /* * Sleep idle_time minus however long it took us * to process the buffers. */ process_time = (uint_t)(difftime(time(0), process_start)); if (process_time < idle_time) (void) sleep(idle_time - process_time); } } done: /* * Make sure to clean house before we exit */ close_all_translogs(config_list); free_buffer_list(&buffer_list); nfsl_freeconfig_list(&config_list); (void) process_pidfile(PID_SHUTDOWN); return (error); } static void short_cleanup(void) { if (debug) { (void) fprintf(stderr, "SIGTERM received, setting state to terminate...\n"); } quick_cleaning = B_TRUE; keep_running = B_FALSE; } static void full_cleanup(void) { if (debug) { (void) fprintf(stderr, "SIGHUP received, setting state to shutdown...\n"); } quick_cleaning = keep_running = B_FALSE; } /* * Removes nfslogtab entries matching the specified buffer_ent, * if 'inactive_only' is set, then only inactive entries are removed. * The buffer_list and sharepoint list entries are removed appropriately. * Returns 0 on success, error otherwise. */ static int nfslogtab_remove( struct buffer_ent **buffer_list, struct buffer_ent **bep, boolean_t allstates) { FILE *fd; int error = 0; struct sharepnt_ent *sep, *next; fd = fopen(NFSLOGTAB, "r+"); rewind(fd); if (fd == NULL) { error = errno; syslog(LOG_ERR, gettext("%s - %s\n"), NFSLOGTAB, strerror(error)); return (error); } if (lockf(fileno(fd), F_LOCK, 0L) < 0) { error = errno; syslog(LOG_ERR, gettext("cannot lock %s - %s\n"), NFSLOGTAB, strerror(error)); (void) fclose(fd); return (error); } for (sep = (*bep)->be_sharepnt; sep != NULL; sep = next) { next = sep->se_next; if (!allstates && sep->se_state == LES_ACTIVE) continue; if (error = logtab_rement(fd, (*bep)->be_name, sep->se_name, NULL, sep->se_state)) { syslog(LOG_ERR, gettext("cannot update %s\n"), NFSLOGTAB); error = EIO; goto errout; } remove_sharepnt_ent(&((*bep)->be_sharepnt), sep); } if ((*bep)->be_sharepnt == NULL) { /* * All sharepoints were removed from NFSLOGTAB. * Remove this buffer from our list. */ remove_buffer_ent(buffer_list, *bep); *bep = NULL; } errout: (void) fclose(fd); return (error); } /* * Deactivates entries if nfslogtab is older than the boot time. */ static int nfslogtab_deactivate_after_boot(void) { FILE *fd; int error = 0; fd = fopen(NFSLOGTAB, "r+"); if (fd == NULL) { error = errno; if (error != ENOENT) { syslog(LOG_ERR, gettext("%s: %s\n"), NFSLOGTAB, strerror(error)); return (-1); } return (0); } if (lockf(fileno(fd), F_LOCK, 0L) < 0) { error = errno; syslog(LOG_ERR, gettext("cannot lock %s: %s\n"), NFSLOGTAB, strerror(error)); (void) fclose(fd); return (-1); } if (logtab_deactivate_after_boot(fd) == -1) { syslog(LOG_ERR, gettext( "Cannot deactivate all entries in %s\n"), NFSLOGTAB); (void) fclose(fd); return (-1); } (void) fclose(fd); return (0); } /* * Enables the log file cycling flag. */ static void enable_logcycling(void) { need2cycle = TRUE; } /* * Cycle all log files that have been active since the last cycling. * This means it's not simply listed in the configuration file, but * there's information associated with it. */ static int cycle_logs(nfsl_config_t *listp, int max_logs_preserve) { nfsl_config_t *clp; void *processed_list = NULL; int error = 0, total_errors = 0; for (clp = listp; clp != NULL; clp = clp->nc_next) { error = 0; /* * Process transpath log. */ if (clp->nc_logpath) { if (is_cycle_needed(clp->nc_logpath, &processed_list, B_FALSE, &error)) { if (clp->nc_transcookie != NULL) { nfslog_close_transactions( &clp->nc_transcookie); assert(clp->nc_transcookie == NULL); } error = cycle_log(clp->nc_logpath, max_logs_preserve); } else if (error) goto errout; } total_errors += error; /* * Process elfpath log. */ if (clp->nc_rpclogpath) { if (is_cycle_needed(clp->nc_rpclogpath, &processed_list, B_FALSE, &error)) { error = cycle_log(clp->nc_rpclogpath, max_logs_preserve); } else if (error) goto errout; } total_errors += error; } errout: /* * Free the list of processed entries. */ (void) is_cycle_needed(NULL, &processed_list, B_TRUE, &error); return (total_errors); } /* * Returns TRUE if this log has not yet been cycled, FALSE otherwise. * '*head' points to the list of entries that have been processed. * If this is a new entry, it gets inserted at the beginning of the * list, and returns TRUE. * * The list is freed if 'need2free' is set, and returns FALSE. * Sets 'error' on failure, and returns FALSE. */ static boolean_t is_cycle_needed(char *path, void **list, boolean_t need2free, int *error) { struct list { char *log; struct list *next; } *head, *next, *p; head = (struct list *)(*list); if (need2free) { /* * Free the list and return */ for (p = head; p != NULL; p = next) { next = p->next; free(p); } head = NULL; return (B_FALSE); } assert(path != NULL); *error = 0; for (p = head; p != NULL; p = p->next) { /* * Have we seen this before? */ if (strcmp(p->log, path) == 0) return (B_FALSE); } /* * Add it to the list */ if ((p = (struct list *)malloc(sizeof (*p))) == NULL) { *error = ENOMEM; syslog(LOG_ERR, gettext("Cannot allocate memory.")); return (B_FALSE); } p->log = path; p->next = head; head = p; return (B_TRUE); } /* * cycle given log file. */ int cycle_log(char *filename, int max_logs_preserve) { int i; char *file_1; char *file_2; int error = 0; struct stat st; if (max_logs_preserve == 0) return (0); if (stat(filename, &st) == -1) { if (errno == ENOENT) { /* * Nothing to cycle. */ return (0); } return (errno); } file_1 = (char *)malloc(PATH_MAX); file_2 = (char *)malloc(PATH_MAX); for (i = max_logs_preserve - 2; i >= 0; i--) { (void) sprintf(file_1, "%s.%d", filename, i); (void) sprintf(file_2, "%s.%d", filename, (i + 1)); if (rename(file_1, file_2) == -1) { error = errno; if (error != ENOENT) { syslog(LOG_ERR, gettext( "cycle_log: can not rename %s to %s: %s"), file_1, file_2, strerror(error)); goto out; } } } (void) sprintf(file_1, "%s.0", filename); if (rename(filename, file_1) == -1) { error = errno; if (error != ENOENT) { syslog(LOG_ERR, gettext( "cycle_log: can not rename %s to %s: %s"), filename, file_1, strerror(error)); goto out; } } error = 0; out: free(file_1); free(file_2); return (error); } /* * If operation = PID_STARTUP then checks the nfslogd.pid file, it is opened * if it exists, read and the pid is checked for an active process. If no * active process is found, the pid of this process is written to the file, * and 0 is returned, otherwise non-zero error is returned. * * If operation = PID_SHUTDOWN then removes the nfslogd.pid file and 0 is * returned. */ static int process_pidfile(enum pidfile_operation op) { int fd, read_count; int error = 0; pid_t pid, mypid; char *PidFile = NFSLOGD_PIDFILE; int open_flags; if (op == PID_STARTUP) open_flags = O_RDWR | O_CREAT; else { assert(op == PID_SHUTDOWN); open_flags = O_RDWR; } if ((fd = open(PidFile, open_flags, 0600)) < 0) { error = errno; if (error == ENOENT && op == PID_SHUTDOWN) { /* * We were going to remove it anyway */ error = 0; goto out; } (void) fprintf(stderr, gettext( "cannot open or create pid file %s\n"), PidFile); goto out; } if (lockf(fd, F_LOCK, 0) < 0) { error = errno; (void) fprintf(stderr, gettext( "Cannot lock %s - %s\n"), PidFile, strerror(error)); goto out; } if ((read_count = read(fd, &pid, sizeof (pid))) < 0) { error = errno; (void) fprintf(stderr, gettext( "Can not read from file %s - %s\n"), PidFile, strerror(error)); } mypid = getpid(); if (op == PID_STARTUP) { if (read_count > 0) { if (kill(pid, 0) == 0) { error = EEXIST; (void) fprintf(stderr, gettext( "Terminated - nfslogd(%ld) already " "running.\n"), pid); goto out; } else if (errno != ESRCH) { error = errno; (void) fprintf(stderr, gettext( "Unexpected error returned %s\n"), strerror(error)); goto out; } } pid = mypid; /* * rewind the file to overwrite old pid */ (void) lseek(fd, 0, SEEK_SET); if (write(fd, &mypid, sizeof (mypid)) < 0) { error = errno; (void) fprintf(stderr, gettext( "Cannot update %s: %s\n"), PidFile, strerror(error)); } } else { assert(pid == mypid); if (unlink(PidFile)) { error = errno; syslog(LOG_ERR, gettext("Cannot remove %s: %s"), strerror(error)); } } out: if (fd >= 0) (void) close(fd); return (error); } /* * Forces a timeout on all open transactions. */ static void transactions_timeout(nfsl_config_t *clp) { for (; clp != NULL; clp = clp->nc_next) { if (clp->nc_transcookie != NULL) { nfslog_process_trans_timeout( (struct nfslog_trans_file *)clp->nc_transcookie, FALSE); } } } /* * Closes all transaction logs causing outstanding transactions * to be flushed to their respective log. */ static void close_all_translogs(nfsl_config_t *clp) { for (; clp != NULL; clp = clp->nc_next) { if (clp->nc_transcookie != NULL) { nfslog_close_transactions(&clp->nc_transcookie); assert(clp->nc_transcookie == NULL); } } } # #ident "%Z%%M% %I% %E% SMI" # # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # 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 # # Specify the maximum number of logs to preserve. # # MAX_LOGS_PRESERVE=10 # Minimum size buffer should reach before processing. # # MIN_PROCESSING_SIZE=524288 # Number of seconds the daemon should sleep waiting for more work. # # IDLE_TIME=300 # CYCLE_FREQUENCY specifies the frequency (in hours) with which the # log buffers should be cycled. # # CYCLE_FREQUENCY=24 # Use UMASK for the creation of logs and file handle mapping tables. # # UMASK=0137 /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NFSLOGD_H #define _NFSLOGD_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "../lib/nfslog_config.h" #include "buffer_list.h" #define NFSLOGD_PIDFILE "/var/run/nfslogd.pid" #define NFSLOG_OPTIONS_FILE "/etc/default/nfslogd" #define MIN_PROCESSING_SIZE 512*1024 /* Minimum size buffer */ /* should reach before */ /* processing */ #define IDLE_TIME 300 /* Max time to wait w/o processing */ /* in seconds */ #define MAX_LOGS_PRESERVE 10 /* Number of log files to keep for */ /* cycling */ #define MAPPING_UPDATE_INTERVAL (SECSPERDAY) /* frequency of updates to */ /* dbm records in seconds */ #define CYCLE_FREQUENCY 24 /* in hours */ #define PRUNE_TIMEOUT (SECSPERHOUR * 168) #define NFSLOG_UMASK 0137 /* for creating tables and logs */ /* * RPC dispatch table for logging. Indexed by program, version, proc. * Based on NFS dispatch table, but differs in that it does not xdr * encode/decode arguments and results. */ struct nfsl_proc_disp { void (*nfsl_dis_args)(); /* prt elf nl args from rpc args */ void (*nfsl_dis_res)(); /* prt elf nl res from rpc res */ char *procname; /* string describing the proc */ }; struct nfsl_vers_disp { int nfsl_dis_nprocs; /* number of procs */ struct nfsl_proc_disp *nfsl_dis_proc_table; /* proc array */ }; struct nfsl_prog_disp { rpcprog_t nfsl_dis_prog; /* program number */ rpcvers_t nfsl_dis_versmin; /* minimum version number */ int nfsl_dis_nvers; /* number of version values */ struct nfsl_vers_disp *nfsl_dis_vers_table; /* versions array */ char *progname; /* string describing the program */ }; struct nfsl_log_file { char *path; /* pathname of file */ FILE *fp; /* file pointer */ char *buf; /* buffer where output queued before print */ int bufoffset; /* current offset in (memory) buffer */ struct nfsl_log_file *next; /* next file in list */ struct nfsl_log_file *prev; /* next file in list */ }; /* * The following four structures are used for processing the buffer file. */ struct valid_rpcs { rpcprog_t prog; rpcvers_t versmin; rpcvers_t versmax; }; /* * Simple struct for keeping track of the offset and length of * records processed from the buffer file. This is used for the logic * of rewriting the buffer header of that last record processed. * Since records within the buffer file can be 'out of order' and nfslogd * sorts those records, we need to keep track of what has been processed * and where. This record keeping is then used to decide when to rewrite * the buffer header and to decide the correct offset for that rewrite. */ struct processed_records { struct processed_records *next; struct processed_records *prev; u_offset_t start_offset; unsigned int len; unsigned int num_recs; }; struct nfslog_buf { struct nfslog_buf *next; struct nfslog_buf *prev; char *bufpath; /* buffer file name */ int fd; /* buffer file fd */ flock_t fl; /* buffer file lock */ u_offset_t filesize; /* file size */ intptr_t mmap_addr; /* address of mmap */ u_offset_t next_rec; /* address of next record */ unsigned int last_rec_id; /* last record id processed */ nfslog_buffer_header bh; /* file buffer header */ struct nfslog_lr *bh_lrp; int num_lrps; struct nfslog_lr *lrps; /* raw records - not cooked */ /* Next fields used for tracking processed records from buf file */ u_offset_t last_record_offset; /* value last written to hdr */ struct processed_records *prp; /* list of processed chunks */ int num_pr_queued; /* # of processed records */ }; struct nfslog_lr { struct nfslog_lr *next; struct nfslog_lr *prev; u_offset_t f_offset; /* offset for ondisk file */ intptr_t record; /* mmap address of record */ unsigned int recsize; /* size of this record */ caddr_t buffer; /* used if mmap fails */ XDR xdrs; nfslog_request_record log_record; /* decoded record */ bool_t (*xdrargs)(); /* xdr function for FREE */ bool_t (*xdrres)(); /* xdr function for FREE */ struct nfslog_buf *lbp; }; /* * Following defines are used as a parameter to nfslog_open_trans() * The bit mask passed to this function will determine which operations * are placed in the log. */ #define TRANSTOLOG_OPER_READ 0x00000001 #define TRANSTOLOG_OPER_WRITE 0x00000002 #define TRANSTOLOG_OPER_SETATTR 0x00000004 #define TRANSTOLOG_OPER_REMOVE 0x00000008 #define TRANSTOLOG_OPER_MKDIR 0x00000010 #define TRANSTOLOG_OPER_CREATE 0x00000020 #define TRANSTOLOG_OPER_RMDIR 0x00000040 #define TRANSTOLOG_OPER_RENAME 0x00000080 #define TRANSTOLOG_OPER_MKNOD 0x00000100 #define TRANSTOLOG_OPER_LINK 0x00000200 #define TRANSTOLOG_OPER_SYMLINK 0x00000400 #define TRANSTOLOG_OPER_READWRITE \ (TRANSTOLOG_OPER_READ | TRANSTOLOG_OPER_WRITE) #define TRANSTOLOG_ALL ((uint32_t)~0) extern int debug; extern boolean_t test; extern int max_logs_preserve; extern uint_t idle_time; extern boolean_t keep_running; extern boolean_t quick_cleaning; extern int cycle_log(char *, int); extern int prune_dbs(char *); extern int process_buffer( struct buffer_ent *, nfsl_config_t **, int, int, int *); extern struct nfslog_buf *nfslog_open_buf(char *, int *); extern void nfslog_close_buf(struct nfslog_buf *, int); extern struct nfslog_lr *nfslog_get_logrecord(struct nfslog_buf *); extern void nfslog_free_logrecord(struct nfslog_lr *, bool_t); extern int nfslog_process_fh_rec(struct nfslog_lr *, char *, char **, char **, bool_t); extern void *nfslog_open_elf_file(char *, nfslog_buffer_header *, int *); extern void nfslog_close_elf_file(void **); extern int nfslog_process_elf_rec(void *, nfslog_request_record *, char *, char *); struct nfslog_trans_file; extern void *nfslog_open_trans_file(char *, uint32_t, uint32_t, int *); extern void nfslog_process_trans_timeout(struct nfslog_trans_file *, uint32_t); extern int nfslog_process_trans_rec(void *, nfslog_request_record *, char *, char *, char *); extern void nfslog_close_transactions(void **); extern void nfslog_opaque_print_buf(void *, int, char *, int *, int); #ifdef __cplusplus } #endif #endif /* _NFSLOGD_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../lib/nfslog_config.h" #include "buffer_list.h" #include "nfslogd.h" extern int _nfssys(int, void *); /* * simple list used to keep track of bad tag messages syslogged. */ struct nfs_log_list { char *l_name; struct nfs_log_list *l_next; }; static void badtag_notify(char *tag); static struct nfs_log_list *badtag_list = NULL; static void cleanup_elf_state(nfsl_config_t *); static void cleanup_trans_state(nfsl_config_t *); /* * Read the contents of the 'bufferpath', process them and store the * user-readable log in 'elfpath', updating the 'fhpath' filehandle * table. * The contents of the configuration list (*config_list) may be * modified if the configuration file has been updated and we can not * find the configuration entry in the currently loaded list. * * Returns 0 on success and sets *buffer_processed to 1. * non zero error on failure and *buffer_processed set to 0. */ int process_buffer( struct buffer_ent *bep, nfsl_config_t **config_list, int min_size, int idle_time, int *buffer_processed) { struct stat st; struct nfsl_flush_args nfa; struct nfslog_buf *lbp = NULL; struct nfslog_lr *lrp; char *path1 = NULL; char *path2 = NULL; char *buffer_inprog = NULL; int buffer_inprog_len; int error = 0; nfsl_config_t *ncp = NULL, *last_good_ncp; char *bufferpath = bep->be_name; char *tag; boolean_t elf_checked = B_FALSE; boolean_t trans_checked = B_FALSE; assert(buffer_processed != NULL); assert(bufferpath != NULL); if (stat(bufferpath, &st) == -1) { error = errno; if (error == ENOENT) { error = 0; buffer_inprog_len = strlen(bufferpath) + strlen(LOG_INPROG_STRING) + 1; buffer_inprog = (char *)malloc(buffer_inprog_len); if (buffer_inprog == NULL) { syslog(LOG_ERR, gettext( "process_buffer: malloc failed")); return (ENOMEM); } (void) sprintf(buffer_inprog, "%s%s", bufferpath, LOG_INPROG_STRING); if (stat(buffer_inprog, &st) == -1) { error = errno; if (bep->be_error != error) { syslog(LOG_ERR, gettext( "Can not stat %s: %s"), buffer_inprog, strerror(error)); } free(buffer_inprog); return (error); } free(buffer_inprog); /* * Does the buffer in progress meet our minimum * processing requirements? or has it been around * longer than we're willing to wait for more * data to be logged? */ if ((st.st_size < min_size) && ((time(0) - bep->be_lastprocessed) < idle_time)) { /* * The buffer does not meet the minimum * size processing requirements, and it has not * been around longer than we're willing to * wait for more data collection. * We return now without processing it. */ return (0); } /* * Issue the LOG_FLUSH system call to flush the * buffer and process it. */ (void) memset((void *)&nfa, 0, sizeof (nfa)); nfa.version = NFSL_FLUSH_ARGS_VERS; nfa.directive = NFSL_RENAME | NFSL_SYNC; nfa.buff = bufferpath; nfa.buff_len = strlen(bufferpath) + 1; if (_nfssys(LOG_FLUSH, &nfa) < 0) { error = errno; if (bep->be_error != error) { syslog(LOG_ERR, gettext( "_nfssys(%s) failed: %s"), nfa.buff, strerror(error)); } return (error); } } else { if (bep->be_error != error) { syslog(LOG_ERR, gettext("Can not stat %s: %s"), bufferpath, strerror(error)); } return (error); } } /* * Open and lock input buffer. * Passes in the value of the last error so that it will not * print it again if it is still hitting the same error condition. */ error = bep->be_error; if ((lbp = nfslog_open_buf(bufferpath, &error)) == NULL) goto done; if ((ncp = last_good_ncp = nfsl_findconfig(*config_list, "global", &error)) == NULL) { assert(error != 0); nfsl_freeconfig_list(config_list); if (error != bep->be_error) { syslog(LOG_ERR, gettext( "Could not search config list: %s"), strerror(error)); } goto done; } assert(error == 0); while ((lrp = nfslog_get_logrecord(lbp)) != NULL && keep_running) { if (*buffer_processed == 0) (*buffer_processed)++; /* * Get the matching config entry. */ tag = lrp->log_record.re_tag; if (strcmp(tag, last_good_ncp->nc_name) != 0) { ncp = nfsl_findconfig(*config_list, tag, &error); if (error) { if (error != bep->be_error) { syslog(LOG_ERR, gettext( "Could not search config list: %s"), strerror(error)); } nfsl_freeconfig_list(config_list); goto done; } if (ncp == NULL) { badtag_notify(tag); ncp = last_good_ncp; goto skip; } last_good_ncp = ncp; } if (ncp->nc_flags & NC_UPDATED) { /* * The location of the log files may have changed, * we need to close transactions and invalidate * cookies so that the log files can be reopened * further down. */ cleanup_elf_state(ncp); cleanup_trans_state(ncp); ncp->nc_flags &= ~NC_UPDATED; /* * Force cookies to be recreated if necessary. */ elf_checked = trans_checked = B_FALSE; } /* * Open output files. */ if (ncp->nc_rpclogpath != NULL) { /* * Log rpc requests in W3C-ELF format. */ if (!elf_checked && ncp->nc_elfcookie != NULL) { /* * Make sure file still exists. * Do this once per buffer. */ if (stat(ncp->nc_rpclogpath, &st) == -1 && errno == ENOENT) { /* * The open rpclogfile has been * deleted. Get new one below. */ cleanup_elf_state(ncp); } elf_checked = B_TRUE; } if (ncp->nc_elfcookie == NULL) { error = bep->be_error; ncp->nc_elfcookie = nfslog_open_elf_file( ncp->nc_rpclogpath, &lbp->bh, &error); if (ncp->nc_elfcookie == NULL) { bep->be_error = error; goto done; } } } if (ncp->nc_logpath != NULL) { /* * Log rpc reqs in trans/ftp format. */ if (!trans_checked && ncp->nc_transcookie != NULL) { /* * Do this once per buffer. */ if (stat(ncp->nc_logpath, &st) == -1 && errno == ENOENT) { /* * The open transaction file has been * deleted. Close pending transaction * work. A new transaction log will be * opened by nfslog_open_trans_file() * below. */ cleanup_trans_state(ncp); } trans_checked = B_TRUE; } if (ncp->nc_transcookie == NULL) { int transtolog; transtolog = (ncp->nc_logformat == TRANSLOG_BASIC) ? TRANSTOLOG_OPER_READWRITE : TRANSTOLOG_ALL; error = bep->be_error; ncp->nc_transcookie = nfslog_open_trans_file( ncp->nc_logpath, ncp->nc_logformat, transtolog, &error); if (ncp->nc_transcookie == NULL) { bep->be_error = error; goto done; } } } assert(ncp->nc_fhpath != NULL); if (nfslog_process_fh_rec(lrp, ncp->nc_fhpath, &path1, &path2, ncp->nc_elfcookie != NULL)) { /* * Make sure there is room. */ if (ncp->nc_elfcookie != NULL) { (void) nfslog_process_elf_rec(ncp->nc_elfcookie, &lrp->log_record, path1, path2); } if (ncp->nc_transcookie != NULL) { (void) nfslog_process_trans_rec( ncp->nc_transcookie, &lrp->log_record, ncp->nc_fhpath, path1, path2); } } skip: if (path1 != NULL) free(path1); if (path2 != NULL) free(path2); path1 = path2 = NULL; nfslog_free_logrecord(lrp, TRUE); } /* while */ if (!error && keep_running) { /* * Keep track of when this buffer was last processed. */ bep->be_lastprocessed = time(0); if (test && *buffer_processed != 0) { /* * Save the buffer for future debugging. We do this * by following the log cycling policy, with a maximum * of 'max_logs_preserve' to save. */ if (cycle_log(bufferpath, max_logs_preserve)) { syslog(LOG_ERR, gettext( "could not save copy of buffer \"%s\""), bufferpath); } } else { /* * Remove buffer since it has been processed. */ if (unlink(bufferpath)) { error = errno; syslog(LOG_ERR, gettext( "could not unlink %s: %s"), bufferpath, strerror(error)); /* * Buffer was processed correctly. */ error = 0; } } } done: if (lbp != NULL) nfslog_close_buf(lbp, quick_cleaning); if (ncp && !quick_cleaning) cleanup_elf_state(ncp); return (error); } static void cleanup_elf_state(nfsl_config_t *ncp) { if (ncp->nc_elfcookie != NULL) { nfslog_close_elf_file(&ncp->nc_elfcookie); assert(ncp->nc_elfcookie == NULL); } } static void cleanup_trans_state(nfsl_config_t *ncp) { if (ncp->nc_transcookie != NULL) { nfslog_close_transactions(&ncp->nc_transcookie); assert(ncp->nc_transcookie == NULL); } } /* * Searches the list of previously seen bad tags. Note that this * list is never pruned. This should not be a problem since the * list of bad tags should be fairl small. New entries are inserted * at the beginning of the list assuming it will be accessed more * frequently since we have just seen it. */ static void badtag_notify(char *tag) { struct nfs_log_list *lp, *p; int error; for (p = badtag_list; p != NULL; p = p->l_next) { if (strcmp(tag, p->l_name) == 0) { /* * We've seen this before, nothing to do. */ return; } } /* * Not on the list, add it. */ syslog(LOG_ERR, gettext("tag \"%s\" not found in %s - " "ignoring records referencing such tag."), tag, NFSL_CONFIG_FILE_PATH); if ((lp = (struct nfs_log_list *)malloc(sizeof (*lp))) != NULL) { if ((lp->l_name = strdup(tag)) != NULL) { lp->l_next = badtag_list; badtag_list = lp; return; /* done */ } } if (lp->l_name != NULL) free(lp->l_name); if (lp) free(lp); error = errno; syslog(LOG_ERR, gettext( "Cannot add \"%s\" to bad tag list: %s"), tag, strerror(error)); } /* * 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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * nfs log - read buffer file and return structs in usable form */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nfslogd.h" #define MAX_LRS_READ_AHEAD 2048 #define MAX_RECS_TO_DELAY 32768 static int nfslog_init_buf(char *, struct nfslog_buf *, int *); static void nfslog_free_buf(struct nfslog_buf *, int); static struct nfslog_lr *nfslog_read_buffer(struct nfslog_buf *); static void free_lrp(struct nfslog_lr *); static struct nfslog_lr *remove_lrp_from_lb(struct nfslog_buf *, struct nfslog_lr *); static void insert_lrp_to_lb(struct nfslog_buf *, struct nfslog_lr *); static void nfslog_rewrite_bufheader(struct nfslog_buf *); /* * Treat the provided path name as an NFS log buffer file. * Allocate a data structure for its handling and initialize it. * *error contains the previous error condition encountered for * this object. This value can be used to avoid printing the last * error endlessly. * It will set *error appropriately after processing. */ struct nfslog_buf * nfslog_open_buf(char *bufpath, int *error) { struct nfslog_buf *lbp = NULL; if (bufpath == NULL) { *error = EINVAL; return (NULL); } if ((lbp = malloc(sizeof (struct nfslog_buf))) == NULL) { *error = ENOMEM; return (NULL); } bzero(lbp, sizeof (struct nfslog_buf)); if (nfslog_init_buf(bufpath, lbp, error)) { free(lbp); return (NULL); } return (lbp); } /* * Free the log buffer struct with all of its baggage and free the data struct */ void nfslog_close_buf(struct nfslog_buf *lbp, int close_quick) { nfslog_free_buf(lbp, close_quick); free(lbp); } /* * Set up the log buffer struct; simple things are opening and locking * the buffer file and then on to mmap()ing it for later use by the * XDR decode path. Make sure to read the buffer header before * returning so that we will be at the first true log record. * * *error contains the last error encountered on this object. It can * be used to avoid reporting the same error endlessly. It is reset * to the current error code on return. */ static int nfslog_init_buf(char *bufpath, struct nfslog_buf *lbp, int *error) { struct stat sb; int preverror = *error; lbp->next = lbp; lbp->prev = lbp; /* * set these values so that the free routine will know what to do */ lbp->mmap_addr = (intptr_t)MAP_FAILED; lbp->last_rec_id = MAXINT - 1; lbp->bh.bh_length = 0; lbp->bh_lrp = NULL; lbp->num_lrps = 0; lbp->lrps = NULL; lbp->last_record_offset = 0; lbp->prp = NULL; lbp->num_pr_queued = 0; lbp->bufpath = strdup(bufpath); if (lbp->bufpath == NULL) { *error = ENOMEM; if (preverror != *error) { syslog(LOG_ERR, gettext("Cannot strdup '%s': %s"), bufpath, strerror(*error)); } nfslog_free_buf(lbp, FALSE); return (*error); } if ((lbp->fd = open(bufpath, O_RDWR)) < 0) { *error = errno; if (preverror != *error) { syslog(LOG_ERR, gettext("Cannot open '%s': %s"), bufpath, strerror(*error)); } nfslog_free_buf(lbp, FALSE); return (*error); } /* * Lock the entire buffer file to prevent conflicting access. * We get a write lock because we want only 1 process to be * generating records from it. */ lbp->fl.l_type = F_WRLCK; lbp->fl.l_whence = SEEK_SET; /* beginning of file */ lbp->fl.l_start = (offset_t)0; lbp->fl.l_len = 0; /* entire file */ lbp->fl.l_sysid = 0; lbp->fl.l_pid = 0; if (fcntl(lbp->fd, F_SETLKW, &lbp->fl) == -1) { *error = errno; if (preverror != *error) { syslog(LOG_ERR, gettext("Cannot lock (%s): %s"), bufpath, strerror(*error)); } nfslog_free_buf(lbp, FALSE); return (*error); } if (fstat(lbp->fd, &sb)) { *error = errno; if (preverror != *error) { syslog(LOG_ERR, gettext("Cannot stat (%s): %s"), bufpath, strerror(*error)); } nfslog_free_buf(lbp, FALSE); return (*error); } lbp->filesize = sb.st_size; lbp->mmap_addr = (intptr_t)mmap(0, lbp->filesize, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, lbp->fd, 0); /* This is part of the duality of the use of either mmap()|read() */ if (lbp->mmap_addr == (intptr_t)MAP_FAILED) { lbp->next_rec = 0; } else { lbp->next_rec = lbp->mmap_addr; } /* Read the header */ if ((lbp->bh_lrp = nfslog_read_buffer(lbp)) == NULL) { *error = EIO; if (preverror != *error) { syslog(LOG_ERR, gettext( "error in reading file '%s': %s"), bufpath, strerror(EIO)); } nfslog_free_buf(lbp, FALSE); return (*error); } if (!xdr_nfslog_buffer_header(&lbp->bh_lrp->xdrs, &lbp->bh)) { *error = EIO; if (preverror != *error) { syslog(LOG_ERR, gettext( "error in reading file '%s': %s"), bufpath, strerror(*error)); } nfslog_free_buf(lbp, FALSE); return (*error); } /* * Set the pointer to the next record based on the buffer header. * 'lbp->bh.bh_offset' contains the offset of where to begin * processing relative to the buffer header. */ lbp->next_rec += lbp->bh.bh_offset; /* * If we are going to be using read() for file data, then we may * have to adjust the current file pointer to take into account * a starting point other than the beginning of the file. * If mmap is being used, this is taken care of as a side effect of * setting up the value of next_rec. */ if (lbp->mmap_addr == (intptr_t)MAP_FAILED && lbp->next_rec != 0) { (void) lseek(lbp->fd, lbp->next_rec, SEEK_SET); /* This is a special case of setting the last_record_offset */ lbp->last_record_offset = lbp->next_rec; } else { lbp->last_record_offset = lbp->next_rec - lbp->mmap_addr; } return (*error = 0); } /* * Free the nfslog buffer and its associated allocations */ static void nfslog_free_buf(struct nfslog_buf *lbp, int close_quick) { XDR xdrs; int error; caddr_t buffer; struct nfslog_lr *lrp, *lrp_next; struct processed_records *prp, *tprp; /* work to free the offset records and rewrite header */ if (lbp->prp) { if (lbp->last_record_offset == lbp->prp->start_offset) { /* adjust the offset for the entire buffer */ lbp->last_record_offset = lbp->prp->start_offset + lbp->prp->len; nfslog_rewrite_bufheader(lbp); } if (close_quick) return; prp = lbp->prp; do { tprp = prp->next; free(prp); prp = tprp; } while (lbp->prp != prp); } if (close_quick) return; /* Take care of the queue log records first */ if (lbp->lrps != NULL) { lrp = lbp->lrps; do { lrp_next = lrp->next; nfslog_free_logrecord(lrp, FALSE); lrp = lrp_next; } while (lrp != lbp->lrps); lbp->lrps = NULL; } /* The buffer header was decoded and needs to be freed */ if (lbp->bh.bh_length != 0) { buffer = (lbp->bh_lrp->buffer != NULL ? lbp->bh_lrp->buffer : (caddr_t)lbp->mmap_addr); xdrmem_create(&xdrs, buffer, lbp->bh_lrp->recsize, XDR_FREE); (void) xdr_nfslog_buffer_header(&xdrs, &lbp->bh); lbp->bh.bh_length = 0; } /* get rid of the bufheader lrp */ if (lbp->bh_lrp != NULL) { free_lrp(lbp->bh_lrp); lbp->bh_lrp = NULL; } /* Clean up for mmap() usage */ if (lbp->mmap_addr != (intptr_t)MAP_FAILED) { if (munmap((void *)lbp->mmap_addr, lbp->filesize)) { error = errno; syslog(LOG_ERR, gettext("munmap failed: %s: %s"), (lbp->bufpath != NULL ? lbp->bufpath : ""), strerror(error)); } lbp->mmap_addr = (intptr_t)MAP_FAILED; } /* Finally close the buffer file */ if (lbp->fd >= 0) { lbp->fl.l_type = F_UNLCK; if (fcntl(lbp->fd, F_SETLK, &lbp->fl) == -1) { error = errno; syslog(LOG_ERR, gettext("Cannot unlock file %s: %s"), (lbp->bufpath != NULL ? lbp->bufpath : ""), strerror(error)); } (void) close(lbp->fd); lbp->fd = -1; } if (lbp->bufpath != NULL) free(lbp->bufpath); } /* * We are reading a record from the log buffer file. Since we are reading * an XDR stream, we first have to read the first integer to determine * how much to read in whole for this record. Our preference is to use * mmap() but if failed initially we will be using read(). Need to be * careful about proper initialization of the log record both from a field * perspective and for XDR decoding. */ static struct nfslog_lr * nfslog_read_buffer(struct nfslog_buf *lbp) { XDR xdrs; unsigned int record_size; struct nfslog_lr *lrp; char *sizebuf, tbuf[16]; caddr_t buffer; offset_t next_rec; lrp = (struct nfslog_lr *)malloc(sizeof (*lrp)); bzero(lrp, sizeof (*lrp)); /* Check to see if mmap worked */ if (lbp->mmap_addr == (intptr_t)MAP_FAILED) { /* * EOF or other failure; we don't try to recover, just return */ if (read(lbp->fd, tbuf, BYTES_PER_XDR_UNIT) <= 0) { free_lrp(lrp); return (NULL); } sizebuf = tbuf; } else { /* EOF check for the mmap() case */ if (lbp->filesize <= lbp->next_rec - lbp->mmap_addr) { free_lrp(lrp); return (NULL); } sizebuf = (char *)(uintptr_t)lbp->next_rec; } /* We have to XDR the first int so we know how much is in this record */ xdrmem_create(&xdrs, sizebuf, sizeof (unsigned int), XDR_DECODE); if (!xdr_u_int(&xdrs, &record_size)) { free_lrp(lrp); return (NULL); } lrp->recsize = record_size; next_rec = lbp->next_rec + lrp->recsize; if (lbp->mmap_addr == (intptr_t)MAP_FAILED) { /* * Read() case - shouldn't be used very much. * Note: The 'buffer' field is used later on * to determine which method is being used mmap()|read() */ if (lbp->filesize < next_rec) { /* partial record from buffer */ syslog(LOG_ERR, gettext( "Last partial record in work buffer %s " "discarded\n"), lbp->bufpath); free_lrp(lrp); return (NULL); } if ((lrp->buffer = malloc(lrp->recsize)) == NULL) { free_lrp(lrp); return (NULL); } bcopy(sizebuf, lrp->buffer, BYTES_PER_XDR_UNIT); if (read(lbp->fd, &lrp->buffer[BYTES_PER_XDR_UNIT], lrp->recsize - BYTES_PER_XDR_UNIT) <= 0) { free_lrp(lrp); return (NULL); } } else if (lbp->filesize < next_rec - lbp->mmap_addr) { /* partial record from buffer */ syslog(LOG_ERR, gettext( "Last partial record in work buffer %s " "discarded\n"), lbp->bufpath); free_lrp(lrp); return (NULL); } /* other initializations */ lrp->next = lrp->prev = lrp; /* Keep track of the offset at which this record was read */ if (lbp->mmap_addr == (intptr_t)MAP_FAILED) lrp->f_offset = lbp->next_rec; else lrp->f_offset = lbp->next_rec - lbp->mmap_addr; /* This is the true address of the record */ lrp->record = lbp->next_rec; lrp->xdrargs = lrp->xdrres = NULL; lrp->lbp = lbp; /* Here is the logic for mmap() vs. read() */ buffer = (lrp->buffer != NULL ? lrp->buffer : (caddr_t)lrp->record); /* Setup for the 'real' XDR decode of the entire record */ xdrmem_create(&lrp->xdrs, buffer, lrp->recsize, XDR_DECODE); /* calculate the offset for the next record */ lbp->next_rec = next_rec; return (lrp); } /* * Simple removal of the log record from the log buffer queue. * Make sure to manage the count of records queued. */ static struct nfslog_lr * remove_lrp_from_lb(struct nfslog_buf *lbp, struct nfslog_lr *lrp) { if (lbp->lrps == lrp) { if (lbp->lrps == lbp->lrps->next) { lbp->lrps = NULL; } else { lbp->lrps = lrp->next; remque(lrp); } } else { remque(lrp); } lbp->num_lrps--; return (lrp); } /* * Insert a log record struct on the log buffer struct. The log buffer * has a pointer to the head of a queue of log records that have been * read from the buffer file but have not been processed yet because * the record id did not match the sequence desired for processing. * The insertion must be in the 'correct'/sorted order which adds * to the complexity of this function. */ static void insert_lrp_to_lb(struct nfslog_buf *lbp, struct nfslog_lr *lrp) { int ins_rec_id = lrp->log_record.re_header.rh_rec_id; struct nfslog_lr *curlrp; if (lbp->lrps == NULL) { /* that was easy */ lbp->lrps = lrp; } else { /* * Does this lrp go before the first on the list? * If so, do the insertion by hand since insque is not * as flexible when queueing an element to the head of * a list. */ if (ins_rec_id < lbp->lrps->log_record.re_header.rh_rec_id) { lrp->next = lbp->lrps; lrp->prev = lbp->lrps->prev; lbp->lrps->prev->next = lrp; lbp->lrps->prev = lrp; lbp->lrps = lrp; } else { /* * Search the queue for the correct insertion point. * Be careful about the insque so that the record * ends up in the right place. */ curlrp = lbp->lrps; do { if (ins_rec_id < curlrp->next->log_record.re_header.rh_rec_id) break; curlrp = curlrp->next; } while (curlrp != lbp->lrps); if (curlrp == lbp->lrps) insque(lrp, lbp->lrps->prev); else insque(lrp, curlrp); } } /* always keep track of how many we have */ lbp->num_lrps++; } /* * We are rewriting the buffer header at the start of the log buffer * for the sole purpose of resetting the bh_offset field. This is * supposed to represent the progress that the nfslogd daemon has made * in its processing of the log buffer file. * 'lbp->last_record_offset' contains the absolute offset of the end * of the last element processed. The on-disk buffer offset is relative * to the buffer header, therefore we subtract the length of the buffer * header from the absolute offset. */ static void nfslog_rewrite_bufheader(struct nfslog_buf *lbp) { XDR xdrs; nfslog_buffer_header bh; /* size big enough for buffer header encode */ #define XBUFSIZE 128 char buffer[XBUFSIZE]; unsigned int wsize; /* * if version 1 buffer is large and the current offset cannot be * represented, then don't update the offset in the buffer. */ if (lbp->bh.bh_flags & NFSLOG_BH_OFFSET_OVERFLOW) { /* No need to update the header - offset too big */ return; } /* * build the buffer header from the original that was saved * on initialization; note that the offset is taken from the * last record processed (the last offset that represents * all records processed without any holes in the processing) */ bh = lbp->bh; /* * if version 1 buffer is large and the current offset cannot be * represented in 32 bits, then save only the last valid offset * in the buffer and mark the flags to indicate that. */ if ((bh.bh_version > 1) || (lbp->last_record_offset - bh.bh_length < UINT32_MAX)) { bh.bh_offset = lbp->last_record_offset - bh.bh_length; } else { /* don't update the offset in the buffer */ bh.bh_flags |= NFSLOG_BH_OFFSET_OVERFLOW; lbp->bh.bh_flags = bh.bh_flags; syslog(LOG_ERR, gettext( "nfslog_rewrite_bufheader: %s: offset does not fit " "in a 32 bit field\n"), lbp->bufpath); } xdrmem_create(&xdrs, buffer, XBUFSIZE, XDR_ENCODE); if (!xdr_nfslog_buffer_header(&xdrs, &bh)) { syslog(LOG_ERR, gettext( "error in re-writing buffer file %s header\n"), lbp->bufpath); return; } wsize = xdr_getpos(&xdrs); if (lbp->mmap_addr == (intptr_t)MAP_FAILED) { /* go to the beginning of the file */ (void) lseek(lbp->fd, 0, SEEK_SET); (void) write(lbp->fd, buffer, wsize); (void) lseek(lbp->fd, lbp->next_rec, SEEK_SET); (void) fsync(lbp->fd); } else { bcopy(buffer, (void *)lbp->mmap_addr, wsize); (void) msync((void *)lbp->mmap_addr, wsize, MS_SYNC); } } /* * With the provided lrp, we will take and 'insert' the range that the * record covered in the buffer file into a list of processed ranges * for the buffer file. These ranges represent the records processed * but not 'marked' in the buffer header as being processed. * This insertion process is being done for two reasons. The first is that * we do not want to pay the performance penalty of re-writing the buffer header * for each record that we process. The second reason is that the records * may be processed out of order because of the unique ids. This will occur * if the kernel has written the records to the buffer file out of order. * The read routine will 'sort' them as the records are read. * * We do not want to re-write the buffer header such that a record is * represented and being processed when it has not been. In the case * that the nfslogd daemon restarts processing and the buffer header * has been re-written improperly, some records could be skipped. * We will be taking the conservative approach and only writing buffer * header offsets when the entire offset range has been processed. */ static void nfslog_ins_last_rec_processed(struct nfslog_lr *lrp) { struct processed_records *prp, *tp; /* init the data struct as if it were the only one */ prp = malloc(sizeof (*prp)); prp->next = prp->prev = prp; prp->start_offset = lrp->f_offset; prp->len = lrp->recsize; prp->num_recs = 1; /* always add since we know we are going to insert */ lrp->lbp->num_pr_queued++; /* Is this the first one? If so, take the easy way out */ if (lrp->lbp->prp == NULL) { lrp->lbp->prp = prp; } else { /* sort on insertion... */ tp = lrp->lbp->prp; do { if (prp->start_offset < tp->start_offset) break; tp = tp->next; } while (tp != lrp->lbp->prp); /* insert where appropriate (before the one we found */ insque(prp, tp->prev); /* * special case where the insertion was done at the * head of the list */ if (tp == lrp->lbp->prp && prp->start_offset < tp->start_offset) lrp->lbp->prp = prp; /* * now that the entry is in place, we need to see if it can * be combined with the previous or following entries. * combination is done by adding to the length. */ if (prp->start_offset == (prp->prev->start_offset + prp->prev->len)) { tp = prp->prev; remque(prp); tp->len += prp->len; tp->num_recs += prp->num_recs; free(prp); prp = tp; } if (prp->next->start_offset == (prp->start_offset + prp->len)) { prp->len += prp->next->len; prp->num_recs += prp->next->num_recs; tp = prp->next; remque(tp); free(tp); } } if (lrp->lbp->num_pr_queued > MAX_RECS_TO_DELAY) { prp = lrp->lbp->prp; if (lrp->lbp->last_record_offset == prp->start_offset) { /* adjust the offset for the entire buffer */ lrp->lbp->last_record_offset = prp->start_offset + prp->len; nfslog_rewrite_bufheader(lrp->lbp); tp = prp->next; if (tp != prp) remque(prp); else tp = NULL; lrp->lbp->prp = tp; lrp->lbp->num_pr_queued -= prp->num_recs; free(prp); } } } /* * nfslog_get_logrecord is responsible for retrieving the next log record * from the buffer file. This would normally be very straightforward but there * is the added complexity of attempting to order the requests coming out of * the buffer file. The fundamental problems is that the kernel nfs logging * functionality does not guarantee that the records were written to the file * in the order that the NFS server processed them. This can cause a problem * in the fh -> pathname mapping in the case were a lookup for a file comes * later in the buffer file than other operations on the lookup's target. * The fh mapping database will not have an entry and will therefore not * be able to map the fh to a name. * * So to solve this problem, the kernel nfs logging code tags each record * with a monotonically increasing id and is guaranteed to be allocated * in the order that the requests were processed. Realize however that * this processing guarantee is essentially for one thread on one client. * This id mechanism does not order all requests since it is only the * single client/single thread case that is most concerning to us here. * * This function will do the 'sorting' of the requests as they are * read from the buffer file. The sorting needs to take into account * that some ids may be missing (operations not logged but ids allocated) * and that the id field will eventually wrap over MAXINT. * * Complexity to solve the fh -> pathname mapping issue. */ struct nfslog_lr * nfslog_get_logrecord(struct nfslog_buf *lbp) { /* figure out what the next should be if the world were perfect */ unsigned int next_rec_id = lbp->last_rec_id + 1; struct nfslog_lr *lrp = NULL; /* * First we check the queued records on the log buffer struct * to see if the one we want is there. The records are sorted * on the record id during the insertions to the queue so that * this check is easy. */ if (lbp->lrps != NULL) { /* Does the first record match ? */ if (lbp->lrps->log_record.re_header.rh_rec_id == next_rec_id) { lrp = remove_lrp_from_lb(lbp, lbp->lrps); lbp->last_rec_id = lrp->log_record.re_header.rh_rec_id; } else { /* * Here we are checking for wrap of the record id * since it is an unsigned in. The idea is that * if there is a huge span between what we expect * and what is queued then we need to flush/empty * the queued records first. */ if (next_rec_id < lbp->lrps->log_record.re_header.rh_rec_id && ((lbp->lrps->log_record.re_header.rh_rec_id - next_rec_id) > (MAXINT / 2))) { lrp = remove_lrp_from_lb(lbp, lbp->lrps); lbp->last_rec_id = lrp->log_record.re_header.rh_rec_id; } } } /* * So the first queued record didn't match (or there were no queued * records to look at). Now we go to the buffer file looking for * the expected log record based on its id. We loop looking for * a matching records and save/queue the records that don't match. * Note that we will queue a maximum number to handle the case * of a missing record id or a queue that is very confused. We don't * want to consume too much memory. */ while (lrp == NULL) { /* Have we queued too many for this buffer? */ if (lbp->num_lrps >= MAX_LRS_READ_AHEAD) { lrp = remove_lrp_from_lb(lbp, lbp->lrps); lbp->last_rec_id = lrp->log_record.re_header.rh_rec_id; break; } /* * Get a record from the buffer file. If none are available, * this is probably and EOF condition (could be a read error * as well but that is masked. :-(). No records in the * file means that we need to pull any queued records * so that we don't miss any in the processing. */ if ((lrp = nfslog_read_buffer(lbp)) == NULL) { if (lbp->lrps != NULL) { lrp = remove_lrp_from_lb(lbp, lbp->lrps); lbp->last_rec_id = lrp->log_record.re_header.rh_rec_id; } else { return (NULL); /* it was really and EOF */ } } else { /* * Just read a record from the buffer file and now we * need to XDR the record header so that we can take * a look at the record id. */ if (!xdr_nfslog_request_record(&lrp->xdrs, &lrp->log_record)) { /* Free and return EOF/NULL on error */ nfslog_free_logrecord(lrp, FALSE); return (NULL); } /* * If the new record is less than or matches the * expected record id, then we return this record */ if (lrp->log_record.re_header.rh_rec_id <= next_rec_id) { lbp->last_rec_id = lrp->log_record.re_header.rh_rec_id; } else { /* * This is not the one we were looking * for; queue it for later processing * (queueing sorts on record id) */ insert_lrp_to_lb(lbp, lrp); lrp = NULL; } } } return (lrp); } /* * Free the log record provided. * This is complex because the associated XDR streams also need to be freed * since allocation could have occured during the DECODE phase. The record * header, args and results need to be XDR_FREEd. The xdr funtions will * be provided if a free needs to be done. * * Note that caller tells us if the record being freed was processed. * If so, then the buffer header should be updated. Updating the buffer * header keeps track of where the nfslogd daemon left off in its processing * if it is unable to complete the entire file. */ void nfslog_free_logrecord(struct nfslog_lr *lrp, bool_t processing_complete) { caddr_t buffer; nfslog_request_record *reqrec; if (processing_complete) { nfslog_ins_last_rec_processed(lrp); } reqrec = &lrp->log_record; buffer = (lrp->buffer != NULL ? lrp->buffer : (caddr_t)lrp->record); xdrmem_create(&lrp->xdrs, buffer, lrp->recsize, XDR_FREE); (void) xdr_nfslog_request_record(&lrp->xdrs, reqrec); if (lrp->xdrargs != NULL && reqrec->re_rpc_arg) (*lrp->xdrargs)(&lrp->xdrs, reqrec->re_rpc_arg); if (reqrec->re_rpc_arg) free(reqrec->re_rpc_arg); if (lrp->xdrres != NULL && reqrec->re_rpc_res) (*lrp->xdrres)(&lrp->xdrs, reqrec->re_rpc_res); if (reqrec->re_rpc_res) free(reqrec->re_rpc_res); free_lrp(lrp); } static void free_lrp(struct nfslog_lr *lrp) { if (lrp->buffer != NULL) free(lrp->buffer); free(lrp); } /* * Utility function used elsewhere */ void nfslog_opaque_print_buf(void *buf, int len, char *outbuf, int *outbufoffsetp, int maxoffset) { int i, j; uint_t *ip; uchar_t *u_buf = (uchar_t *)buf; int outbufoffset = *outbufoffsetp; outbufoffset += sprintf(&outbuf[outbufoffset], " \""); if (len <= sizeof (int)) { for (j = 0; (j < len) && (outbufoffset < maxoffset); j++, u_buf++) outbufoffset += sprintf(&outbuf[outbufoffset], "%02x", *u_buf); return; } /* More than 4 bytes, print with spaces in integer offsets */ j = (int)((uintptr_t)buf % sizeof (int)); i = 0; if (j > 0) { i = sizeof (int) - j; for (; (j < sizeof (int)) && (outbufoffset < maxoffset); j++, u_buf++) outbufoffset += sprintf(&outbuf[outbufoffset], "%02x", *u_buf); } /* LINTED */ ip = (uint_t *)u_buf; for (; ((i + sizeof (int)) <= len) && (outbufoffset < maxoffset); i += sizeof (int), ip++) { outbufoffset += sprintf(&outbuf[outbufoffset], " %08x", *ip); } if (i < len) { /* Last element not int */ u_buf = (uchar_t *)ip; if (i > j) /* not first element */ outbufoffset += sprintf(&outbuf[outbufoffset], " "); for (; (i < len) && (outbufoffset < maxoffset); i++, u_buf++) { outbufoffset += sprintf(&outbuf[outbufoffset], "%02x", *u_buf); } } if (outbufoffset < maxoffset) outbufoffset += sprintf(&outbuf[outbufoffset], "\""); *outbufoffsetp = outbufoffset; }