/**************************************************************************** * * * COPYRIGHT (c) 1990 - 2004 * * This Software Provided * * By * * Robin's Nest Software Inc. * * * * Permission to use, copy, modify, distribute and sell this software and * * its documentation for any purpose and without fee is hereby granted, * * provided that the above copyright notice appear in all copies and that * * both that copyright notice and this permission notice appear in the * * supporting documentation, and that the name of the author not be used * * in advertising or publicity pertaining to distribution of the software * * without specific, written prior permission. * * * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * * NO EVENT SHALL HE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL * * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF * * THIS SOFTWARE. * * * ****************************************************************************/ /* * Module: dtutil.c * Author: Robin T. Miller * * Description: * Utility routines for generic data test program. */ #include "dt.h" #include #include #include #include /* for MAXHOSTNAMELEN */ #include #include #include #include #if defined(sun) # define strtoul strtol #endif /* defined(sun) */ #if defined(ultrix) extern void *valloc(size_t size); #endif /* defined(ultrix) */ /* * Modification History: * * October 21st, 2004 by Robin Miller. * Report the current file position during error report. * Although the relative block number is formulated from the file * offset, some folks would also like to see the actual offset. * For random I/O, always align offsets to the device size (dsize). * Failure to do this causes false data corruptions to regular files. * For big-endian machines, the IOT pattern must be byte swapped, since * IOT is designed to be little-endian only. * * June 24th, 2004 by Robin Miller. * Major cleanup to init_iotdata() function to properly handle * non-modulo prefix and transfer counts. The odd bytes were not * initialized to something (now ~0) which lead to false failures. * * June 22nd, 2004 by Robin Miller. * Added support for triggers on corruption. * Properly report failing lba when lbdata is used. * Don't align random offsets to the device size when testing * regular files through a file system. In general, random I/O is * not usually going to work to a file, since part of file usually * gets overwitten (sorry, we don't track blocks already written). * * March 30th, 2004 by Robin Miller. * Improve lseek error messages (should they should fail). * * March 24th, 2004 by Robin Miller. * Update code in do_random() where the random data limit * (rdata_limit) was being truncated to an unsigned long, which * is 32-bits many systems. This causes large capacity disks, * such as 36GB (0x900000000), to be truncated to zero which * causes a divide by zero ("Arithmetic exception" on HP-UX). * Also increase the size of the random number (randum) to * 64-bits (on 32-bit systems), so larger seeks are possible. * * February 23, 2004 by Robin Miller. * Reverse the buffer and prefix pattern bytes being dumped * in verify_prefix(), so they properly reflect the expected versus * found bytes. Otherwise, the information is misleading. * * November 25th, 2003 by Robin Miller. * When formatting the prefix string, if we're doing random * I/O, round the prefix string up to sizeof(u_int32), so partial * patterns (non-modulo our 4 byte pattern) do not get used, which * causes false data compare failures. * Note: Failures still occur if random I/O is used with pattern * files containing non-repeating data pattern bytes! * * November 20th, 2003 by Robin Miller. * Broken verify data function up for better performance. * Update prefix string logic to write the string in every * logical block (lbdata_size). This had to be done or else random * I/O with prefix strings failed! It also give better coverage. * * November 17th, 2003 by Robin Miller. * Breakup output to stdout or stderr, rather than writing * all output to stderr. If output file is stdout ('-') or a log * file is specified, then all output reverts to stderr. * * October 8th, 2003 by Robin Miller. * On AIX, accept ENXIO for I/O's pass EOF. * * September 27th, 2003 by Robin Miller. * Added support for AIX. * * March 20th, 2003 by Robin Miller. * Added FmtPrefix() to format the prefix string. * * March 18th, 2003 by Robin Miller. * Optimize code and loops using USE_PATTERN_BUFFER define. * * March 15th, 2003 by Robin Miller. * Added support for data pattern prefix strings. * * March 4th, 2003 by Robin Miller. * Add support for broken EOF SunOS release. This means writes * at EOF return a count of zero, but there is no errno value to key * off like POSIX specifies. * * November 21st, 2002 by Robin Miller. * On HP-UX, accept ENXIO for I/O's pass EOF. * * November 14th, 2002 by Robin Miller. * Add support for 32-bit HP-UX compilation. * * October 10th, 2002 by Robin Miller. * Display correct erroring byte during data compare errors, * when using a 32-bit hex data pattern. * * February 1st, 2002 by Robin Miller. * Make porting changes necessary for Solaris 8. * * January 29th, by Robin Miller. * Minor tweak to clarify correct versus incorrect data dumped. * * June 25th, 2001 by Robin Miller. * Report mt status for all errors, not just EIO errors. Also, * report mt status on unexpected EOF/EOM when no data transferred. * Note: Reporting mt and EEI status is only done for Tru64 Unix. * * February 24th, 2001 by Robin Miller. * Add conditionalization for QNX RTP (Neutrino). * * January 24th, 2001 by Robin Miller. * Add support for variable I/O requests sizes. * Conditionalize some functions to allow INLINE macros. * Modifications to allow IOT test pattern to honor the lba * size variable, rather than hardcoding IOT to 512 byte sectors. * * January 15th, 2001 by Robin Miller. * Don't terminate() on failures, return error to caller. * Note: seek functions still call terminate()... more work! * * January 2nd, 2001 by Robin Miller. * Make changes to build using MKS/NuTCracker product. * * October 4th, 2000 by Robin Miller. * Update is_Eof() to accept ENXIO for AIO reads @ EOM on * SCO UnixWare systems. All other systems return ENOSPC or zero. * * April 25th, 2000 by Robin Miller. * Added an expect flag to dump_buffer to help with formatting. * * April 18th, 2000 by Robin Miller. * Modified calls to report_error() to ensure error count bumped. * * March 28th, 2000 by Robin Miller. * Modified file position functions to accept device information * parameter, so necessary debug could be added to these funcitons. * Also, only scale the random position upwards by the device size * every other record, so low blocks gets utilized more often. * Note: These changes degrade random I/O performance slightly. * [ All debug needs ripped out to obtain better performance, ] * * February 17th, 2000 by Robin Miller. * Adding better support for multi-volume tape testing. Mainly, * make it work with writing multiple tape files, rather than one file. * * February 16th, 2000 by Robin Miller. * Set exit_status to FAILURE in RecordError() function. This * is necessary to catch any error being logged, since test functions * no longer call terminate() with FAILURE status. * * January 22nd, 2000 by Robin Miller. * Add check for Cygwin device names for Windows/NT. * * January 4th, 2000 by Robin Miller. * Major cleanup in verify_data(). Now reports IOT block number. * Added verify_buffers() and verify_lbdata() functions for raw. * * December 31st, 1999 by Robin Miller. * Modify do_random() to ensure rlimit is not exceeded when the lower * file position is factored in, to stay within desired lba range. * * December 30th, 1999 by Robin Miller. * Modify do_random() to take into consideration the transfer size, * to ensure the requested random I/O data limit is not exceeded. * Added get_lba() function to return the current logical block address. * * November 11th, 1999 by Robin Miller. * Add LogDiagMsg() to log diagnostic information to event logger. * * November 10th, 1999 by Robin Miller. * Update make_lbdata() & winit_lbdata() so if the IOT test pattern is * selected, and the device block size is NOT 512 bytes, we force 1 block. * Note: This only affects Tru64 Unix, since we obtain the real sector size. * * November 9th, 1999 by Robin Miller. * Remove use of '%p' in dump_buffer(), since it's use is inconsistent. * With Cygnus Solutions, '%p' displays a leading '0x', other OS's don't! * * August 26th, 1999 by Robin Miller. * Report an error for ENOSPC, if no data has been transferred. * Previously, this error was only reported for tape devices. * * August 6th, 1999 by Robin Miller. * Better parameterizing of "long long" printf formatting. * * July 29, 1999 by Robin Miller. * Merge in changes made to compile on FreeBSD. * * July 22nd, 1999 by Robin Miller. * Added support for IOT (DJ's) test pattern. * * July 7th, 1999 by Robin Miller. * Modify CvtStrtoValue() to check for recursive calls which return * zero (0) for '/' and '%', which cause a divide by zero fault/core dump. * * June 28, 1999 by Robin Miller. * For 32-bit systems, added CvtStrtoLarge() function to * permit double or long long values, since u_long is too small. * * May 27, 1999 by Robin Miller. * Added support for micro-second delays. * * April 8, 1999 by Robin Miller. * Added format_ltime() to format table() sysinfo times. * * January 7, 1999 by Robin Miller. * Removed fflush() in Fprintf() function which caused intermixed * output when multiple processes were running (i.e., serial line testing). * * December 21, 1998 by Robin Miller. * - update Malloc() to clear allocated memory. * - for DUNIX, updates to handle resets for tapes. * * December 16, 1998 by Robin Miller. * Merge in changes made by George Bittner: * - modify do_random(), use random value as a block number * instead of a byte offset, for testing larger disks/files. * - ensure the random offset is within starting file position * and the end of the disk/partition. * * November 19, 1998, by Robin Miller. * Fix problem where wrong lbdata lba was returned for offset 0. * This required updates to make_lbdata() and winit_lbdata() functions. * [ sorry folks, major brain burp!!! ] * * November 16, 1998 by Robin Miller. * For AIO, report the relative block from saved AIO control block. * * October 29, 1998 by Robin Miller. * Implement a random I/O data limit, instead of using the normal * data limit variable (not good to dual purpose this value). * * October 26, 1998 by Robin Miller. * Add functions make_lbdata() and winit_lbdata() which handle * using both random I/O and lbdata options. The file offset seeked * to is used as the starting lbdata address. * * April 28, 1998 by Robin Miller. * For WIN32/NT, or in O_BINARY into open flags to force binary * mode (the default is text mode). * * May 16, 1997 by Robin Miller. * Modified macro used in ReportDeviceInfo() which rounded block * up, causing the erroring block to be off by one, when data * compare errors were not at the beginning of a block. Also, * report the block offset (byte within block) when non-modulo. * * May 14, 1997 by Robin Miller. * If we encounter a ENOSPC error and no data has been transferred, * flag this as an error. This normally indicates a zero length * partition or the user may have seek'ed past eom or eop. * * March 27, 1997 by Ali Eghlima. * Fix call to report_error() to reflect "unlink" failed. * * March 7, 1997 by Robin Miller. * In ReportDeviceInfo(), when we're doing a copy/verify * operation, allow output device to be a different offset. * * February 28, 1996 by Robin Miller. * Modified ReportDeviceInfo() so we seek past disk errors. * This action allows testing to continue with "errors=n". * * February 21, 1996 by Robin Miller. * Added do_random() function for random I/O to disks. * * December 19, 1995 by Robin Miller. * Conditionalize for Linux Operating System. * * Novemeber 18, 1995 by Robin Miller. * Removing unaligned data access (ade) test code (code cleanup). * * November 11, 1995 by Robin Miller. * Fix bug with verifying pad bytes. Basically, the previous logic, * while valid for short reads, was incorrect when the pad bytes did * *not* start on a modulo sizeof(int) boundary. This caused variable * length reads with small increment values to report an (invalid) * pad byte data compare error. NOTE: Only occurred w/pattern file. * * July 22, 1995 by Robin Miller. * Finally, correctly dump data buffers with context (yes, I made * sure I tested min/max limits via fault insertion this time). * * July 15, 1995 by Robin Miller. * Add is_Eof() to handle end of media (ENOSPC), and cleanup code. * * July 7, 1995 by Robin Miller. * Correctly dump the pattern buffer on data compare errors. * When reporting a compare error, also display the byte count. * This latter information is useful for variable length records. * * July 6, 1995 by Robin Miller. * Changed a number of fprintf's to Fprintf so the child PID gets * reported during error processing (how did I miss this stuff?). * * September 23, 1994 by Robin Miller. * Make changes necessary to build on QNX 4.21 release. * * January 20, 1994 by Robin Miller. * When checking the pad bytes, don't do the entire buffer since very * large buffers (e.g. 100m) may have been specified using min, max, * and incr options which cause excessive paging and poor performance. * * October 28, 1993 by Robin Miller. * For multiple processes, display the PID to differentiate output. * * September 15, 1993 by Robin Miller. * Limit pattern buffer dumping to size of pattern buffer. * * September 17, 1993 by Robin Miller. * Added RecordWarning() function to reporting record number and * time stamp on warning errors (useful for debugging purposes). * More limiting of data & pattern buffer dumping (less bytes). * * September 3, 1993 by Robin Miller. * Allow "inf" or "INF" keywords to set maximum counts. * * September 2, 1993 by Robin Miller. * Added device specific information. * * September 1, 1993 by Robin Miller. * Report proper record number during errors (add partial records). * Limit data dumped when data verify errors occur (short records). * * August 27, 1993 by Robin MIller. * Added support for DEC OSF/1 POSIX Asynchronous I/O (AIO). * * August 17, 1993 by Robin Miller. * Added function RecordError() to record when an error occurs, * and added function Ctime() to append date/time string to the * log buffer. * * August 10, 1993 by Robin Miller. * Added verify_padbuf() function to check pad buffer bytes after * read operations to ensure they weren't overwritten. * * August 4, 1993 by Robin Miller. * Added various printing functions to simplify error reporting. * * September 11, 1992 by Robin Miller. * Added additional debug information when reopening a device. * * September 5, 1992 by Robin Miller. * Initial port to QNX 4.1 Operating System. * * May 22, 1992 by Robin Miller. * Control / force kernel address data exception via flag. * * March 11, 1992 by Robin Miller. * Changes necessary for port to 64-bit Alpha architecture. * * October 16, 1990 by Robin Miller. * Added myalloc() memory allocation function to allocate and * align the buffer address using the alignment value. * * October 9, 1990 by Robin Miller. * Use variable hz instead of HZ define for clock ticks per second * so user can specify different value via "hz=ticks" option. * * August 21, 1990 by Robin Miller. * Changed exit status so scripts can detect and handle errors * based on the exit code. If not success, fatal error, or end * of file/tape, the exit code is the error number (errno). * * August 21, 1990 by Robin Miller. * Added function dump_buffer() to dump buffer in hex bytes. * * August 8, 1990 by Robin Miller. * Added functions seek_file() and skip_records(). Modified seek * file function to avoid lseek() system call overhead (see code). * * Changed malloc() to valloc() to align buffer on page boundry. * On some archetectures, this results on better performance to * raw devices since the DMA is done directly to the users' buffer. * * April 11, 1990 by Robin Miller. * Added function mmap_file() to memory map a file. Added munmap() * syscall to reopen_file() which gets called for multiple passes. * This allows us to time the mmap() as well as the munmap() code. * * March 21, 1990 by Robin Miller. * Added function delete_file() which checks for a regular file * and then unlinks it. * */ /* * Forward References: */ size_t copy_prefix(u_char *buffer, size_t bcount); int verify_prefix(struct dinfo *dip, u_char *buffer, size_t bcount, size_t *pcount); static int verify_data_normal( struct dinfo *dip, u_char *buffer, size_t bcount, u_int32 pattern); static int verify_data_prefix( struct dinfo *dip, u_char *buffer, size_t bcount, u_int32 pattern ); static int verify_data_with_lba(struct dinfo *dip, u_char *buffer, size_t bcount, u_int32 pattern, u_int32 *lba ); static size_t CalculateDumpSize(size_t size); static int dopad_verify ( struct dinfo *dip, u_char *buffer, size_t offset, u_int32 pattern, size_t pbytes, size_t pindex, bool inverted ); int vSprintf(char *bufptr, const char *msg, va_list ap); #if 0 static char *pad_str = "Pad"; #endif static char *lba_str = "Lba"; static char *data_str = "Data"; static char *pattern_str = "Pattern"; static char *prefix_str = "Prefix"; static char *verify_str = "Verify"; static char *compare_error_str = "Data compare error at byte"; static char *bad_conversion_str = "Warning: unexpected conversion size of %d bytes.\n"; /************************************************************************ * * * delete_file() - Delete the specified file. * * * * This function ensures the file specified is a regular * * file before doing an unlink() to delete the file. * * * * Inputs: dip = The device information pointer. * * * * Outputs: Returns SUCCESS/FAILURE = File Delete/Not Deleted. * * * ************************************************************************/ int delete_file (struct dinfo *dip) { int status; if (debug_flag) { Printf("Deleting file '%s'...\n", dip->di_dname); } if ( (status = unlink(dip->di_dname)) == FAILURE) { report_error ("unlink", TRUE); } return (status); } void mySleep(unsigned int sleep_time) { if (micro_flag) { (void) usleep(sleep_time); } else { (void) sleep(sleep_time); } return; } /************************************************************************ * * * CalculateDumpSize() - Calculate the number of data bytes to dump. * * * * Description: * * For non-memory mapped files, we'll dump the pad bytes. These * * pad bytes do not exist for memory mapped files which are directly * * mapped to memory addresses. * * * * Inputs: size = The size of the buffer to dump. * * * * Outputs: Size of buffer to dump. * * * ************************************************************************/ static size_t CalculateDumpSize (size_t size) { size_t dump_size = size; if (!mmap_flag) { dump_size += PADBUFR_SIZE; } if (dump_size > data_size) dump_size = data_size; return (dump_size); } /************************************************************************ * * * dump_buffer() Dump data buffer in hex bytes. * * * * Inputs: name = The buffer name being dumped. * * base = Base pointer of buffer to dump. * * ptr = Pointer into buffer being dumped. * * dump_size = The size of the buffer to dump. * * bufr_size = The maximum size of this buffer. * * expected = Boolean flag (True = Expected). * * * * Return Value: * * Void. * * * ************************************************************************/ void dump_buffer ( char *name, u_char *base, u_char *ptr, size_t dump_size, size_t bufr_size, bool expected ) { size_t i, limit, offset; u_int field_width = 16; u_char *bend = (base + bufr_size); u_char *bptr; /* * Since many requests do large transfers, limit data dumped. */ limit = (dump_size < dump_limit) ? dump_size : dump_limit; /* * Now to provide context, attempt to dump data on both sides of * the corrupted data, ensuring buffer limits are not exceeded. */ bptr = (ptr - (limit >> 1)); if (bptr < base) bptr = base; if ( (bptr + limit) > bend) { limit = (bend - bptr); /* Dump to end of buffer. */ } offset = (ptr - base); /* Offset to failing data. */ /* * NOTE: Rotate parameters are not displayed since we don't have * the base data address and can't use global due to AIO design. * [ if I get ambitious, I'll correct this in a future release. ] */ Fprintf ("The %scorrect data starts at address %#lx (marked by asterisk '*')\n", (expected) ? "" : "in", ptr); Fprintf ("Dumping %s Buffer (base = %#lx, offset = %u, limit = %u bytes):\n", name, base, offset, limit); LogMsg (efp, logLevelError, (PRT_NOIDENT | PRT_NOFLUSH), "\n"); for (i = 0; i < limit; i++, bptr++) { if ((i % field_width) == (size_t) 0) { if (i) fprintf (efp, "\n"); LogMsg (efp, logLevelError, (PRT_NOIDENT | PRT_NOFLUSH), "%#lx ", (u_long)bptr); } fprintf (efp, "%c%02x", (bptr == ptr) ? '*' : ' ', *bptr); } if (i) Fprint ("\n"); if (expected) { LogMsg (efp, logLevelError, (PRT_NOIDENT | PRT_NOFLUSH), "\n"); } (void)fflush(efp); } /************************************************************************ * * * fill_buffer() - Fill Buffer with a Data Pattern. * * * * Description: * * If a pattern_buffer exists, then this data is used to fill the * * buffer instead of the data pattern specified. * * * * Inputs: buffer = Pointer to buffer to fill. * * byte_count = Number of bytes to fill. * * pattern = Data pattern to fill buffer with. * * * * Return Value: * * Void. * * * ************************************************************************/ void fill_buffer ( u_char *buffer, size_t byte_count, u_int32 pattern) { register u_char *bptr = buffer; register u_char *pptr, *pend; register size_t bcount = byte_count; pptr = pattern_bufptr; pend = pattern_bufend; /* * Initialize the buffer with a data pattern. */ if ( !prefix_string ) { while (bcount--) { *bptr++ = *pptr++; if (pptr == pend) { pptr = pattern_buffer; } } } else { register size_t i; for (i = 0; i < bcount; ) { if ((i % lbdata_size) == 0) { size_t pcount = copy_prefix (bptr, (bcount - i)); i += pcount; bptr += pcount; continue; } *bptr++ = *pptr++; i++; if (pptr == pend) { pptr = pattern_buffer; } } } pattern_bufptr = pptr; return; } /************************************************************************ * * * init_buffer() - Initialize Buffer with a Data Pattern. * * * * Inputs: buffer = Pointer to buffer to init. * * count = Number of bytes to initialize. * * pattern = Data pattern to init buffer with. * * * * Return Value: * * Void. * * * ************************************************************************/ void init_buffer ( u_char *buffer, size_t count, u_int32 pattern ) { register u_char *bptr; union { u_char pat[sizeof(u_int32)]; u_int32 pattern; } p; register size_t i; /* * Initialize the buffer with a data pattern. */ p.pattern = pattern; bptr = buffer; for (i = 0; i < count; i++) { *bptr++ = p.pat[i & (sizeof(u_int32) - 1)]; } return; } #if _BIG_ENDIAN_ /************************************************************************ * * * init_swapped() - Initialize Buffer with a Swapped Data Pattern. * * * * Inputs: buffer = Pointer to buffer to init. * * count = Number of bytes to initialize. * * pattern = Data pattern to init buffer with. * * * * Return Value: * * Void. * * * ************************************************************************/ void init_swapped ( u_char *buffer, size_t count, u_int32 pattern ) { register u_char *bptr; union { u_char pat[sizeof(u_int32)]; u_int32 pattern; } p; register size_t i; /* * Initialize the buffer with a data pattern. */ p.pattern = pattern; bptr = buffer; for (i = count; i ; ) { *bptr++ = p.pat[--i & (sizeof(u_int32) - 1)]; } return; } #endif /* _BIG_ENDIAN_ */ /************************************************************************ * * * init_lbdata() - Initialize Data Buffer with Logical Block Data. * * * * Description: * * This function takes the starting logical block address, and * * inserts it every logical block size bytes, overwriting the first 4 * * bytes of each logical block with its' address. * * * * Inputs: buffer = The data buffer to initialize. * * count = The data buffer size (in bytes). * * lba = The starting logical block address. * * lbsize = The logical block size (in bytes). * * * * Outputs: Returns the next lba to use. * * * ************************************************************************/ u_int32 init_lbdata ( u_char *buffer, size_t count, u_int32 lba, u_int32 lbsize ) { u_char *bptr = buffer; register ssize_t i; /* * Initialize the buffer with logical block data. */ if (prefix_string) { register size_t pcount = 0, scount = lbsize; /* * The lba is encoded after the prefix string. */ pcount = MIN(prefix_size, count); scount -= pcount; for (i = 0; (i+pcount+sizeof(lba)) <= count; ) { bptr += pcount; htos (bptr, lba, sizeof(lba)); i += lbsize; bptr += scount; lba++; } } else { for (i = 0; (i+sizeof(lba)) <= count; ) { htos (bptr, lba, sizeof(lba)); i += lbsize; bptr += lbsize; lba++; } } return (lba); } #if !defined(INLINE_FUNCS) /* * Calculate the starting logical block number. */ u_int32 make_lba( struct dinfo *dip, off_t pos ) { if (pos == (off_t) 0) { return ((u_int32) 0); } else { return (pos / lbdata_size); } } off_t make_offset(struct dinfo *dip, u_int32 lba) { return ((off_t)(lba * lbdata_size)); } /* * Calculate the starting lbdata block number. */ u_int32 make_lbdata( struct dinfo *dip, off_t pos ) { if (pos == (off_t) 0) { return ((u_int32) 0); } else { return (pos / lbdata_size); } } #endif /* !defined(INLINE_FUNCS) */ u_int32 winit_lbdata( struct dinfo *dip, off_t pos, u_char *buffer, size_t count, u_int32 lba, u_int32 lbsize ) { if (user_lbdata) { /* Using user defined lba, not file position! */ return (init_lbdata (buffer, count, lba, lbsize)); } else if (pos == (off_t) 0) { return (init_lbdata (buffer, count, (u_int32) 0, lbsize)); } else { return (init_lbdata (buffer, count, (pos / lbsize), lbsize)); } } /************************************************************************ * * * init_iotdata() - Initialize Pattern Buffer with IOT test pattern. * * * * Description: * * This function takes the starting logical block address, and * * inserts it every logical block size bytes. The data pattern used * * is the logical block with the constant 0x01010101 added every u_int. * * * * NOTE: The IOT pattern is stored in the pattern buffer, which * * is assumed to be page aligned, thus there are no alignment problems. * * Note: Addition of prefix string changes this alignment assumption! * * * * This implementation does _not_ provide the "best" performance, * * but it does allow the normal 'dt' data flow to be mostly unaffected. * * * * Inputs: bcount = The data buffer size (in bytes). * * lba = The starting logical block address. * * lbsize = The logical block size (in bytes). * * * * Outputs: Returns the next lba to use. * * * * Note: If the count is smaller than sizeof(u_int32), then no lba is * * encoded in the buffer. Instead, we init odd bytes with ~0. * * * ************************************************************************/ u_int32 init_iotdata ( size_t bcount, u_int32 lba, u_int32 lbsize ) { register ssize_t count = (ssize_t)bcount; register u_int32 lba_pattern; /* IOT pattern initialization size. */ register int iot_icnt = sizeof(lba_pattern); register int i; if (lbsize == 0) return (lba); pattern_bufptr = pattern_buffer; /* * If the prefix string is a multiple of an unsigned int, * we can initialize the buffer using 32-bit words, otherwise * we must do so a byte at a time which is slower (of course). * * Note: This 32-bit fill is possible since the pattern buffer * is known to be page aligned! * * Format: ... * * Also Note: The prefix string is copied to the data buffer * by fill_buffer(), so don't be mislead by this code. The * IOT pattern bytes is adjusted for the sizeof(prefix_string). */ if (count < iot_icnt) { init_buffer(pattern_buffer, count, ~0); } else if (prefix_string && (prefix_size & (iot_icnt-1))) { register u_char *bptr = pattern_buffer; /* * Initialize the buffer with the IOT test pattern. */ while ( (count > 0) && (count >= sizeof(lba)) ) { /* * Process one lbsize'd block at a time. * * Format: ... */ lba_pattern = lba++; for (i = (lbsize - prefix_size); ( (i > 0) && (count >= iot_icnt) ); ) { #if _BIG_ENDIAN_ init_swapped(bptr, iot_icnt, lba_pattern); #else /* !_BIG_ENDIAN_ */ init_buffer(bptr, iot_icnt, lba_pattern); #endif /* _BIG_ENDIAN_ */ lba_pattern += 0x01010101; i -= iot_icnt; bptr += iot_icnt; count -= iot_icnt; } } /* Handle any residual count here! */ if (count && (count < iot_icnt)) { init_buffer(bptr, count, ~0); } } else { register int wperb; /* words per lbsize'ed buffer */ register u_int32 *bptr; wperb = (lbsize / iot_icnt); bptr = (u_int32 *)pattern_buffer; if (prefix_string) { /* * Adjust counts for the prefix string. */ wperb -= (prefix_size / iot_icnt); count -= prefix_size; } /* * Initialize the buffer with the IOT test pattern. */ while ( (count > 0) && (count >= iot_icnt) ) { lba_pattern = lba++; for (i = 0; (i < wperb) && (count >= iot_icnt); i++) { #if _BIG_ENDIAN_ init_swapped((u_char *)bptr++, iot_icnt, lba_pattern); #else /* !_BIG_ENDIAN_ */ *bptr++ = lba_pattern; #endif /* _BIG_ENDIAN_ */ lba_pattern += 0x01010101; count -= iot_icnt; } } /* Handle any residual count here! */ if (count && (count < iot_icnt)) { init_buffer((u_char *)bptr, count, ~0); } } return (lba); } /************************************************************************ * * * init_padbytes() - Initialize pad bytes at end of data buffer. * * * * Inputs: buffer = Pointer to start of data buffer. * * offset = Offset to where pad bytes start. * * pattern = Data pattern to init buffer with. * * * * Return Value: * * Void. * * * ************************************************************************/ void init_padbytes ( u_char *buffer, size_t offset, u_int32 pattern ) { size_t i; u_char *bptr; union { u_char pat[sizeof(u_int32)]; u_int32 pattern; } p; p.pattern = pattern; bptr = buffer + offset; for (i = 0; i < PADBUFR_SIZE; i++) { *bptr++ = p.pat[i & (sizeof(u_int32) - 1)]; } } /* * copy_prefix() - Copy Prefix String to Buffer. * * Inputs: * buffer = Pointer to buffer to copy prefix. * bcount = Count of remaining buffer bytes. * * Implicit Inputs: * prefix_string = The prefix string. * prefix_size = The prefix size. * * Outputs: * Returns number of prefix bytes copied. */ size_t copy_prefix( u_char *buffer, size_t bcount ) { size_t pcount; pcount = MIN(prefix_size, bcount); (void)memcpy(buffer, prefix_string, pcount); return (pcount); } /* * verify_prefix() - Verify Buffer with Prefix String. * * Inputs: * dip = The device information pointer. * buffer = Address of buffer to verify. * bcount = Count of remaining buffer bytes. * pcount = Pointer to return prefix count verified. * * Implicit Inputs: * prefix_string = The prefix string. * prefix_size = The prefix size. * * Outputs: * pcount gets the prefix string count verified. * Return value is Success or Failure. */ int verify_prefix( struct dinfo *dip, u_char *buffer, size_t bcount, size_t *pcount ) { register u_char *bptr = buffer; register u_char *pstr = (u_char *)prefix_string; register size_t count; register int i; int status = SUCCESS; count = MIN(prefix_size, bcount); for (i = 0; (i < count); i++, bptr++, pstr++) { if (*bptr != *pstr) { size_t dump_size; ReportCompareError (dip, count, i, *pstr, *bptr); Fprintf ("Mismatch of data pattern prefix: '%s'\n", prefix_string); /* expected */ dump_size = CalculateDumpSize (prefix_size); dump_buffer (prefix_str, (u_char *)prefix_string, pstr, dump_size, prefix_size, TRUE); /* received */ dump_size = CalculateDumpSize (bcount); dump_buffer (data_str, buffer, bptr, dump_size, bcount, FALSE); status = FAILURE; ExecuteTrigger(dip, "miscompare"); break; } } *pcount = count; return (status); } /************************************************************************ * * * verify_buffers() - Verify Data Buffers. * * * * Description: * * Simple verification of two data buffers. * * * * Inputs: dip = The device information pointer. * * dbuffer = Data buffer to verify with. * * vbuffer = Verification buffer to use. * * count = The number of bytes to compare. * * * * Outputs: Returns SUCCESS/FAILURE = Data Ok/Compare Error. * * * ************************************************************************/ int verify_buffers( struct dinfo *dip, u_char *dbuffer, u_char *vbuffer, size_t count ) { u_int32 i; u_char *dptr = dbuffer; u_char *vptr = vbuffer; for (i = 0; (i < count); i++, dptr++, vptr++) { if (*dptr != *vptr) { size_t dump_size = CalculateDumpSize (count); ReportCompareError (dip, count, i, *dptr, *vptr); /* expected */ dump_buffer (data_str, dbuffer, dptr, dump_size, count, TRUE); /* received */ dump_buffer (verify_str, vbuffer, vptr, dump_size, count, FALSE); ExecuteTrigger(dip, "miscompare"); return (FAILURE); } } return (SUCCESS); } int verify_lbdata( struct dinfo *dip, u_char *dbuffer, u_char *vbuffer, size_t count, u_int32 *lba ) { u_int32 i, dlbn = 0, vlbn; u_char *dptr = dbuffer; u_char *vptr = vbuffer; int status = SUCCESS; for (i = 0; (i+sizeof(dlbn) <= count); i += lbdata_size, dptr += lbdata_size, vptr += lbdata_size) { if (iot_pattern) { dlbn = get_lbn(dptr); vlbn = get_lbn(vptr); } else { dlbn = stoh (dptr, sizeof(dlbn)); vlbn = stoh (vptr, sizeof(vlbn)); } if (dlbn != vlbn) { size_t dump_size = CalculateDumpSize (count); ReportLbdataError(dip, *lba, count, i, dlbn, vlbn); /* expected */ dump_buffer (data_str, dbuffer, dptr, dump_size, count, TRUE); /* received */ dump_buffer (verify_str, vbuffer, vptr, dump_size, count, FALSE); status = FAILURE; break; } } *lba = (dlbn + 1); return (status); } /************************************************************************ * * * verify_data() - Verify Data Pattern. * * * * Description: * * If a pattern_buffer exists, then this data is used to compare * * the buffer instead of the pattern specified. * * * * Inputs: dip = The device information pointer. * * buffer = Pointer to data to verify. * * count = The number of bytes to compare. * * pattern = Data pattern to compare against. * * lba = Pointer to starting logical block address. * * * * Outputs: Returns SUCCESS/FAILURE = Data Ok/Compare Error. * * lba gets updated with the next lba to verify with. * * * ************************************************************************/ int verify_data ( struct dinfo *dip, u_char *buffer, size_t count, u_int32 pattern, u_int32 *lba ) { bool check_lba = (iot_pattern || (lbdata_flag && lbdata_size)); /* * I hate to duplicate code, but the smaller functions * optimize better and give *much* better performance. */ if ( !check_lba && !prefix_string ) { return ( verify_data_normal(dip, buffer, count, pattern) ); } else if ( !check_lba && prefix_string ) { return ( verify_data_prefix(dip, buffer, count, pattern) ); } else { return ( verify_data_with_lba(dip, buffer, count, pattern, lba) ); } } static int verify_data_normal( struct dinfo *dip, u_char *buffer, size_t bcount, u_int32 pattern ) { register size_t i = 0; register u_char *vptr = buffer; register u_char *pptr = pattern_bufptr; register u_char *pend = pattern_bufend; register size_t count = bcount; bool error = FALSE; int status = SUCCESS; while ( (i < count) ) { if (*vptr != *pptr) { error = TRUE; ReportCompareError (dip, count, i, *pptr, *vptr); break; } else { i++, pptr++, vptr++; if (pptr == pend) pptr = pattern_buffer; } } if (error) { if (dump_flag) { size_t dump_size = CalculateDumpSize (count); if (pattern_buffer) { size_t pdump_size = (dump_size < patbuf_size) ? dump_size : patbuf_size; /* expected */ dump_buffer (pattern_str, pattern_buffer, pptr, pdump_size, patbuf_size, TRUE); } /* received */ dump_buffer (data_str, buffer, vptr, dump_size, count, FALSE); } ExecuteTrigger(dip, "miscompare"); status = FAILURE; } pattern_bufptr = pptr; return (status); } static int verify_data_prefix( struct dinfo *dip, u_char *buffer, size_t bcount, u_int32 pattern ) { register size_t i = 0; register u_char *vptr = buffer; register u_char *pptr = pattern_bufptr; register u_char *pend = pattern_bufend; register size_t count = bcount; bool error = FALSE; int status = SUCCESS; while ( (i < count) ) { /* * Verify the prefix string (if any). */ if (prefix_string && (i % lbdata_size) == 0) { size_t pcount; status = verify_prefix (dip, vptr, (count - i), &pcount); if (status == FAILURE) return (status); i += pcount; vptr += pcount; continue; } if (*vptr != *pptr) { error = TRUE; ReportCompareError (dip, count, i, *pptr, *vptr); break; } else { i++, pptr++, vptr++; if (pptr == pend) pptr = pattern_buffer; } } if (error) { if (dump_flag) { size_t dump_size = CalculateDumpSize (count); if (pattern_buffer) { size_t pdump_size = (dump_size < patbuf_size) ? dump_size : patbuf_size; /* expected */ dump_buffer (pattern_str, pattern_buffer, pptr, pdump_size, patbuf_size, TRUE); } /* received */ dump_buffer (data_str, buffer, vptr, dump_size, count, FALSE); } ExecuteTrigger(dip, "miscompare"); status = FAILURE; } pattern_bufptr = pptr; return (status); } static int verify_data_with_lba( struct dinfo *dip, u_char *buffer, size_t bcount, u_int32 pattern, u_int32 *lba ) { register size_t i = 0; register u_char *vptr = buffer; register u_char *pptr = pattern_bufptr; register u_char *pend = pattern_bufend; register size_t count = bcount; register u_int32 lbn, vlbn = *lba; bool error = FALSE, lbn_error = FALSE; int status = SUCCESS; while ( (i < count) ) { /* * Handle IOT and Lbdata logical block checks first. */ if ( ((i % lbdata_size) == 0) ) { /* * Verify the prefix string prior to encoded lba's. */ if (prefix_string) { size_t pcount; status = verify_prefix (dip, vptr, (count - i), &pcount); if (status == FAILURE) return (status); vptr += pcount; if ( (i += pcount) == count) continue; } if ( (i+sizeof(lbn) <= count) ) { if (iot_pattern) { vlbn = get_lbn(pptr); lbn = get_lbn(vptr); } else { lbn = stoh (vptr, sizeof(lbn)); } if (lbn != vlbn) { error = lbn_error = TRUE; ReportLbdataError (dip, *lba, count, i, vlbn, lbn); break; } else { int size; vlbn++; i += sizeof(lbn); vptr += sizeof(lbn); /* Skip past pattern bytes, handling wrapping. */ size = sizeof(lbn); while (size--) { pptr++; if (pptr == pend) pptr = pattern_buffer; } } continue; } } if (*vptr != *pptr) { error = TRUE; ReportCompareError (dip, count, i, *pptr, *vptr); break; } else { i++, pptr++, vptr++; if (pptr == pend) pptr = pattern_buffer; } } if (error) { if (dump_flag) { size_t dump_size = CalculateDumpSize (count); if (lbn_error && !iot_pattern) { u_int32 elbn = vlbn; /* Can't take address of register. */ /* expected - yep, real ugly, but gotta be correct! */ dump_buffer (lba_str, (u_char *)&elbn, (u_char *)&elbn, sizeof(elbn), sizeof(elbn), TRUE); } else if (pattern_buffer) { size_t pdump_size = (dump_size < patbuf_size) ? dump_size : patbuf_size; /* expected */ dump_buffer (pattern_str, pattern_buffer, pptr, pdump_size, patbuf_size, TRUE); } /* received */ dump_buffer (data_str, buffer, vptr, dump_size, count, FALSE); } ExecuteTrigger(dip, "miscompare"); status = FAILURE; } pattern_bufptr = pptr; *lba = vlbn; /* Pass updated lba back to caller. */ return (status); } /************************************************************************ * * * verify_padbytes() - Verify Pad Bytes Consistency. * * * * Description: * * This function simply checks the pad bytes to ensure they * * haven't been overwritten after a read operation. * * * * Inputs: dip = The device information pointer. * * buffer = Pointer to start of pad buffer. * * count = The last record read byte count. * * pattern = Data pattern to compare against. * * offset = Offset to where pad bytes start. * * * * Outputs: Returns SUCCESS/FAILURE = Data Ok/Compare Error. * * * ************************************************************************/ int verify_padbytes ( struct dinfo *dip, u_char *buffer, size_t count, u_int32 pattern, size_t offset ) { size_t pbytes, pindex; int status; /* * For short reads, checks inverted data bytes & pad bytes. */ if ( (offset != count) && spad_check) { size_t resid = (offset - count); pindex = (count & (sizeof(u_int32) - 1)); pbytes = (resid < PADBUFR_SIZE) ? resid : PADBUFR_SIZE; status = dopad_verify (dip, buffer, count, pattern, pbytes, pindex, TRUE); if (status == FAILURE) return (status); } pindex = 0; pbytes = PADBUFR_SIZE; return (dopad_verify (dip, buffer, offset, pattern, pbytes, pindex, FALSE)); } static int dopad_verify ( struct dinfo *dip, u_char *buffer, size_t offset, u_int32 pattern, size_t pbytes, size_t pindex, bool inverted ) { int status = SUCCESS; u_char *vptr; size_t i; union { u_char pat[sizeof(u_int32)]; u_int32 pattern; } p; p.pattern = pattern; vptr = buffer + offset; /* * NOTE: We could be comparing inverted data on short reads. */ for (i = pindex; i < (pbytes + pindex); i++, vptr++) { if (*vptr != p.pat[i & (sizeof(u_int32) - 1)]) { (void) RecordError(); Fprintf ( "Data compare error at %s byte %u in record number %lu\n", (inverted) ? "inverted" : "pad", (inverted) ? (offset + i) : i, (dip->di_records_read + 1)); ReportDeviceInfo (dip, offset, i, FALSE); Fprintf ("Data expected = %#x, data found = %#x, pattern = 0x%08x\n", p.pat[i & (sizeof(u_int32) - 1)], *vptr, pattern); if (dump_flag) { /* * Limit data dumped for short corrupted records. */ size_t dump_size = CalculateDumpSize (offset); dump_buffer (data_str, buffer, vptr, dump_size, data_size, FALSE); } else { Fprintf ("Data buffer pointer = %#lx, buffer offset = %ld\n", vptr, offset); } fflush (efp); status = FAILURE; ExecuteTrigger(dip, "miscompare"); break; } } return (status); } /************************************************************************ * * * process_pfile() - Process a pattern file. * * * * Inputs: fd = Pointer to file descriptor. * * file = Pointer to pattern file name. * * mode = The mode (read/write) for open. * * * * Outputs: Returns on success, exits on open failure. * * * * Return Value: * * Void. * * * ************************************************************************/ void process_pfile (int *fd, char *file, int mode) { struct stat sb; size_t count, size; u_char *buffer; #if defined(__WIN32__) mode |= O_BINARY; #endif /* defined(__WIN32__) */ if ( (*fd = open (file, mode)) == FAILURE) { Fprintf ("Error opening pattern file '%s', mode = %o\n", file, mode); report_error ("process_pfile", TRUE); exit (exit_status); } if (fstat (*fd, &sb) < 0) { report_error ("fstat", TRUE); exit (exit_status); } /* * Only support regular files at this time. */ if ((sb.st_mode & S_IFMT) != S_IFREG) { Fprintf ("Expect regular file for pattern file.\n"); exit (exit_status); } size = (size_t) sb.st_size; buffer = (u_char *) myalloc (size, 0); if ( (count = read (*fd, buffer, size)) != size) { Fprintf ("Pattern file '%s' read error!\n", file); if ((ssize_t)count == FAILURE) { report_error ("read", TRUE); exit (exit_status); } else { LogMsg (efp, logLevelCrit, 0, "Attempted to read %d bytes, read only %d bytes.", size, count); exit (FATAL_ERROR); } } patbuf_size = size; setup_pattern (buffer, size); /* * Currently don't need pattern file open for anything else. */ (void) close (*fd); *fd = -1; } /************************************************************************ * * * setup_pattern() - Setup pattern variables. * * * * Inputs: buffer = Pointer to pattern buffer. * * size = Size of pattern buffer. * * * * Outputs: Returns on success, exits on open failure. * * * * Return Value: * * Void. * * * ************************************************************************/ void setup_pattern (u_char *buffer, size_t size) { patbuf_size = size; pattern_buffer = buffer; pattern_bufptr = buffer; pattern_bufend = buffer + size; pattern = (u_int32) 0; switch (size) { case sizeof(u_char): pattern = (u_int32) buffer[0]; break; case sizeof(u_short): pattern = ( ((u_int32)buffer[1] << 8) | (u_int32)buffer[0] ); break; case 0x03: pattern = ( ((u_int32)buffer[2] << 16) | ((u_int32)buffer[1] << 8) | (u_int32) buffer[0] ); break; default: pattern = ( ((u_int32)buffer[3] << 24) | ((u_int32)buffer[2] << 16) | ((u_int32)buffer[1] << 8) | (u_int32)buffer[0]); break; } } /* * Copy pattern bytes to pattern buffer with proper byte ordering. */ void copy_pattern (u_int32 pattern, u_char *buffer) { buffer[0] = (u_char) pattern; buffer[1] = (u_char) (pattern >> 8); buffer[2] = (u_char) (pattern >> 16); buffer[3] = (u_char) (pattern >> 24); } /* * Function to display ASCII time. */ void print_time (FILE *fp, clock_t time) { u_int hr, min, sec, frac; frac = time % hz; frac = (frac * 100) / hz; time /= hz; sec = time % 60; time /= 60; min = time % 60; if (hr = time / 60) { fprintf (fp, "%dh", hr); } fprintf (fp, "%02dm", min); fprintf (fp, "%02d.", sec); fprintf (fp, "%02ds\n", frac); } void format_time (clock_t time) { clock_t hr, min, sec, frac; frac = (time % hz); frac = (frac * 100) / hz; time /= hz; sec = time % 60; time /= 60; min = time % 60; if (hr = time / 60) { Lprintf ("%dh", hr); } Lprintf ("%02dm", min); Lprintf ("%02d.", sec); Lprintf ("%02ds\n", frac); } #if defined(DEC) /* * Format table() sysinfo time. */ void format_ltime (long time, int tps) { long hr, min, sec, frac; /*Lprintf ("%.3f - ", (float)((float)time/(float)tps));*/ frac = (time % tps); frac = (frac * 1000) / tps; time /= tps; sec = time % 60; time /= 60; min = time % 60; if (hr = time / 60) { Lprintf ("%dh", hr); } Lprintf ("%02dm", min); Lprintf ("%02d.", sec); Lprintf ("%03ds\n", frac); } #endif /* defined(DEC) */ /************************************************************************ * * * seek_file() Seeks to the specified file offset. * * * * Inputs: fd = The file descriptor. * * records = The number of records. * * size = The size of each record. * * whence = The method of setting position: * * * * SEEK_SET (L_SET) = Set to offset bytes. * * SEEK_CUR (L_INCR) = Increment by offset bytes. * * SEEK_END (L_XTND) = Extend by offset bytes. * * * * offset = (record count * size of each record) * * * * Return Value: * * Returns file position on Success, (off_t)-1 on Failure. * * * ************************************************************************/ off_t seek_file (int fd, u_long records, off_t size, int whence) { off_t pos; /* * Seek to specifed file position. */ if ((pos = lseek (fd, (off_t)(records * size), whence)) == (off_t)-1) { Fprintf("lseek failed (fd %d, offset " FUF ", whence %d)\n", fd, (off_t)(records * size), whence); report_error ("lseek", TRUE); } return (pos); } /* * Utility functions to handle file positioning. */ off_t seek_position (struct dinfo *dip, off_t offset, int whence) { off_t pos; #if defined(DEBUG) if (Debug_flag) { Printf ("attempting lseek (fd=%d, offset=" FUF ", whence=%d)\n", dip->di_fd, offset, whence); } #endif /* defined(DEBUG) */ /* * Seek to specifed file position. */ if ((pos = lseek (dip->di_fd, offset, whence)) == (off_t) -1) { Fprintf("lseek failed (fd %d, offset " FUF ", whence %d)\n", dip->di_fd, offset, whence); report_error ("lseek", TRUE); terminate (exit_status); } #if defined(DEBUG) if (Debug_flag) { Printf ("pos=" FUF " = lseek (fd=%d, offset=%lu, whence=%d)\n", pos, dip->di_fd, offset, whence); } #endif /* defined(DEBUG) */ return (pos); } #if !defined(INLINE_FUNCS) off_t get_position (struct dinfo *dip) { #if defined(_BSD) return (seek_position (dip, (off_t) 0, L_INCR)); #else /* !defined(_BSD) */ return (seek_position (dip, (off_t) 0, SEEK_CUR)); #endif /* defined(_BSD) */ } #endif /* !defined(INLINE_FUNCS) */ u_int32 get_lba (struct dinfo *dip) { off_t pos; if ( (pos = get_position(dip)) ) { return ( (u_int32)(pos / lbdata_size) ); } else { return ( (u_int32) 0 ); } } off_t incr_position (struct dinfo *dip, off_t offset) { #if defined(_BSD) off_t pos = seek_position (dip, offset, L_INCR); #else /* !defined(_BSD) */ off_t pos = seek_position (dip, offset, SEEK_CUR); #endif /* defined(_BSD) */ if (Debug_flag || rDebugFlag) { large_t lba = (pos / (off_t)dip->di_dsize); Printf ("Seeked to block " LUF " (" LXF ") at byte position " FUF "\n", lba, lba, pos); } return (pos); } off_t set_position (struct dinfo *dip, off_t offset) { #if defined(_BSD) off_t pos = seek_position (dip, offset, L_SET); #else /* !defined(_BSD) */ off_t pos = seek_position (dip, offset, SEEK_SET); #endif /* defined(_BSD) */ if (Debug_flag || rDebugFlag) { large_t lba = (pos / (off_t)dip->di_dsize); Printf ("Seeked to block " LUF " (" LXF ") at byte position " FUF "\n", lba, lba, pos); } return (pos); } #if !defined(INLINE_FUNCS) off_t make_position(struct dinfo *dip, u_int32 lba) { return ( (off_t)(lba * lbdata_size)); } #endif /* !defined(INLINE_FUNCS) */ void show_position (struct dinfo *dip, off_t pos) { if (debug_flag || rDebugFlag) { large_t lba = make_lba(dip, pos); Printf ("Current file offset is " FUF " (" FXF "), relative lba is %u (%#x)\n", pos, pos, lba, lba); } } u_long get_random(void) { /* * These first 2 functions generate a number in range (0 - 2^31). */ #if defined(RAND48) return ( (u_long)lrand48() ); #elif defined(RANDOM) return ( (u_long)random() ); #else /* Use ANSI rand() below... */ /* * ANSI specifies rand() returns value in range (0 - 32767). * NOTE: BSD rand() returns value in range (0 - 2^31) also. */ return ( (u_long)rand() ); #endif /* defined(RAND28) */ } /* * Function to calculate variable length request size. */ size_t get_variable (struct dinfo *dip) { size_t length; u_long randum; randum = get_random(); length = (size_t)((randum % max_size) + min_size); if (dip->di_dsize) { if ( (dip->di_dtype->dt_dtype != DT_REGULAR) || ((dip->di_dtype->dt_dtype == DT_REGULAR) && fsalign_flag) ) { length = roundup(length, dip->di_dsize); } } if (length > max_size) length = max_size; return (length); } /* * Function to set random number generator seed. */ void set_rseed(u_int seed) { #if defined(RAND48) /* System V */ srand48 ((long) seed); #elif defined(RANDOM) /* BSD? */ srandom (seed); #else /* ANSI */ srand (seed); #endif } /* * Function to set position for random I/O. */ off_t do_random (struct dinfo *dip, bool doseek, size_t xfer_size) { off_t pos, dsize, ralign; large_t randum; u_long records; dsize = (off_t)(dip->di_dsize); ralign = (off_t)((random_align) ? random_align : dsize); if (dip->di_mode == READ_MODE) { records = dip->di_records_read; } else { records = dip->di_records_written; } /* * Ensure the random alignment size is modulo the device size. */ #if 0 if ( (dip->di_dtype->dt_dtype != DT_REGULAR) || ((dip->di_dtype->dt_dtype == DT_REGULAR) && fsalign_flag) ) { if ( (ralign % dsize) != 0) { ralign = roundup(ralign, dsize); } } #else /* revert to previous release (for now) */ if ( (ralign % dsize) != 0) { ralign = roundup(ralign, dsize); } #endif /* 0 */ randum = (large_t)get_random(); /* * Set position so that the I/O is in the range from file_position to * data_limit and is block size aligned. * * Since randum only ranges to 2^31, treat it as a block number to * allow random access to more blocks in a large device or file. * * Note: This scaling/rounding/range checking kills performance! */ if (records & 0x01) { randum *= dsize; /* Scale upwards every other record. */ } pos = (off_t)(randum % rdata_limit); /* * Ensure the upper data limit isn't exceeded. */ while ((pos + xfer_size) >= rdata_limit) { pos = ( (pos + xfer_size) % rdata_limit); } /* * Round up and align as necessary. */ #if 0 if ( (dip->di_dtype->dt_dtype != DT_REGULAR) || ((dip->di_dtype->dt_dtype == DT_REGULAR) && fsalign_flag) ) { pos = roundup(pos, ralign); } #else /* revert to previous release (for now) */ pos = roundup(pos, ralign); #endif /* 0 */ /* * Ensure the position is within random range. */ if (pos < file_position) { if ((pos + file_position + xfer_size) >= rdata_limit) { pos = (file_position + ((pos + xfer_size) / dsize)); } else { pos += file_position; } /* Round up and adjust back down as necessary. (yea, ugly :-) */ while ( ((pos = roundup(pos, ralign)) + xfer_size) >= rdata_limit) { pos -= (ralign + dsize); } if (pos < file_position) pos = file_position; /* too low! */ } if (doseek) { return (set_position (dip, pos)); } else { /* * Note: For AIO, we just calculate the random position. */ if (Debug_flag || rDebugFlag) { large_t lba = (pos / dsize); Printf ("Random position set to " FUF " block " LUF " (" LXF ").\n", pos, lba, lba); } return (pos); } } /************************************************************************ * * * skip_records() Skip past specified number of records. * * * * Inputs: dip = The device info pointer. * * records = The number of records. * * buffer = The buffer to read into. * * size = The size of each record. * * * * Return Value: * * Returns SUCCESS/FAILURE/WARNING = Ok/Read Failed/Partial Read. * * * ************************************************************************/ int skip_records ( struct dinfo *dip, u_long records, u_char *buffer, off_t size) { u_long i; size_t count; int status = SUCCESS; /* * Skip over the specified record(s). */ for (i = 0; i < records; i++) { count = read (dip->di_fd, buffer, size); if ( (status = check_read (dip, count, size)) == FAILURE) { break; } } return (status); } /************************************************************************ * * * myalloc() Allocate aligned buffer at specified offset. * * * * Description: This function first gets a page aligned buffer using * * malloc(), then it returns a pointer offset bytes into * * the page. This was done to test page alignment code * * in device drivers. * * * * Inputs: size = The size of the buffer to allocate. * * offset = The offset into the page. * * * * Outputs: Returns the allocated buffer or 0 for failure. * * * ************************************************************************/ void * myalloc (size_t size, int offset) { u_char *bp; #if defined(_BSD) if ( (bp = (u_char *) valloc (size + offset)) == (u_char *) 0) { LogMsg (efp, logLevelCrit, 0, "valloc() failed allocating %lu bytes.\n", (size + offset)); exit (FATAL_ERROR); } #elif defined(_QNX_SOURCE) && !defined(_QNX_32BIT) if ( (bp = (u_char *) malloc (size + offset)) == (u_char *) 0) { LogMsg (efp, logLevelCrit, 0, "malloc() failed allocating %u bytes.\n", (size + offset)); exit (FATAL_ERROR); } #else /* !defined_BSD) || !defined(_QNX_SOURCE) */ if ( (bp = (u_char *) malloc (size + offset + page_size)) == (u_char *) 0) { LogMsg (efp, logLevelCrit, 0, "malloc() failed allocating %lu bytes.\n", (size + offset + page_size)); exit (FATAL_ERROR); } else { bp = (u_char *)( ((ptr_t)bp + (page_size-1)) &~ (page_size-1) ); } #endif /* defined(_BSD) */ bp += offset; if (debug_flag) { Printf ( "Allocated buffer at address %#lx of %u bytes, using offset %u\n", bp, size, offset); } return (bp); } /* * Allocate memory with appropriate error checking. */ void * Malloc (size_t size) { void *bp; if ( (bp = malloc (size)) == NULL) { LogMsg (efp, logLevelCrit, 0, "malloc() failed allocating %u bytes.\n", size); exit (ENOMEM); } memset (bp, '\0', size); return (bp); } /************************************************************************ * * * CvtStrtoValue() - Converts ASCII String into Numeric Value. * * * * Inputs: nstr = String to convert. * * eptr = Pointer for terminating character pointer. * * base = The base used for the conversion. * * * * Outputs: eptr = Points to terminating character or nstr if an * * invalid if numeric value cannot be formed. * * * * Return Value: * * Returns converted number or -1 for FAILURE. * * * ************************************************************************/ u_long CvtStrtoValue (char *nstr, char **eptr, int base) { u_long n = 0, val; if ( (n = strtoul (nstr, eptr, base)) == 0L) { if (nstr == *eptr) { n++; } } #ifdef notdef if (nstr == *eptr) { return (n); } #endif /* notdef */ nstr = *eptr; for (;;) { switch (*nstr++) { case 'k': case 'K': /* Kilobytes */ n *= KBYTE_SIZE; continue; case 'g': case 'G': /* Gigibytes */ n *= GBYTE_SIZE; continue; case 'm': case 'M': /* Megabytes */ n *= MBYTE_SIZE; continue; #if defined(QuadIsLong) case 't': case 'T': n *= TBYTE_SIZE; continue; #endif /* defined(QuadIsLong) */ case 'w': case 'W': /* Word count. */ n *= sizeof(int); continue; case 'q': case 'Q': /* Quadword count. */ n *= sizeof(large_t); continue; case 'b': case 'B': /* Block count. */ n *= BLOCK_SIZE; continue; case 'd': case 'D': /* Device size. */ n *= device_size; continue; case 'c': case 'C': /* Core clicks. */ case 'p': case 'P': /* Page size. */ n *= page_size; continue; case 'i': case 'I': if ( ( ( nstr[0] == 'N' ) || ( nstr[0] == 'n' ) ) && ( ( nstr[1] == 'F' ) || ( nstr[1] == 'f' ) )) { nstr += 2; n = (u_long)INFINITY; continue; } else { goto error; } case '+': n += CvtStrtoValue (nstr, eptr, base); nstr = *eptr; continue; case '-': n -= CvtStrtoValue (nstr, eptr, base); nstr = *eptr; continue; case '*': case 'x': case 'X': n *= CvtStrtoValue (nstr, eptr, base); nstr = *eptr; continue; case '/': val = CvtStrtoValue (nstr, eptr, base); if (val) n /= val; nstr = *eptr; continue; case '%': val = CvtStrtoValue (nstr, eptr, base); if (val) n %= val; nstr = *eptr; continue; case '~': n = ~CvtStrtoValue (nstr, eptr, base); nstr = *eptr; continue; case '|': n |= CvtStrtoValue (nstr, eptr, base); nstr = *eptr; continue; case '&': n &= CvtStrtoValue (nstr, eptr, base); nstr = *eptr; continue; case '^': n ^= CvtStrtoValue (nstr, eptr, base); nstr = *eptr; continue; case '<': if (*nstr++ != '<') goto error; n <<= CvtStrtoValue (nstr, eptr, base); nstr = *eptr; continue; case '>': if (*nstr++ != '>') goto error; n >>= CvtStrtoValue (nstr, eptr, base); nstr = *eptr; continue; case ' ': case '\t': continue; case '\0': *eptr = --nstr; break; default: error: n = 0L; *eptr = --nstr; break; } return (n); } } /************************************************************************ * * * CvtStrtoLarge() - Converts ASCII String into Large Value. * * * * Inputs: nstr = String to convert. * * eptr = Pointer for terminating character pointer. * * base = The base used for the conversion. * * * * Outputs: eptr = Points to terminating character or nstr if an * * invalid if numeric value cannot be formed. * * * * Return Value: * * Returns converted number or -1 for FAILURE. * * * ************************************************************************/ large_t CvtStrtoLarge (char *nstr, char **eptr, int base) { large_t n = 0, val; #if defined(QuadIsLong) || defined(HP_UX) if ( (n = strtoul (nstr, eptr, base)) == (large_t) 0) { #elif defined(QuadIsLongLong) # if defined(SCO) || defined(__QNXNTO__) || defined(SOLARIS) || defined(AIX) || defined(_NT_SOURCE) if ( (n = strtoull (nstr, eptr, base)) == (large_t) 0) { # else /* !defined(SCO) && !defined(__QNXNTO__) && !defined(AIX) && !defined(_NT_SOURCE) */ if ( (n = strtouq (nstr, eptr, base)) == (large_t) 0) { # endif /* defined(SCO) || defined(__QNXNTO__) || defined(SOLARIS) || defined(AIX) || defined(_NT_SOURCE) */ #else /* assume QuadIsDouble */ if ( (n = strtod (nstr, eptr)) == (large_t) 0) { #endif if (nstr == *eptr) { n++; } } #ifdef notdef if (nstr == *eptr) { return (n); } #endif /* notdef */ nstr = *eptr; for (;;) { switch (*nstr++) { case 'k': case 'K': /* Kilobytes */ n *= KBYTE_SIZE; continue; case 'g': case 'G': /* Gigibytes */ n *= GBYTE_SIZE; continue; case 'm': case 'M': /* Megabytes */ n *= MBYTE_SIZE; continue; case 't': case 'T': n *= TBYTE_SIZE; continue; case 'w': case 'W': /* Word count. */ n *= sizeof(int); continue; case 'q': case 'Q': /* Quadword count. */ n *= sizeof(large_t); continue; case 'b': case 'B': /* Block count. */ n *= BLOCK_SIZE; continue; case 'd': case 'D': /* Device size. */ n *= device_size; continue; case 'c': case 'C': /* Core clicks. */ case 'p': case 'P': /* Page size. */ n *= page_size; continue; case 'i': case 'I': if ( ( ( nstr[0] == 'N' ) || ( nstr[0] == 'n' ) ) && ( ( nstr[1] == 'F' ) || ( nstr[1] == 'f' ) )) { nstr += 2; n = INFINITY; continue; } else { goto error; } case '+': n += CvtStrtoLarge (nstr, eptr, base); nstr = *eptr; continue; case '-': n -= CvtStrtoLarge (nstr, eptr, base); nstr = *eptr; continue; case '*': case 'x': case 'X': n *= CvtStrtoLarge (nstr, eptr, base); nstr = *eptr; continue; case '/': val = CvtStrtoLarge (nstr, eptr, base); if (val) n /= val; nstr = *eptr; continue; #if !defined(QuadIsDouble) case '%': val = CvtStrtoLarge (nstr, eptr, base); if (val) n %= val; nstr = *eptr; continue; case '~': n = ~CvtStrtoLarge (nstr, eptr, base); nstr = *eptr; continue; case '|': n |= CvtStrtoLarge (nstr, eptr, base); nstr = *eptr; continue; case '&': n &= CvtStrtoLarge (nstr, eptr, base); nstr = *eptr; continue; case '^': n ^= CvtStrtoLarge (nstr, eptr, base); nstr = *eptr; continue; case '<': if (*nstr++ != '<') goto error; n <<= CvtStrtoLarge (nstr, eptr, base); nstr = *eptr; continue; case '>': if (*nstr++ != '>') goto error; n >>= CvtStrtoLarge (nstr, eptr, base); nstr = *eptr; continue; #endif /* !defined(QuadIsDouble) */ case ' ': case '\t': continue; case '\0': *eptr = --nstr; break; default: error: n = 0; *eptr = --nstr; break; } return (n); } } /************************************************************************ * * * CvtTimetoValue() - Converts ASCII Time String to Numeric Value. * * * * Inputs: nstr = String to convert. * * eptr = Pointer for terminating character pointer. * * * * Outputs: eptr = Points to terminating character or nstr if an * * invalid if numeric value cannot be formed. * * * * Return Value: * * Returns converted number in seconds or -1 for FAILURE. * * * ************************************************************************/ time_t CvtTimetoValue (char *nstr, char **eptr) { time_t n = 0; int base = ANY_RADIX; if ( (n = strtoul (nstr, eptr, base)) == 0L) { if (nstr == *eptr) { n++; } } #ifdef notdef if (nstr == *eptr) { return (n); } #endif /* notdef */ nstr = *eptr; for (;;) { switch (*nstr++) { case 'd': case 'D': /* Days */ n *= SECS_PER_DAY; continue; case 'h': case 'H': /* Hours */ n *= SECS_PER_HOUR; continue; case 'm': case 'M': /* Minutes */ n *= SECS_PER_MIN; continue; case 's': case 'S': /* Seconds */ continue; /* default */ case '+': n += CvtTimetoValue (nstr, eptr); nstr = *eptr; continue; case '-': n -= CvtTimetoValue (nstr, eptr); nstr = *eptr; continue; case '*': case 'x': case 'X': n *= CvtTimetoValue (nstr, eptr); nstr = *eptr; continue; case '/': n /= CvtTimetoValue (nstr, eptr); nstr = *eptr; continue; case '%': n %= CvtTimetoValue (nstr, eptr); nstr = *eptr; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': nstr--; n += CvtTimetoValue (nstr, eptr); nstr = *eptr; continue; case ' ': case '\t': continue; case '\0': *eptr = --nstr; break; default: n = 0L; *eptr = --nstr; break; } return (n); } } #if defined(ultrix) /* * This was added since the ULTRIX version returns rounded up whole * numbers instead of the scaled clock ticks. I want the resolution. */ /* * times() for X/Open/POSIX compatability. * * Returns the number of (CLK_TCK)ths of a second since the epoch. * The value may wrap. */ static int scale (struct timeval *tvp) { /* * convert seconds and microseconds to CLK_TCKs */ return ((tvp->tv_sec * CLK_TCK) + ((tvp->tv_usec * CLK_TCK) / 1000000)); } clock_t times (struct tms *tmsp) { struct rusage ru; struct timeval tp; if (getrusage(RUSAGE_SELF, &ru) < 0) return (-1); tmsp->tms_utime = scale(&ru.ru_utime); tmsp->tms_stime = scale(&ru.ru_stime); if (getrusage(RUSAGE_CHILDREN, &ru) < 0) return ((clock_t) -1); tmsp->tms_cutime = scale(&ru.ru_utime); tmsp->tms_cstime = scale(&ru.ru_stime); if (gettimeofday(&tp, (struct timezone *)0) < 0) return((clock_t) -1); return((clock_t) scale(&tp)); } #endif /* defined(ultrix) */ /* * Format & append time string to log file buffer. */ /*VARARGS*/ void Ctime (time_t timer) { char *buf; char *bp = log_bufptr; buf = ctime(&timer); (void) strcpy (bp, buf); bp += strlen(bp); log_bufptr = bp; } /* * Record current time of when error occured. */ u_long RecordError(void) { error_time = time((time_t *) 0); /* ctime() adds newline '\n' to time stamp. */ LogMsg (efp, logLevelError, PRT_NOIDENT, "\n"); LogMsg (efp, logLevelCrit, 0, "Error number %lu occurred on %s", ++error_count, ctime(&error_time)); end_time = times (&etimes); Fprintf ("Elapsed time since beginning of pass: "); print_time (efp, end_time - pass_time); Fprintf ("Elapsed time since beginning of test: "); print_time (efp, end_time - start_time); exit_status = FAILURE; return (error_count); } /* * Record current time & record count of when warning occur. */ u_long RecordWarning(u_long record) { error_time = time((time_t *) 0); Printf ("Warning on record number %lu, occurred at %s", (record + 1), ctime(&error_time)); end_time = times (&etimes); Printf ("Elapsed time since beginning of pass: "); print_time (ofp, end_time - pass_time); Printf ("Elapsed time since beginning of test: "); print_time (ofp, end_time - start_time); return (warning_errors); } /* * Display failure message to stderr & flush output. */ /*VARARGS*/ void LogMsg (FILE *fp, enum logLevel level, int flags, char *fmtstr, ...) { va_list ap; if (hazard_flag && !(flags & PRT_NOLEVEL)) { fprintf (fp, "RPCLOG%d: ", level); } if ( !(flags & PRT_NOIDENT) ) { if ( ((num_procs || num_slices) && !child_pid) || forked_flag) { fprintf (fp, "%s (%d): ", cmdname, getpid()); } else { fprintf (fp, "%s: ", cmdname); } } va_start(ap, fmtstr); vfprintf (fp, fmtstr, ap); va_end(ap); if ( !(flags & PRT_NOFLUSH) ) { (void) fflush (fp); } return; } void Fprintf (char *format, ...) { va_list ap; FILE *fp = efp; if (hazard_flag) { fprintf (fp, "RPCLOG%d: ", logLevelError); } if ( ((num_procs || num_slices) && !child_pid) || forked_flag) { fprintf (fp, "%s (%d): ", cmdname, getpid()); } else { fprintf (fp, "%s: ", cmdname); } va_start(ap, format); vfprintf (fp, format, ap); va_end(ap); (void) fflush (fp); } /* * Same as Fprintf except no identifier or log prefix. */ /*VARARGS*/ void Fprint (char *format, ...) { va_list ap; FILE *fp = efp; va_start(ap, format); vfprintf (fp, format, ap); va_end(ap); } /* * Format & append string to log file buffer. */ /*VARARGS*/ void Lprintf (char *format, ...) { va_list ap; char *bp = log_bufptr; va_start(ap, format); vsprintf (bp, format, ap); va_end(ap); bp += strlen(bp); log_bufptr = bp; } /* * Flush the log buffer and reset the buffer pointer. */ void Lflush(void) { FILE *fp = ofp; Fputs (log_buffer, fp); fflush (fp); log_bufptr = log_buffer; } /* * Display failure message to stdout & flush output. */ /*VARARGS*/ void Printf (char *format, ...) { va_list ap; FILE *fp = ofp; if (hazard_flag) { fprintf (fp, "RPCLOG%d: ", logLevelLog); } if ( ((num_procs || num_slices) && !child_pid) || forked_flag) { fprintf (fp, "%s (%d): ", cmdname, getpid()); } else { fprintf (fp, "%s: ", cmdname); } va_start(ap, format); vfprintf (fp, format, ap); va_end(ap); (void) fflush (fp); } /* * Same as Printf except no program name identifier. */ /*VARARGS*/ void Print (char *format, ...) { va_list ap; FILE *fp = ofp; if (hazard_flag) { fprintf (fp, "RPCLOG%d: ", logLevelLog); } va_start(ap, format); vfprintf (fp, format, ap); va_end(ap); } /*VARARGS*/ int Sprintf(char *bufptr, char *msg, ...) { va_list ap; va_start(ap, msg); (void) vsprintf (bufptr, msg, ap); va_end(ap); return (strlen (bufptr)); } int vSprintf(char *bufptr, const char *msg, va_list ap) { (void) vsprintf(bufptr, msg, ap); return (strlen (bufptr)); } /************************************************************************ * * * Fputs() Common function to Write String to an Output Stream. * * * * Inputs: str = The string buffer pointer. * * stream = The file stream to access. * * * * Return Value: * * Returns 0 / -1 = SUCCESS / FAILURE. * * * ************************************************************************/ int Fputs (char *str, FILE *stream) { int status = SUCCESS; (void) fputs ((char *) str, stream); if (ferror (stream) != 0) { clearerr (stream); status = FAILURE; } return (status); } #if defined(_QNX_SOURCEx) || defined(SOLARIS) /* * TODO: Could be made a macro for better performance. */ void bzero (char *buffer, size_t length) { memset ((void *)buffer, '\0', length); } #endif /* defined(_QNX_SOURCE) || defined(SOLARIS) */ /************************************************************************ * * * is_Eof() - Check For End Of File Condition. * * * * Description: * * Detect end of file or end of media. Here's the presumptions: * * * * For Writes, we expect a errno (count == -1) and (errno == ENOSPC). * * For Reads, a (count == 0) indicates end of file, while a * * (count == -1) and (errno == ENOSPC) indicates end of medium. * * * * Actually, two file marks normally indicates the end of logical * * tape, while (errno == ENOSPC) normally indicates reading past all of * * the recorded data. Note, some tapes (QIC) only have one file mark. * * * * Is this confusing or what? I'm doing the best I can here :-) * * * * Inputs: dip = The device information pointer. * * count = The count from the last I/O request. * * status = Optional pointer to status variable. * * * * Return Value: * * Returns TRUE / FALSE = End of file / Not Eof. * * * ************************************************************************/ int is_Eof (struct dinfo *dip, size_t count, int *status) { /* * We expect writes @ EOF to fail w/count -1, and errno ENOSPC. */ if ( (dip->di_mode == WRITE_MODE) && (count == (size_t) 0) ) { #if defined(BrokenEOF) dip->di_end_of_file = TRUE; exit_status = END_OF_FILE; return (end_of_file = TRUE); #else /* !defined(BrokenEOF) */ return (FALSE); #endif /* defined(BrokenEOF) */ } #if defined(SCO) || defined(HP_UX) || defined(AIX) if ( (count == (size_t) 0) || ( ((ssize_t)count == (ssize_t) -1) && ((errno == ENOSPC) || (errno == ENXIO)) ) ) { #else /* !defined(SCO) && !defined(HP_UX) && !defined(AIX) */ if ( (count == (size_t) 0) || (((ssize_t)count == (ssize_t) -1) && (errno == ENOSPC)) ) { #endif /* defined(SCO) || defined(HP_UX) || defined(AIX) */ large_t data_bytes; if (dip->di_mode == READ_MODE) { data_bytes = dip->di_dbytes_read; } else { data_bytes = dip->di_dbytes_written; } if (debug_flag || eDebugFlag) { Printf ("End of %s detected, count = %d, errno = %d [file #%lu, record #%lu]\n", (count == 0) ? "file" : "media", count, errno, (dip->di_mode == READ_MODE) ? (dip->di_files_read + 1) : (dip->di_files_written + 1), (dip->di_mode == READ_MODE) ? (dip->di_records_read + 1) : (dip->di_records_written + 1)); } if (dip->di_dtype->dt_dtype == DT_TAPE) { if (count == (size_t) 0) { /* Two file mark's == end of logical tape. */ if (dip->di_end_of_file) dip->di_end_of_logical = TRUE; if (dip->di_end_of_logical) dip->di_end_of_media = TRUE; } else { /* ENOSPC */ /* Remember, QIC tapes only have one file mark! */ dip->di_end_of_logical = TRUE; dip->di_end_of_media = TRUE; } } #if defined(SCO) || defined(HP_UX) if ( ( ((ssize_t)count == (ssize_t) -1) && ((errno == ENOSPC) || (errno == ENXIO)) ) && (data_bytes == (large_t ) 0) ) { /* This is the key... */ #else /* !defined(SCO) && !defined(HP_UX) */ if ( (((ssize_t)count == (ssize_t) -1) && (errno == ENOSPC)) && (data_bytes == (large_t ) 0) ) { /* This is the key... */ #endif /* defined(SCO) || defined(HP_UX) */ exit_status = errno; report_error((dip->di_mode == READ_MODE) ? "read" : "write", TRUE); #if defined(EEI) if ( (dip->di_dtype->dt_dtype == DT_TAPE) && eei_flag) { print_mtstatus (dip->di_fd, dip->di_mt, TRUE); } #endif /* defined(EEI) */ if (status) *status = FAILURE; } else { exit_status = END_OF_FILE; } #if defined(EEI) if ((dip->di_dtype->dt_dtype == DT_TAPE) && eei_flag) { clear_eei_status(dip->di_fd, FALSE); } #endif /* defined(EEI) */ dip->di_end_of_file = TRUE; return (end_of_file = TRUE); } return (FALSE); } /* * Used to mimic EOF @ BOM when direction is reverse. */ void set_Eof(struct dinfo *dip) { if (debug_flag || eDebugFlag) { Printf ("Beginning of media detected [file #%lu, record #%lu]\n", (dip->di_mode == READ_MODE) ? (dip->di_files_read + 1) : (dip->di_files_written + 1), (dip->di_mode == READ_MODE) ? dip->di_records_read : dip->di_records_written); } end_of_file = TRUE; exit_status = END_OF_FILE; dip->di_end_of_file = TRUE; return; } void ReportCompareError ( struct dinfo *dip, size_t byte_count, u_int byte_position, u_int expected_data, u_int data_found) { (void) RecordError(); if (dip->di_dtype->dt_dtype == DT_TAPE) { Fprintf ("File #%lu, %s %u in record number %lu\n", (dip->di_files_read + 1), compare_error_str, byte_position, (dip->di_records_read + 1)); } else { Fprintf ("%s %u in record number %lu\n", compare_error_str, byte_position, (dip->di_records_read + 1)); } ReportDeviceInfo (dip, byte_count, byte_position, FALSE); Fprintf ("Data expected = %#x, data found = %#x, byte count = %lu\n", expected_data, data_found, byte_count); #if defined(LOG_DIAG_INFO) if (logdiag_flag) { char *bp = msg_buffer; if (dip->di_dtype->dt_dtype == DT_TAPE) { bp += Sprintf(bp, "%s: File #%lu, %s %u in record number %lu\n", cmdname, (dip->di_files_read + 1), compare_error_str, byte_position, (dip->di_records_read + 1)); } else { bp += Sprintf(bp, "%s: %s %u in record number %lu\n", cmdname, compare_error_str, byte_position, (dip->di_records_read + 1)); } bp += Sprintf(bp, "%s: Data expected = %#x, data found = %#x, byte count = %lu\n", cmdname, expected_data, data_found, byte_count); LogDiagMsg(msg_buffer); } #endif /* defined(LOG_DIAG_INFO) */ } void ReportDeviceInfo ( struct dinfo *dip, size_t byte_count, u_int byte_position, bool eio_error ) { /* * For disk devices, also report the relative block address. */ if (dip->di_random_access) { large_t lba; off_t current_offset; off_t starting_offset; u_int32 dsize = dip->di_dsize; off_t block_offset = (byte_position % dsize); #if defined(AIO) if (aio_flag) { starting_offset = current_acb->aio_offset; current_offset = (starting_offset + byte_position); } else { current_offset = get_position (dip); starting_offset = (current_offset - byte_count); } #else /* !defined(AIO) */ current_offset = get_position (dip); starting_offset = (current_offset - byte_count); #endif /* defined(AIO) */ lba = WhichBlock ((starting_offset + byte_position), dsize); dip->di_lba = lba; dip->di_offset = current_offset; dip->di_position = block_offset; LogMsg (efp, logLevelError, PRT_NOFLUSH, "Relative block number where the error occurred is %lu," " position " FUF, lba, (starting_offset + byte_position)); if (block_offset) { LogMsg (efp, logLevelError, (PRT_NOIDENT|PRT_NOLEVEL), " (offset %lu)\n", block_offset); } else { LogMsg (efp, logLevelError, (PRT_NOIDENT|PRT_NOLEVEL), "\n"); } #if defined(LOG_DIAG_INFO) if (logdiag_flag) { char *bp = msg_buffer; bp += Sprintf(bp, "%s: Relative block number where the error occurred is %lu, ", " position " FUF, cmdname, lba, (starting_offset + byte_position)); if (block_offset) { bp += Sprintf(bp, " (offset %lu)\n", block_offset); } else { bp += Sprintf(bp, "\n"); } LogDiagMsg(msg_buffer); } #endif /* defined(LOG_DIAG_INFO) */ /* * Seek past the erroring block, so we can continue our I/O. */ if (eio_error) { current_offset += dsize; if (dip->di_mode == READ_MODE) { dip->di_fbytes_read += dsize; } else { dip->di_fbytes_written += dsize; } (void) set_position (dip, current_offset); /* * When copying data, properly position the output device too. */ if ( (io_mode != TEST_MODE) && (dip != output_dinfo) && output_dinfo->di_random_access ) { /* * NOTE: Output device could be at a different offset. */ struct dinfo *odip = output_dinfo; off_t output_offset = get_position(odip); output_offset += dsize; if (dip->di_mode == READ_MODE) { dip->di_fbytes_read += dsize; } else { dip->di_fbytes_written += dsize; } (void) set_position (odip, output_offset); } } #if defined(EEI) } else if ( (dip->di_dtype->dt_dtype == DT_TAPE) && eei_flag) { print_mtstatus (dip->di_fd, dip->di_mt, TRUE); #endif /* defined(EEI) */ } } /************************************************************************ * * * ReportLbDataError() - Report Logical Block Data Compare Error. * * * * Inputs: dip = The device info structure. * * lba = The starting logical block address. * * byte_count = The byte count of the last request. * * byte_position = Data buffer index where compare failed. * * expected_data = The expected data. * * data_found = The incorrect data found. * * * * Return Value: Void. * * * ************************************************************************/ void ReportLbdataError ( struct dinfo *dip, u_int32 lba, u_int32 byte_count, u_int32 byte_position, u_int32 expected_data, u_int32 data_found ) { (void) RecordError(); if (dip->di_dtype->dt_dtype == DT_TAPE) { Fprintf ("File #%lu, %s %u in record number %u\n", (dip->di_files_read + 1), compare_error_str, byte_position, (dip->di_records_read + 1)); } else { Fprintf ("%s %u in record number %u\n", compare_error_str, byte_position, (dip->di_records_read + 1)); } ReportDeviceInfo (dip, byte_count, byte_position, FALSE); Fprintf ("Block expected = %u (0x%x), block found = %u (0x%x), count = %u\n", expected_data, expected_data, data_found, data_found, byte_count); #if defined(LOG_DIAG_INFO) if (logdiag_flag) { char *bp = msg_buffer; if (dip->di_dtype->dt_dtype == DT_TAPE) { bp += Sprintf(bp, "%s: File #%lu, %s %u in record number %lu\n", cmdname, (dip->di_files_read + 1), compare_error_str, byte_position, (dip->di_records_read + 1)); } else { bp += Sprintf(bp, "%s: %s %u in record number %lu\n", cmdname, compare_error_str, byte_position, (dip->di_records_read + 1)); } bp += Sprintf(bp, "%s: Block expected = %u (0x%x), block found = %u (0x%x), count = %u\n", cmdname, expected_data, expected_data, data_found, data_found, byte_count); LogDiagMsg(msg_buffer); } #endif /* defined(LOG_DIAG_INFO) */ } /* * Check for all hex characters in string. */ int IS_HexString (char *s) { if ( (*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X')) ) { s += 2; /* Skip over "0x" or "0X" */ } while (*s) { if ( !isxdigit((int)*s++) ) return (FALSE); } return (TRUE); } /* * FmtPrefix() - Format the Prefix String. * * Special Format Characters: * * %d = The device name. * %D = The real device name. * %h = The host name. * %H = The full host name. * %p = The process ID. * %P = The parent process ID. * %u = The user (login) name. * * Inputs: * dip = The device information pointer. * prefix = Pointer to prefix address. * psize = Pointer to prefix length; * * Outputs: * Returns SUCCESS or FAILURE. * Prefix string and size are updated. */ int FmtPrefix (struct dinfo *dip, char **prefix, int *psize) { char *from = *prefix; char *buffer, *to; int length = *psize; if (strstr (from, "%") == NULL) { /* See comments below. */ if (io_type != RANDOM_IO) { return (SUCCESS); } } buffer = to = Malloc(KBYTE_SIZE); while (length--) { bool full_info = FALSE; switch (*from) { case '%': if (length) { switch (*(from + 1)) { case 'd': { to += Sprintf(to, "%s", dip->di_dname); break; } case 'D': { if ( dip->di_device ) { /* Only if known. */ to += Sprintf(to, "%s", dip->di_device); } else { struct dtype *dtp = dip->di_dtype; to += Sprintf(to, "%s", dtp->dt_type); } break; } case 'H': full_info = TRUE; /* FALL THROUGH */ case 'h': { char *p, hostname[MAXHOSTNAMELEN]; if ( gethostname(hostname, sizeof(hostname)) ) { perror("gethostname()"); return (FAILURE); } if ( !full_info ) { if (p = strchr(hostname, '.')) { *p = '\0'; } } to += Sprintf(to, "%s", hostname); break; } case 'p': { pid_t pid = getpid(); to += Sprintf(to, "%d", pid); break; } case 'P': { pid_t ppid = getppid(); to += Sprintf(to, "%d", ppid); break; } case 'u': { char *username = getlogin(); if (username) { to += Sprintf(to, "%s", username); } else { perror("getlogin()"); } break; } default: { *to++ = *from; *to++ = *(from + 1); break; } } length--; from += 2; break; } /* FALLTHROUGH */ default: { *to++ = *from++; break; } } } free (*prefix); *psize = strlen(buffer)+1; /* plus NULL! */ /* * To avoid problems with random I/O, make the prefix string * modulo the lba (iot or lbdata) or our 4 byte data pattern. * Otherwise, random I/O fails with a partial pattern. */ if (io_type == RANDOM_IO) { *psize = roundup(*psize, sizeof(u_int32)); } *prefix = Malloc(*psize); (void)strcpy(*prefix, buffer); free (buffer); return (SUCCESS); } /* * String copy with special mapping. */ int StrCopy (void *to_buffer, void *from_buffer, size_t length) { u_char *to = (u_char *) to_buffer; u_char *from = (u_char *) from_buffer; u_char c, key; int count = 0; while ( length ) { c = *from++; length--; if ( (c != '^') && (c != '\\') ) { *to++ = c; count++; continue; } if (length == 0) { *to++ = c; count++; continue; } if (c == '^') { /* control/X */ c = *from++; length--; *to++ = (c & 037); count++; continue; } c = *from++; length--; if (c == 'a') key = '\007'; /* alert (bell) */ else if (c == 'b') key = '\b'; /* backspace */ else if ( (c == 'e') || (c == 'E') ) key = '\033'; /* escape */ else if (c == 'f') key = '\f'; /* formfeed */ else if (c == 'n') key = '\n'; /* newline */ else if (c == 'r') key = '\r'; /* return */ else if (c == 't') key = '\t'; /* tab */ else if (c == 'v') key = '\v'; /* vertical tab */ else if ( (length >= 2) && ((c == 'x') || (c == 'X')) ) { /* hex */ int cnt; u_char val = 0; for (cnt = 0, key = 0; cnt < 2; cnt++) { c = *from++; count--; if ( isxdigit(c) ) { if ( ('c' >= '0') && (c <= '9') ) val = (c - '0'); else if ( ('c' >= 'a') && (c <= 'f') ) val = 10 + (c - 'a'); else if ( ('c' >= 'A') && (c <= 'F') ) val = 10 + (c - 'A'); } else { key = c; break; } key = (key << 4) | val; } } else if ( (length >= 3) && ((c >= '0') && (c <= '7')) ) { /* octal */ int cnt; for (cnt = 0, key = 0; cnt < 3; cnt++) { key = (key << 3) | (c - '0'); c = *from++; length--; if (c < '0' || c > '7') break; } } else { key = c; /* Nothing special here... */ } *to++ = key; count++; } return (count); } /************************************************************************ * * * stoh() - Convert SCSI bytes to Host short/int/long format. * * * * Description: * * This function converts SCSI (big-endian) byte ordering to the * * format necessary by this host. * * * * Inputs: bp = Pointer to SCSI data bytes. * * size = The conversion size in bytes. * * * * Return Value: * * Returns a long value with the proper byte ordering. * * * ************************************************************************/ u_long stoh (u_char *bp, size_t size) { u_long value = 0L; switch (size) { case sizeof(u_char): value = (u_long) bp[0]; break; case sizeof(u_short): value = ( ((u_long)bp[0] << 8) | (u_long)bp[1] ); break; case 0x03: value = ( ((u_long)bp[0] << 16) | ((u_long)bp[1] << 8) | (u_long) bp[2] ); break; case sizeof(u_int): value = ( ((u_long)bp[0] << 24) | ((u_long)bp[1] << 16) | ((u_long)bp[2] << 8) | (u_long)bp[3]); break; #if defined(MACHINE_64BITS) /* * These sizes, require "long long" (64 bits), and since most * compilers don't support this, I've only defined for Alpha. */ case 0x05: value = ( ((u_long)bp[0] << 32L) | ((u_long)bp[1] << 24) | ((u_long)bp[2] << 16) | ((u_long)bp[3] << 8) | (u_long)bp[4] ); break; case 0x06: value = ( ((u_long)bp[0] << 40L) | ((u_long)bp[1] << 32L) | ((u_long)bp[2] << 24) | ((u_long)bp[3] << 16) | ((u_long)bp[4] << 8) | (u_long)bp[5] ); break; case 0x07: value = ( ((u_long)bp[0] << 48L) | ((u_long)bp[1] << 40L) | ((u_long)bp[2] << 32L) | ((u_long)bp[3] << 24) | ((u_long)bp[4] << 16) | ((u_long)bp[5] << 8) | (u_long)bp[6] ); break; case sizeof(u_long): /* * NOTE: Compiler dependency? If I don't cast each byte * below to u_long, the code generated simply ignored the * bytes [0-3] and sign extended bytes [4-7]. Strange. */ value = ( ((u_long)bp[0] << 56L) | ((u_long)bp[1] << 48L) | ((u_long)bp[2] << 40L) | ((u_long)bp[3] << 32L) | ((u_long)bp[4] << 24) | ((u_long)bp[5] << 16) | ((u_long)bp[6] << 8) | (u_long)bp[7] ); break; #endif /* defined(MACHINE_64BITS) */ default: Fprintf (bad_conversion_str, size); break; } return (value); } /************************************************************************ * * * htos() - Convert Host short/int/long format to SCSI bytes. * * * * Description: * * This function converts host values to SCSI (big-endian) byte * * ordering. * * * * Inputs: bp = Pointer to SCSI data bytes. * * value = The numeric value to convert. * * size = The conversion size in bytes. * * * * Return Value: * * Void. * * * ************************************************************************/ void htos (u_char *bp, u_long value, size_t size) { switch (size) { case sizeof(u_char): bp[0] = (u_char) value; break; case sizeof(u_short): bp[0] = (u_char) (value >> 8); bp[1] = (u_char) value; break; case 0x03: bp[0] = (u_char) (value >> 16); bp[1] = (u_char) (value >> 8); bp[2] = (u_char) value; break; case sizeof(u_int): bp[0] = (u_char) (value >> 24); bp[1] = (u_char) (value >> 16); bp[2] = (u_char) (value >> 8); bp[3] = (u_char) value; break; #if defined(MACHINE_64BITS) /* * These sizes, require "long long" (64 bits), and since most * compilers don't support this, I've only defined for Alpha. */ case 0x05: bp[0] = (u_char) (value >> 32L); bp[1] = (u_char) (value >> 24); bp[2] = (u_char) (value >> 16); bp[3] = (u_char) (value >> 8); bp[4] = (u_char) value; break; case 0x06: bp[0] = (u_char) (value >> 40L); bp[1] = (u_char) (value >> 32L); bp[2] = (u_char) (value >> 24); bp[3] = (u_char) (value >> 16); bp[4] = (u_char) (value >> 8); bp[5] = (u_char) value; break; case 0x07: bp[0] = (u_char) (value >> 48L); bp[1] = (u_char) (value >> 40L); bp[2] = (u_char) (value >> 32L); bp[3] = (u_char) (value >> 24); bp[4] = (u_char) (value >> 16); bp[5] = (u_char) (value >> 8); bp[6] = (u_char) value; break; case sizeof(u_long): bp[0] = (u_char) (value >> 56L); bp[1] = (u_char) (value >> 48L); bp[2] = (u_char) (value >> 40L); bp[3] = (u_char) (value >> 32L); bp[4] = (u_char) (value >> 24); bp[5] = (u_char) (value >> 16); bp[6] = (u_char) (value >> 8); bp[7] = (u_char) value; break; #endif /* defined(MACHINE_64BITS) */ default: Fprintf (bad_conversion_str, size); break; } } #if defined(DEC) #include extern int binlogmsg(int msg_class, char *msg_buffer); /* * LogDiagMsg() - Log a Generic Diagnostic Message. * * Inputs: * msg - Message to log (not to exceed 1024 bytes). * * Notes: * The binlogmsg() function writes to /dev/kbinlog, so this API * normally requires running as root, or setuid root. Changing the * /dev/kbinlog permissions is NFG, we still get an EIO error :-) */ void LogDiagMsg(char *msg) { int error; /* * TODO?: Later, we may wish opt for the event logger (EVM). */ if (logdiag_flag) { error = binlogmsg (ELMSGT_DIAG, msg); if (error && (debug_flag || Debug_flag)) { perror("binlogmsg failed"); } } return; } #else /* !defined(DEC) */ void LogDiagMsg(char *msg) { return; } #endif /* defined(DEC) */ enum trigger_type check_trigger_type (char *str) { if (strcmp(str, "br") == 0) { return (TRIGGER_BR); } else if (strcmp(str, "bdr") == 0) { return (TRIGGER_BDR); } else if (strcmp(str, "seek") == 0) { return (TRIGGER_SEEK); } else if (strncmp(str, "cmd:", 4) == 0) { trigger_cmd = &str[4]; return (TRIGGER_CMD); } else { LogMsg (efp, logLevelCrit, 0, "Valid trigger types are: br, bdr, seek, or cmd:string\n"); return (TRIGGER_INVALID); } } void ExecuteTrigger (struct dinfo *dip, ...) { char cmd[STRING_BUFFER_SIZE]; enum trigger_type trigger = dip->di_trigger; int status; if (( (trigger == TRIGGER_BR) || (trigger == TRIGGER_BDR) || (trigger == TRIGGER_SEEK) ) && (dip->di_dtype->dt_dtype != DT_DISK) ) { LogMsg (efp, logLevelWarn, 0, "Trigger requires a raw disk to execute Scu!\n"); return; } switch (trigger) { case TRIGGER_NONE: return; /*NOTREACHED*/ break; case TRIGGER_BR: (void)sprintf(cmd, "scu -f %s br", dip->di_dname); break; case TRIGGER_BDR: (void)sprintf(cmd, "scu -f %s bdr", dip->di_dname); break; case TRIGGER_SEEK: { (void)sprintf(cmd, "scu -f %s seek lba " LUF, dip->di_dname, dip->di_lba); break; } case TRIGGER_CMD: { va_list ap; char *op; va_start(ap, dip); op = va_arg(ap, char *); va_end(ap); /* * Format: cmd dname op dsize offset position lba errno */ (void)sprintf(cmd, "%s %s %s %u " FUF " %u " LUF " %d", trigger_cmd, dip->di_dname, op, dip->di_dsize, dip->di_offset, dip->di_position, dip->di_lba, dip->di_errno); break; } default: LogMsg (efp, logLevelCrit, 0, "Invalid trigger type detected, type = %d\n", trigger); terminate(FATAL_ERROR); /*NOTREACHED*/ break; } Printf("Executing: %s\n", cmd); status = system(cmd); if (status) Printf("Trigger exited with status %d!\n", status); return; }