/**************************************************************************** * * * 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: dtgen.c * Author: Robin T. Miller * Date: September 8, 1993 * * Description: * Generic test functions for the 'dt' program. * * Modification History: * * June 23rd, 2004 by Robin Miller. * Added support for triggers on corruption. * Allow non-modulo record sizes for regular files. * * February 27th, 2004 by Robin Miller. * In open_file() pre-format error string for Hazard printing. * * 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. * * September 27th, 2003 by Robin Miller. * Added support for AIX. * * February 21st, 2002 by Robin Miller. * Fix regression introduced by January 29th, 2002 update! * When AIO is specified the base buffer is not setup, only the * data buffer, so referencing this NULL pointer dumped core. * * January 29th, 2002 by Robin Miller. * If compare is disabled, initialize the data buffer to be written. * This is important to honor the user specified pattern, and to avoid * problems writing data to tapes in compressed mode (user may wish to * specify a non-repeating data pattern). In retrospect, it may not * be a good idea to use !compare to disable buffer initialization, * but changing this now will break too many folks :-) * * February 24th, 2001 by Robin Miller. * Add conditionalization for QNX RTP (Neutrino). * * February 6th, 2001 by Robin Miller. * Handle multiple slices to the same output file, by ensuring the * file disposition is set to "keep". Otherwise, the first completing * process deletes the in-use file, which is generally not desriable. * * avoid unexpected failures. This *is* permitted for input files. * * January 29th, 2001 by Robin Miller. * Added validity checks to ensure transfer counts are modulo the * device size, to avoid unexpected failures. * * January 25th, 2001 by Robin Miller. * Added validate options function for checking options after the * device information has been setup (i.e., device size, etc). * * January 15th, 2001 by Robin Miller. * Add error checks to seek_file() and skip_file() calls. * If reopen failures occur, don't terminate, return the error. * * January 14th, 2001 by Robin Miller. * Added support for multiple volumes option. * * January 12th, 2001 by Robin Miller. * When reading multiple tape files, issue a forward space file * mark command between tape files. For systems which don't support * tape operations, we'll use continue to use the read_eof() method. * * December 30th, 2000 by Robin Miller. * Make changes to build using MKS/NuTCracker product. * * November 8th, 2000 by Robin Miller. * Add check for ignoring device close failures. In this case, * the error is displayed as a warning instead of a failure. * * July 14th, 2000 by Robin Miller. * In flush_file(), control sync'ing of data using fsync flag. * * May 8th, 2000 by Robin Miller. * Honor the di_closing flag, to avoid a race condition with the * close function being called (again) while still closing, from the * terminate() routine called by the runtime= alarm, or signals. * [ damn, fixed this in 1994, broke it during recent re-write :-) ] * * April 18th, 2000 by Robin Miller. * Added messages for open/close failures doing EEI reset tests. * Modified calls to report_error() to ensure error count bumped. * * March 28th, 2000 by Robin Miller. * Modified calls to file position functions which now accept a * device information parameter. * * February 19th, 2000 by Robin Miller. * When EEI resets are enabled, retry open/reopen failures attempts. * Also, if we're processing EEI resets, don't fail on close(). * * February 17th, 2000 by Robin Miller. * Adding better support for multi-volume tape testing. * * January 22nd, 2000 by Robin Miller. * Added support for Cygwin tape devices for Windows/NT. * * August 2, 1999 by Robin Miller. * Don't do fsync() to tape devices to avoid errors on Linux. * * May 27, 1999 by Robin Miller. * Adding support for micro-second delays. * * February 26, 1999 by Robin Miller. * Adjust the file count AFTER writing a file mark, so our tape * repositioning code will work if the Reset occurs during the WFM. * * December 21, 1998 by Robin Miller. * Updates necessary to match my tape API changes. * Save device/file open flags in device info struct. * * December 17, 1998 by Robin Miller. * For DUNIX tape devices, clear persistent EEI data after opens. * * 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). * * April 7, 1998 by Robin Miller. * Fix problem with attempting reopen of stdin/stdout. * * March 27, 1997 by Ali Eghlima. * Added cluster support, so more than one process or one system * can access a resource. dlm are being used to synchronize all * access. * * December 9, 1995 by Robin Miller. * Allow writing tape file marks on QNX Operating System. * * September 23, 1994 by Robin Miller. * Make changes necessary to build on QNX 4.21 release. This * required changing O_DSYNCH to O_DSYNC, and O_FSYNCH to O_SYNC. * * May 19, 1994 by Robin Miller. * Altered logic in close_file() & reopen_file() functions to set * the file descriptor to NoFd prior to calling close() to actually * perform the close operation. This avoids a problem where if the * user types Ctrl/C while the close is in progress, the signal handler * gets invoked immediately after the system call, the fd doesn't * get marked closed, and our termination code then attempts to * close an already closed file descriptor and reports the error: * "dt: 'close' - Bad file number" */ #include "dt.h" #if defined(_QNX_SOURCE) # include #else /* !defined(_QNX_SOURCE) */ # include #endif /* defined(_QNX_SOURCE) */ /* * Declare the generic (default) test functions. */ struct dtfuncs generic_funcs = { /* tf_open, tf_close, tf_initialize, */ open_file, close_file, initialize, /* tf_start_test, tf_end_test, */ init_file, nofunc, /* tf_read_file, tf_read_data, tf_cancel_reads, */ read_file, read_data, nofunc, /* tf_write_file, tf_write_data, tf_cancel_writes, */ write_file, write_data, nofunc, /* tf_flush_data, tf_verify_data, tf_reopen_file, */ flush_file, verify_data, reopen_file, /* tf_startup, tf_cleanup, tf_validate_opts */ nofunc, nofunc, validate_opts }; #if defined(MUNSA) dlm_status_t gen_stat; #endif /* defined(MUNSA) */ /************************************************************************ * * * open_file() - Open an input/output file for read/write. * * * * Description: * * This function does the default (generic) open processing. * * * * Inputs: dip = The device information pointer. * * oflags = The device/file open flags. * * * * Return Value: * * Returns 0 / -1 = SUCCESS / FAILURE. * * * ************************************************************************/ int open_file (struct dinfo *dip, int oflags) { char *file = dip->di_dname; int status = SUCCESS; #if defined(EEI) int open_retries = EEI_OPEN_RETRIES; #endif end_of_file = FALSE; dip->di_end_of_file = FALSE; dip->di_end_of_media = FALSE; dip->di_end_of_logical = FALSE; dip->di_errno = 0; dip->di_offset = 0L; dip->di_position = 0; dip->di_lba = 0; if ( (strlen(file) == 1) && (*file == '-') ) { if (debug_flag) { Printf ("Dup'ing standard %s...\n", (dip->di_ftype == INPUT_FILE) ? "input" : "output"); } if (dip->di_ftype == INPUT_FILE) { stdin_flag = TRUE; dip->di_fd = dup (fileno(stdin)); } else { ofp = efp; /* Redirect output to stderr. */ stdout_flag = TRUE; dip->di_fd = dup (fileno(stdout)); verify_flag = FALSE; } if (dip->di_fd < 0) { Fprintf ("dup -> "); report_error (file, TRUE); status = FAILURE; } } else { #if defined(__WIN32__) oflags |= O_BINARY; #endif /* defined(__WIN32__) */ #if defined(EEI) retry: #endif if (debug_flag) { Printf ( "Attempting to open %s file '%s', open flags = %#o (%#x)...\n", (dip->di_ftype == INPUT_FILE) ? "input" : "output", file, oflags, oflags); } dip->di_oflags = oflags; if (dip->di_ftype == INPUT_FILE) { dip->di_fd = open (file, oflags); } else { dip->di_fd = open (file, oflags, 0666); } if (dip->di_fd < 0) { char fmt[STRING_BUFFER_SIZE]; #if defined(EEI) if ( (errno == EIO) && eei_resets && (dip->di_dtype && (dip->di_dtype->dt_dtype == DT_TAPE)) ) { if (--open_retries) { if (verbose_flag) { Printf("Warning, ignoring open error and retrying...\n"); } goto retry; } } #endif /* defined(EEI) */ (void)sprintf(fmt, "open -> %s", file); report_error (fmt, TRUE); ExecuteTrigger(dip, "open"); status = FAILURE; } } if ( (status != FAILURE) && debug_flag) { Printf ("%s file '%s' successfully opened, fd = %d\n", (dip->di_ftype == INPUT_FILE) ? "Input" : "Output", file, dip->di_fd); } return (status); } /************************************************************************ * * * close_file() - Close an open file descriptor. * * * * Description: * * This function does the default (generic) close processing. * * * * Inputs: dip = The device information pointer. * * * * Return Value: * * Returns 0 / -1 = SUCCESS / FAILURE. * * * ************************************************************************/ int close_file (struct dinfo *dip) { char *file = dip->di_dname; int status = SUCCESS; if (dip->di_closing || (dip->di_fd == NoFd)) { return (status); /* Closing or not open. */ } dip->di_closing = TRUE; if (cdelay_count) { /* Optional close delay */ mySleep (cdelay_count); } if (debug_flag) { Printf ("Closing file '%s', fd = %d...\n", file, dip->di_fd); } if ((status = close (dip->di_fd)) == FAILURE) { if (cerrors_flag) { report_error ("close", TRUE); ExecuteTrigger(dip, "close"); } else if (verbose_flag) { status = SUCCESS; Printf("Warning, close failed, errno = %d - %s\n", errno, strerror(errno)); Printf("Warning, ignoring close failure and continuing...\n"); } #if defined(EEI) if ( (errno == EIO) && eei_resets && (dip->di_dtype->dt_dtype == DT_TAPE) ) { if (dip->di_proc_eei) { if (verbose_flag) { Printf("Warning, ignoring close failure and continuing...\n"); } error_count--; status = SUCCESS; } /* No retries, we're already closed! */ } #endif /* defined(EEI) */ } dip->di_fd = NoFd; dip->di_closing = FALSE; return (status); } /************************************************************************ * * * reopen_file() - Close and reopen file descriptor. * * * * Inputs: dip = The device information pointer. * * oflags = The device/file open flags. * * * * Return Value: * * Returns 0 / -1 = SUCCESS / FAILURE. * * * ************************************************************************/ int reopen_file (struct dinfo *dip, int oflags) { struct dtfuncs *dtf = dip->di_funcs; char *file = dip->di_dname; int status = SUCCESS; #if defined(EEI) int open_retries = EEI_OPEN_RETRIES; #endif /* * For stdin or stdout, do not attempt close/open. */ if ( (strlen(file) == 1) && (*file == '-') ) return (status); /* * For tape, we must close & reopen to get to BOT, * an lseek() won't do the trick. */ if (dip->di_fd != NoFd) { /* If not already closed... */ status = (*dtf->tf_close)(dip); } if (edelay_count) { /* Optional end delay. */ mySleep (edelay_count); } end_of_file = FALSE; dip->di_end_of_file = FALSE; dip->di_end_of_media = FALSE; dip->di_end_of_logical = FALSE; dip->di_errno = 0; dip->di_offset = 0L; dip->di_position = 0; dip->di_lba = 0; #if defined(__WIN32__) oflags |= O_BINARY; #endif /* defined(__WIN32__) */ #if defined(EEI) retry: #endif if (debug_flag) { Printf ("Attempting to reopen file '%s', open flags = %#o (%#x)...\n", file, oflags, oflags); } dip->di_oflags = oflags; if ( (dip->di_fd = open (file, oflags)) == FAILURE) { #if defined(EEI) if ( (dip->di_dtype->dt_dtype == DT_TAPE) && (errno == EIO) && eei_resets) { if (--open_retries) { if (verbose_flag) { Printf("Warning, ignoring open error and retrying...\n"); } goto retry; } } #endif /* defined(EEI) */ report_error ("reopen", TRUE); ExecuteTrigger(dip, "open"); return (FAILURE); } if (debug_flag) { Printf ("File '%s' successfully reopened, fd = %d\n", file, dip->di_fd); } #if defined(EEI) if ( (status == SUCCESS) && eei_flag && (dip->di_dtype->dt_dtype == DT_TAPE) ) { clear_eei_status(dip->di_fd, FALSE); } #endif /* defined(EEI) */ return (status); } /************************************************************************ * * * initialize() - Do the default program initialization. * * * * Description: * * This function does the default (generic) test initialization. * * * * Inputs: dip = The device information pointer. * * * * Outputs: Always SUCCESS right now. * * * ************************************************************************/ int initialize (struct dinfo *dip) { if (!data_buffer) { base_buffer = data_buffer = myalloc (data_size, align_offset); } return (SUCCESS); } /************************************************************************ * * * init_file() - Initial file processing. * * * * Description: * * This function is used to process options before starting tests. * * * * Inputs: dip = The device information pointer. * * * * Outputs: Always SUCCESS right now. * * * ************************************************************************/ int init_file (struct dinfo *dip) { int status = SUCCESS; int fd = dip->di_fd; /* * If the lba option is specified, and we're a disk device, * then setup a file position to be seek'ed to below. */ if ( lbdata_addr && !user_position && ((dip->di_dtype->dt_dtype == DT_DISK) || (dip->di_dtype->dt_dtype == DT_BLOCK)) ) { file_position = make_position(dip, lbdata_addr); if ( (io_type == RANDOM_IO) && (rdata_limit <= file_position) ) { LogMsg (efp, logLevelCrit, 0, "Please specify a random data limit > lba file position!\n"); exit (FATAL_ERROR); } } /* * Seek to specified offset (if requested). */ if (file_position) { last_position = set_position (dip, file_position); } /* * Seek to specified record (if requested). */ if (seek_count) { #if defined(_BSD) last_position = seek_file (fd, seek_count, block_size, L_INCR); #else /* !defined(_BSD) */ last_position = seek_file (fd, seek_count, block_size, SEEK_CUR); #endif /* defined(_BSD) */ if (last_position == (off_t) FAILURE) return (FAILURE); show_position (dip, last_position); } /* * Skip over input record(s) (if requested). */ if (skip_count) { status = skip_records (dip, skip_count, data_buffer, block_size); if (debug_flag && (status != FAILURE)) { Printf ("Successfully skipped %d records.\n", skip_count); } } return (status); } /************************************************************************ * * * flush_file() - Flush file data to disk. * * * * Description: * * This function is used to flush the file data to disk after the * * write pass of each test. * * * * Inputs: dip = The device information pointer. * * * * Return Value: * * Returns SUCCESS / FAILURE = Ok / Sync Failed. * * * ************************************************************************/ int flush_file (struct dinfo *dip) { int status = SUCCESS; int fd = dip->di_fd; /* * Ensure data is sync'ed to disk file. */ if ( fsync_flag ) { if (debug_flag) { Printf ("Flushing data to file '%s'...\n", dip->di_dname); } #if defined(_QNX_SOURCE) if ((status = fcntl (fd, F_SETFL, O_DSYNC)) < 0) { report_error("F_SETFL", TRUE); } #else /* !defined(_QNX_SOURCE) */ if ((status = fsync (fd)) < 0) { /* Force data to disk. */ report_error ("fsync", TRUE); } #endif /* !defined(_QNX_SOURCE) */ } return (status); } /************************************************************************ * * * read_file - Read and optionally verify data in the test file. * * * * Inputs: dip = The device information pointer. * * * * Return Value: * * Returns SUCCESS/FAILURE = Ok / Error. * * * ************************************************************************/ int read_file (struct dinfo *dip) { int status; struct dtfuncs *dtf = dip->di_funcs; #if defined(MUNSA) if (munsa_flag) { if (debug_flag) { Printf ("converting to dlm NL-> %d \n", input_munsa_lock_type); } gen_stat = dlm_cvt(&lkid, input_munsa_lock_type, NULL, 0, 0, 0, NULL, 0); if (gen_stat != DLM_SUCCESS) { Fprintf ("dlm_cvt failed\n"); dlm_error(&lkid, gen_stat); /* exit with FATAL ERROR */ } if (debug_flag) { Printf ("done, converting to dlm NL-> %d \n", input_munsa_lock_type); } } /* end if(munsa_flag) .... */ #endif /* defined(MUNSA) */ dip->di_offset = make_offset(dip, lbdata_addr); /* * Loop reading/comparing data until we've detected end of file * or we've reached the data limit or record limit. */ do { /* Read/compare data. */ dip->di_fbytes_read =(v_large) 0; dip->di_records_read = (v_large) 0; read_some_more: if ((status = (*dtf->tf_read_data)(dip)) == FAILURE) break; if (volumes_flag && (multi_volume >= volume_limit) && (dip->di_volume_records >= volume_records)) { break; } /* * Handle reading multiple tapes and multiple files. */ if (end_of_file && multi_flag && (dip->di_dtype->dt_dtype == DT_TAPE) && ((dip->di_records_read != record_limit) && (dip->di_fbytes_read != data_limit)) ) { /* Check for logical end of tape. */ (void) (*dtf->tf_cancel_reads)(dip); if ((status = read_eom(dip)) != SUCCESS) break; if ( !end_of_file ) goto read_some_more; } if (end_of_file) dip->di_files_read++; if ( (dip->di_dtype->dt_dtype == DT_TAPE) && (file_limit && (dip->di_files_read < file_limit)) ) { /* * Normally, we handle EOF conditions on the fly, but when lbdata * or the IOT pattern is enabled, we must cancel outstanding I/O's * so that aio_offset (for LBA) is accurate on subsequent files. */ if (lbdata_flag || iot_pattern) { (void) (*dtf->tf_cancel_reads)(dip); } /* * An exact record or data limit keeps us short of file marks, * so we read and check for an expected end of file here. */ if (!end_of_file) { #if defined(__NUTC__) || defined(__QNXNTO__) || defined(AIX) if ((status = read_eof(dip)) != SUCCESS) break; #else /* !defined(__NUTC__) && !defined(__QNXNTO__) && !defined(AIX) */ status = DoForwardSpaceFile (dip, (daddr_t) 1); if (status != SUCCESS) break; #endif /* defined(__NUTC__) || defined(__QNXNTO__) || defined(AIX) */ if (++dip->di_files_read == file_limit) break; } dip->di_fbytes_read =(v_large) 0; dip->di_records_read = (v_large) 0; end_of_file = FALSE; continue; } } while ( !end_of_file && (error_count < error_limit) && (dip->di_records_read < record_limit) && (dip->di_fbytes_read < data_limit) ); /* * We cancel the reads here incase multiple files were being * read, so reads continue while we process each file mark. */ (void) (*dtf->tf_cancel_reads)(dip); return (status); } /************************************************************************ * * * write_file() - Write data to the test file/device. * * * * Inputs: dip = The device information pointer. * * * * Return Value: * * Returns SUCCESS/FAILURE = Ok/Error. * * * ************************************************************************/ int write_file (struct dinfo *dip) { int status; struct dtfuncs *dtf = dip->di_funcs; #if defined(MUNSA) if (munsa_flag) { if (debug_flag) { Printf ("converting to dlm NL-> %d .\n", output_munsa_lock_type); } gen_stat = dlm_cvt(&lkid, output_munsa_lock_type, NULL, 0, 0, 0, NULL, 0); if (gen_stat != DLM_SUCCESS) { Fprintf ("dlm_cvt failed\n"); dlm_error(&lkid, gen_stat); /* exit with FATAL ERROR */ } if (debug_flag) { Printf ("done, converting to dlm NL-> %d .\n", output_munsa_lock_type); } } /* end if(munsa_flag)... */ #endif /* defined(MUNSA) */ dip->di_offset = make_offset(dip, lbdata_addr); /* * Initialize the data buffer with a pattern if compare is * disabled, so we honor the user specified pattern. */ if ((io_mode == TEST_MODE) && !compare_flag) { if (iot_pattern) { u_int32 lba = make_lbdata (dip, dip->di_offset); (void)init_iotdata(block_size, lba, lbdata_size); } fill_buffer (data_buffer, block_size, pattern); } /* * Loop writing data until end of media or data limit reached. */ do { /* Write data pattern. */ if (raw_flag) { dip->di_fbytes_read = (v_large) 0; dip->di_records_read = (v_large) 0; } dip->di_fbytes_written = (v_large) 0; dip->di_records_written = (v_large) 0; if ((status = (*dtf->tf_write_data)(dip)) == FAILURE) break; if (volumes_flag && (multi_volume >= volume_limit) && (dip->di_volume_records >= volume_records)) { break; } /* * Handle writing multiple tape files. */ if ( (dip->di_dtype->dt_dtype == DT_TAPE) && (file_limit && (dip->di_files_written < file_limit)) ) { /* * For tapes, write a file mark for all but last file. * The last file mark(s) are written when closing tape. */ #if !defined(__NUTC__) && !defined(__QNXNTO__) && !defined(AIX) if ((dip->di_files_written + 1) < file_limit) { status = DoWriteFileMark (dip, (daddr_t) 1); if (status != SUCCESS) break; } #endif /* !defined(__NUTC__) && !defined(__QNXNTO__) && !defined(AIX) */ if (++dip->di_files_written < file_limit) { dip->di_fbytes_written =(v_large) 0; dip->di_records_written = (v_large) 0; continue; /* Write the next file. */ } } } while ( !end_of_file && (error_count < error_limit) && (dip->di_records_written < record_limit) && (dip->di_fbytes_written < data_limit) ); return (status); } /************************************************************************ * * * validate_opts() - Generic Validate Option Test Criteria. * * * * Description: * * This function verifies the options specified are valid for the * * test criteria selected. * * * * Inputs: dip = The device information pointer. * * * * Return Value: * * Returns SUCESS / FAILURE = Valid / Invalid Options. * * * ************************************************************************/ int validate_opts (struct dinfo *dip) { if (bypass_flag) return (SUCCESS); if (dip->di_fd == NoFd) { /* * Validation checks *before* the device is open. */ if (min_size && (dip->di_dtype->dt_dtype != DT_REGULAR)) { size_t value; char *emsg = NULL; if (dip->di_dsize > min_size) { value = min_size; emsg = "min size"; } else if (dip->di_dsize > max_size) { value = max_size; emsg = "max size"; } if (emsg) { Fprintf( "Please specify %s (%u) greater than device size %u of bytes.\n", emsg, value, dip->di_dsize); return (FAILURE); } } } else { /* * Validation checks *after* the device is open. */ if ( (dip->di_dtype->dt_dtype == DT_REGULAR) && (dip->di_ftype == OUTPUT_FILE) && num_slices && (dispose_mode == DELETE_FILE) ) { dispose_mode = KEEP_FILE; if (verbose_flag) { Printf("Warning: Multiple slices to same file, setting dispose=keep!\n"); } } if ( (io_dir == REVERSE) || (io_type == RANDOM_IO) ) { if ( !dip->di_random_access ) { Fprintf( "Random I/O or reverse direction, is only valid for random access device!\n"); return (FAILURE); } if ( (dip->di_dtype->dt_dtype == DT_REGULAR) && !user_capacity ) { Fprintf( "Please specify a data limit, record count, or capacity for random I/O.\n"); return (FAILURE); } } /* * Note: Non-modulo device sizes permitted to regular files! */ if (dip->di_random_access && (dip->di_dtype->dt_dtype != DT_REGULAR) ) { size_t value; char *emsg = NULL; if (block_size % dip->di_dsize) { value = block_size; emsg = "block size"; } else if (min_size && (min_size % dip->di_dsize)) { value = min_size; emsg = "min size"; } else if (max_size && (max_size % dip->di_dsize)) { value = max_size; emsg = "max size"; } else if (incr_count && (incr_count % dip->di_dsize)) { value = incr_count; emsg = "incr count"; } if (emsg) { Fprintf( "Please specify a %s (%u) modulo the device size of %u bytes!\n", emsg, value, dip->di_dsize); return (FAILURE); } } } return (SUCCESS); }