469 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			469 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
 | |
|  * 
 | |
|  * This program is free software; you can redistribute it and/or modify it
 | |
|  * under the terms of version 2 of the GNU General Public License as
 | |
|  * published by the Free Software Foundation.
 | |
|  * 
 | |
|  * This program is distributed in the hope that it would be useful, but
 | |
|  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | |
|  * 
 | |
|  * Further, this software is distributed without any warranty that it is
 | |
|  * free of the rightful claim of any third person regarding infringement
 | |
|  * or the like.  Any license provided herein, whether implied or
 | |
|  * otherwise, applies only to this software file.  Patent licenses, if
 | |
|  * any, provided herein do not apply to combinations of this program with
 | |
|  * other software, or any other product whatsoever.
 | |
|  * 
 | |
|  * You should have received a copy of the GNU General Public License along
 | |
|  * with this program; if not, write the Free Software Foundation, Inc., 59
 | |
|  * Temple Place - Suite 330, Boston MA 02111-1307, USA.
 | |
|  * 
 | |
|  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
 | |
|  * Mountain View, CA  94043, or:
 | |
|  * 
 | |
|  * http://www.sgi.com 
 | |
|  * 
 | |
|  * For further information regarding this notice, see: 
 | |
|  * 
 | |
|  * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
 | |
|  */
 | |
| /*
 | |
|  * This module contains code for logging writes to files, and for
 | |
|  * perusing the resultant logfile.  The main intent of all this is
 | |
|  * to provide a 'write history' of a file which can be examined to
 | |
|  * judge the state of a file (ie. whether it is corrupted or not) based
 | |
|  * on the write activity.
 | |
|  *
 | |
|  * The main abstractions available to the user are the wlog_file, and
 | |
|  * the wlog_rec.  A wlog_file is a handle encapsulating a write logfile.
 | |
|  * It is initialized with the wlog_open() function.  This handle is
 | |
|  * then passed to the various wlog_xxx() functions to provide transparent
 | |
|  * access to the write logfile.
 | |
|  *
 | |
|  * The wlog_rec datatype is a structure which contains all the information
 | |
|  * about a file write.  Examples include the file name, offset, length,
 | |
|  * pattern, etc.  In addition there is a bit which is cleared/set based
 | |
|  * on whether or not the write has been confirmed as complete.  This 
 | |
|  * allows the write logfile to contain information on writes which have
 | |
|  * been initiated, but not yet completed (as in async io).
 | |
|  *
 | |
|  * There is also a function to scan a write logfile in reverse order.
 | |
|  *
 | |
|  * NOTE:	For target file analysis based on a write logfile, the
 | |
|  * 		assumption is made that the file being written to is
 | |
|  * 		locked from simultaneous access, so that the order of
 | |
|  * 		write completion is predictable.  This is an issue when
 | |
|  * 		more than 1 process is trying to write data to the same
 | |
|  *		target file simultaneously.
 | |
|  *
 | |
|  * The history file created is a collection of variable length records
 | |
|  * described by scruct wlog_rec_disk in write_log.h.  See that module for
 | |
|  * the layout of the data on disk.
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| #include <errno.h>
 | |
| #include <string.h>
 | |
| #include <sys/param.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/types.h>
 | |
| #include "write_log.h"
 | |
| 
 | |
| #ifndef BSIZE
 | |
| #ifdef linux
 | |
| #define BSIZE DEV_BSIZE
 | |
| #else
 | |
| #ifdef __CYGWIN__
 | |
| #define BSIZE S_BLKSIZE
 | |
| #else
 | |
| #define BSIZE BBSIZE
 | |
| #endif
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| #ifndef PATH_MAX
 | |
| #define PATH_MAX          255
 | |
| /*#define PATH_MAX pathconf("/", _PC_PATH_MAX)*/
 | |
| #endif
 | |
| 
 | |
| char	Wlog_Error_String[256];
 | |
| 
 | |
| #if __STDC__
 | |
| static int	wlog_rec_pack(struct wlog_rec *wrec, char *buf, int flag);
 | |
| static int	wlog_rec_unpack(struct wlog_rec *wrec, char *buf);
 | |
| #else
 | |
| static int	wlog_rec_pack();
 | |
| static int	wlog_rec_unpack();
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * Initialize a write logfile.  wfile is a wlog_file structure that has
 | |
|  * the w_file field filled in.  The rest of the information in the
 | |
|  * structure is initialized by the routine.
 | |
|  *
 | |
|  * The trunc flag is used to indicate whether or not the logfile should
 | |
|  * be truncated if it currently exists.  If it is non-zero, the file will
 | |
|  * be truncated, otherwise it will be appended to.
 | |
|  *
 | |
|  * The mode argument is the [absolute] mode which the file will be
 | |
|  * given if it does not exist.  This mode is not affected by your process
 | |
|  * umask.
 | |
|  */
 | |
| 
 | |
| int
 | |
| wlog_open(wfile, trunc, mode)
 | |
| struct wlog_file	*wfile;
 | |
| int			trunc;
 | |
| int			mode;
 | |
| {
 | |
| 	int	omask, oflags;
 | |
| 
 | |
| 	if (trunc)
 | |
| 		trunc = O_TRUNC;
 | |
| 
 | |
| 	omask = umask(0);
 | |
| 
 | |
| 	/*
 | |
| 	 * Open 1 file descriptor as O_APPEND
 | |
| 	 */
 | |
| 
 | |
| 	oflags = O_WRONLY | O_APPEND | O_CREAT | trunc;
 | |
| 	wfile->w_afd =
 | |
| 		open(wfile->w_file, oflags, mode);
 | |
| 	umask(omask);
 | |
| 
 | |
| 	if (wfile->w_afd == -1) {
 | |
| 		sprintf(Wlog_Error_String,
 | |
| 			"Could not open write_log - open(%s, %#o, %#o) failed:  %s\n",
 | |
| 			wfile->w_file, oflags, mode, strerror(errno));
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Open the next fd as a random access descriptor
 | |
| 	 */
 | |
| 
 | |
| 	oflags = O_RDWR;
 | |
| 	if ((wfile->w_rfd = open(wfile->w_file, oflags)) == -1) {
 | |
| 		sprintf(Wlog_Error_String,
 | |
| 			"Could not open write log - open(%s, %#o) failed:  %s\n",
 | |
| 			wfile->w_file, oflags, strerror(errno));
 | |
| 		close(wfile->w_afd);
 | |
| 		wfile->w_afd = -1;
 | |
| 		return -1;
 | |
| 	}
 | |
|     
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Release all resources associated with a wlog_file structure allocated
 | |
|  * with the wlog_open() call.
 | |
|  */
 | |
| 
 | |
| int
 | |
| wlog_close(wfile)
 | |
| struct wlog_file	*wfile;
 | |
| {
 | |
| 	close(wfile->w_afd);
 | |
| 	close(wfile->w_rfd);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Write a wlog_rec structure to a write logfile.  Offset is used to
 | |
|  * control where the record will be written.  If offset is < 0, the
 | |
|  * record will be appended to the end of the logfile.  Otherwise, the
 | |
|  * record which exists at the indicated offset will be overlayed.  This
 | |
|  * is so that we can record writes which are outstanding (with the w_done
 | |
|  * bit in wrec cleared), but not completed, and then later update the
 | |
|  * logfile when the write request completes (as with async io).  When
 | |
|  * offset is >= 0, only the fixed length portion of the record is 
 | |
|  * rewritten.  See text in write_log.h for details on the format of an
 | |
|  * on-disk record.
 | |
|  * 
 | |
|  * The return value of the function is the byte offset in the logfile
 | |
|  * where the record begins.
 | |
|  *
 | |
|  * Note:  It is the callers responsibility to make sure that the offset
 | |
|  * parameter 'points' to a valid record location when a record is to be
 | |
|  * overlayed.  This is guarenteed by saving the return value of a previous
 | |
|  * call to wlog_record_write() which wrote the record to be overlayed.
 | |
|  *
 | |
|  * Note2:  The on-disk version of the wlog_rec is MUCH different than
 | |
|  * the user version.  Don't expect to od the logfile and see data formatted
 | |
|  * as it is in the wlog_rec structure.  Considerable data packing takes
 | |
|  * place before the record is written.
 | |
|  */
 | |
| 
 | |
| int
 | |
| wlog_record_write(wfile, wrec, offset)
 | |
| struct wlog_file	*wfile;
 | |
| struct wlog_rec		*wrec;
 | |
| long			offset;
 | |
| {
 | |
|     int		reclen;
 | |
|     char	wbuf[WLOG_REC_MAX_SIZE + 2];
 | |
| 
 | |
|     /*
 | |
|      * If offset is -1, we append the record at the end of file
 | |
|      *
 | |
|      * Otherwise, we overlay wrec at the file offset indicated and assume
 | |
|      * that the caller passed us the correct offset.  We do not record the
 | |
|      * fname in this case.
 | |
|      */
 | |
| 
 | |
|     reclen = wlog_rec_pack(wrec, wbuf, (offset < 0));
 | |
| 
 | |
|     if (offset < 0) {
 | |
| 	/*
 | |
| 	 * Since we're writing a complete new record, we must also tack
 | |
| 	 * its length onto the end so that wlog_scan_backward() will work.
 | |
| 	 * Length is asumed to fit into 2 bytes.
 | |
| 	 */
 | |
| 	    
 | |
| 	    wbuf[reclen] = reclen / 256;
 | |
| 	    wbuf[reclen+1] = reclen % 256;
 | |
| 	    reclen += 2;
 | |
| 
 | |
| 	    write(wfile->w_afd, wbuf, reclen);
 | |
| 	    offset = lseek(wfile->w_afd, 0, SEEK_CUR) - reclen;
 | |
|     } else {
 | |
| 	    lseek(wfile->w_rfd, offset, SEEK_SET);
 | |
| 	    write(wfile->w_rfd, wbuf, reclen);
 | |
|     }
 | |
|     
 | |
|     return offset;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Function to scan a logfile in reverse order.  Wfile is a valid
 | |
|  * wlog_file structure initialized by wlog_open().  nrecs is the number
 | |
|  * of records to scan (all records are scanned if nrecs is 0).  func is
 | |
|  * a user-supplied function to call for each record found.  The function
 | |
|  * will be passed a single parameter - a wlog_rec structure .
 | |
|  */
 | |
| 
 | |
| int
 | |
| wlog_scan_backward(wfile, nrecs, func, data)
 | |
| struct wlog_file	*wfile;
 | |
| int 			nrecs;
 | |
| int 			(*func)();
 | |
| long			data;
 | |
| {
 | |
| 	int			fd, leftover, nbytes, offset, recnum, reclen, rval;
 | |
| 	char    		buf[BSIZE*32], *bufend, *cp, *bufstart;
 | |
| 	char		albuf[WLOG_REC_MAX_SIZE];
 | |
| 	struct wlog_rec	wrec;
 | |
| 
 | |
| 	fd = wfile->w_rfd;
 | |
| 
 | |
| 	/*
 | |
| 	 * Move to EOF.  offset will always hold the current file offset
 | |
| 	 */
 | |
| 
 | |
| 	lseek(fd, 0, SEEK_END);
 | |
| 	offset = lseek(fd, 0, SEEK_CUR);
 | |
| 
 | |
| 	bufend = buf + sizeof(buf);
 | |
| 	bufstart = buf;
 | |
| 
 | |
| 	recnum = 0;
 | |
| 	leftover = 0;
 | |
| 	while ((!nrecs || recnum < nrecs) && offset > 0) {
 | |
| 		/*
 | |
| 		 * Check for beginning of file - if there aren't enough bytes
 | |
| 		 * remaining to fill buf, adjust bufstart.
 | |
| 		 */
 | |
| 
 | |
| 		if (offset + leftover < sizeof(buf)) {
 | |
| 			bufstart = bufend - (offset + leftover);
 | |
| 			offset = 0;
 | |
| 		} else {
 | |
| 			offset -= sizeof(buf) - leftover;
 | |
| 		}
 | |
| 
 | |
| 		/* 
 | |
| 		 * Move to the proper file offset, and read into buf
 | |
| 		 */
 | |
| 
 | |
| 		lseek(fd, offset, SEEK_SET);
 | |
| 		nbytes = read(fd, bufstart, bufend - bufstart - leftover);
 | |
| 
 | |
| 		if (nbytes == -1) {
 | |
| 			sprintf(Wlog_Error_String,
 | |
| 				"Could not read history file at offset %d - read(%d, %#o, %d) failed:  %s\n",
 | |
| 				offset, fd, (int)bufstart,
 | |
| 				bufend - bufstart - leftover, strerror(errno));
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		cp = bufend;
 | |
| 		leftover = 0;
 | |
| 
 | |
| 		while (cp >= bufstart) {
 | |
| 
 | |
| 			/*
 | |
| 			 * If cp-bufstart is not large enough to hold a piece
 | |
| 			 * of record length information, copy remainder to end
 | |
| 			 * of buf and continue reading the file.
 | |
| 			 */
 | |
| 
 | |
| 			if (cp - bufstart < 2) {
 | |
| 				leftover = cp - bufstart;
 | |
| 				memcpy(bufend - leftover, bufstart, leftover);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			/*
 | |
| 			 * Extract the record length.  We must do it this way
 | |
| 			 * instead of casting cp to an int because cp might
 | |
| 			 * not be word aligned.
 | |
| 			 */
 | |
| 
 | |
| 			reclen = (*(cp-2) * 256) + *(cp -1);
 | |
| 
 | |
| 			/*
 | |
| 			 * If cp-bufstart isn't large enough to hold a
 | |
| 			 * complete record, plus the length information, copy
 | |
| 			 * the leftover bytes to the end of buf and continue
 | |
| 			 * reading.
 | |
| 			 */
 | |
| 
 | |
| 			if (cp - bufstart < reclen + 2) {
 | |
| 				leftover = cp - bufstart;
 | |
| 				memcpy(bufend - leftover, bufstart, leftover);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			/*
 | |
| 			 * Adjust cp to point at the start of the record.
 | |
| 			 * Copy the record into wbuf so that it is word
 | |
| 			 * aligned and pass the record to the user supplied
 | |
| 			 * function.
 | |
| 			 */
 | |
| 
 | |
| 			cp -= reclen + 2;
 | |
| 			memcpy(albuf, cp, reclen);
 | |
| 
 | |
| 			wlog_rec_unpack(&wrec, albuf);
 | |
| 
 | |
| 			/*
 | |
| 			 * Call the user supplied function -
 | |
| 			 * stop if instructed to.
 | |
| 			 */
 | |
| 
 | |
| 			if ((rval = (*func)(&wrec, data)) == WLOG_STOP_SCAN) {
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			recnum++;
 | |
| 
 | |
| 			if (nrecs && recnum >= nrecs)
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * The following 2 routines are used to pack and unpack the user
 | |
|  * visible wlog_rec structure to/from a character buffer which is
 | |
|  * stored or read from the write logfile.  Any changes to either of
 | |
|  * these routines must be reflected in the other.
 | |
|  */
 | |
| 
 | |
| static int
 | |
| wlog_rec_pack(wrec, buf, flag)
 | |
| struct wlog_rec	*wrec;
 | |
| char		*buf;
 | |
| int             flag;
 | |
| {
 | |
| 	char			*file, *host, *pattern;
 | |
| 	struct wlog_rec_disk	*wrecd;
 | |
| 
 | |
| 	wrecd = (struct wlog_rec_disk *)buf;
 | |
| 
 | |
| 	wrecd->w_pid = (uint)wrec->w_pid;
 | |
| 	wrecd->w_offset = (uint)wrec->w_offset;
 | |
| 	wrecd->w_nbytes = (uint)wrec->w_nbytes;
 | |
| 	wrecd->w_oflags = (uint)wrec->w_oflags;
 | |
| 	wrecd->w_done = (uint)wrec->w_done;
 | |
| 	wrecd->w_async = (uint)wrec->w_async;
 | |
| 
 | |
| 	wrecd->w_pathlen = (wrec->w_pathlen > 0) ? (uint)wrec->w_pathlen : 0;
 | |
| 	wrecd->w_hostlen = (wrec->w_hostlen > 0) ? (uint)wrec->w_hostlen : 0;
 | |
| 	wrecd->w_patternlen = (wrec->w_patternlen > 0) ? (uint)wrec->w_patternlen : 0;
 | |
| 
 | |
| 	/*
 | |
| 	 * If flag is true, we should also pack the variable length parts
 | |
| 	 * of the wlog_rec.  By default, we only pack the fixed length
 | |
| 	 * parts.
 | |
| 	 */
 | |
| 
 | |
| 	if (flag) {
 | |
| 		file = buf + sizeof(struct wlog_rec_disk);
 | |
| 		host = file + wrecd->w_pathlen;
 | |
| 		pattern = host + wrecd->w_hostlen;
 | |
| 	
 | |
| 		if (wrecd->w_pathlen > 0)
 | |
| 			memcpy(file, wrec->w_path, wrecd->w_pathlen);
 | |
| 	
 | |
| 		if (wrecd->w_hostlen > 0)
 | |
| 			memcpy(host, wrec->w_host, wrecd->w_hostlen);
 | |
| 	
 | |
| 		if (wrecd->w_patternlen > 0)
 | |
| 			memcpy(pattern, wrec->w_pattern, wrecd->w_patternlen);
 | |
| 
 | |
| 		return (sizeof(struct wlog_rec_disk) +
 | |
| 			wrecd->w_pathlen + wrecd->w_hostlen + wrecd->w_patternlen);
 | |
| 	} else {
 | |
| 		return sizeof(struct wlog_rec_disk);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int
 | |
| wlog_rec_unpack(wrec, buf)
 | |
| struct wlog_rec	*wrec;
 | |
| char		*buf;
 | |
| {
 | |
| 	char			*file, *host, *pattern;
 | |
| 	struct wlog_rec_disk	*wrecd;
 | |
| 
 | |
| 	bzero((char *)wrec, sizeof(struct wlog_rec));
 | |
| 	wrecd = (struct wlog_rec_disk *)buf;
 | |
| 
 | |
| 	wrec->w_pid = wrecd->w_pid;
 | |
| 	wrec->w_offset = wrecd->w_offset;
 | |
| 	wrec->w_nbytes = wrecd->w_nbytes;
 | |
| 	wrec->w_oflags = wrecd->w_oflags;
 | |
| 	wrec->w_hostlen = wrecd->w_hostlen;
 | |
| 	wrec->w_pathlen = wrecd->w_pathlen;
 | |
| 	wrec->w_patternlen = wrecd->w_patternlen;
 | |
| 	wrec->w_done = wrecd->w_done;
 | |
| 	wrec->w_async = wrecd->w_async;
 | |
| 
 | |
| 	if (wrec->w_pathlen > 0) {
 | |
| 		file = buf + sizeof(struct wlog_rec_disk);
 | |
| 		memcpy(wrec->w_path, file, wrec->w_pathlen);
 | |
| 	}
 | |
| 
 | |
| 	if (wrec->w_hostlen > 0) {
 | |
| 		host = buf + sizeof(struct wlog_rec_disk) + wrec->w_pathlen;
 | |
| 		memcpy(wrec->w_host, host, wrec->w_hostlen);
 | |
| 	}
 | |
| 
 | |
| 	if (wrec->w_patternlen > 0) {
 | |
| 		pattern = buf + sizeof(struct wlog_rec_disk) +
 | |
| 			wrec->w_pathlen + wrec->w_hostlen;
 | |
| 		memcpy(wrec->w_pattern, pattern, wrec->w_patternlen);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 |