/**************************************************************************** * * * 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: dtread.c * Author: Robin T. Miller * * Description: * Read routines for generic data test program. */ #include "dt.h" #if !defined(_QNX_SOURCE) # include <sys/file.h> #endif /* !defined(_QNX_SOURCE) */ #include <sys/stat.h> /* * Modification History: * * October 21st, 2004 by Robin Miller. * For variable record lengths, ensure we prime the first size * to ensure it meets device size alignment requirements. * * June 23rd, 2004 by Robin Miller. * Added support for triggers on corruption. * * February 13th, 2004 by Robin Miller. * Factor in the file position when doing reverse I/O, to avoid * reading into that area (which is now deemed protected). * * 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. * * March 4th, 2003 by Robin Miller. * Add EOF support for older SunOS release. This means reads * past EOF return EIO, but continue on this to find real capacity. * * November 20th, 2002 by Robin Miller. * Updated FindCapacity() to expect ENXIO for reads past EOM. * * June 25th, 2001 by Robin Miller. * Restructured code associated with Tru64 Unix EEI, so we obtain * the EEI status for all tape errors, not just EIO errors. * * January 28th, 2001 by Robin Miller. * Allow FindCapacity() to be called prior to the file being opened * The device capacity is necessary early on for the new slices option. * * January 26th, 2001 by Robin Miller. * Added support for reverse reading. * * January 24th, 2001 by Robin Miller. * Add support for variable I/O requests sizes. * * January 14th, 2001 by Robin Miller. * Added support for multiple volumes option. * * January 2nd, 2001 by Robin Miller. * Remove check for block or character device in FindCapacity(), since * that check is already done in mainline code, so it's a duplicate check. * * December 30th, 2000 by Robin Miller. * Make changes to build using MKS/NuTCracker product. * * October 2nd, 2000 by Robin Miller. * Update FindCapacity() to accept ENXIO for reads at EOM on * SCO UnixWare systems. * * April 2nd, 2000 by Robin Miller. * Updated FindCapacity(): printf -> Fprintf so messages go to * log file, and add/change casts on large_t values (nothing big :-). * * March 28th, 2000 by Robin Miller. * Modified calls to file position functions which now accept a * device information parameter. * * March 27th, 2000 by Robin Miller. * Modified FindCapacity() to continue on read() errors, to handle * broken Linux ATAPI (IDE) driver, which returns EIO on errors past the * end of media (damn!). * * February 18th, 2000 by Robin Miller. * Fix a problem where the records read value was not updated * when the data limit was reached first. * * 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. * * January 17th, 2000 by Robin Miller. * Added checks @ EOF with/multi-volume enabled, so Copy/Verify * operations properly prompt for the next volume. This allows 'dt' * to be used as a general purpose multi-volume tool w/other utilities. * * January 6th, 2000 by Robin Miller. * Added support for multi-volume media. * * December 30th, 1999 by Robin Miller. * Modify call to do_random() to pass the transfer size. * Fix lbdata problem when using step option (wrong lba). * * August 6th, 1999 by Robin Miller. * Better parameterizing of "long long" printf formatting. * * July 22nd, 1999 by Robin Miller. * Added support for IOT (DJ's) test pattern. * * May 27, 1999 by Robin Miller. * Adding support for micro-second delays. * * March 1, 1999 by Robin Miller. * For tapes when Debug is enabled, report the file number. * * December 21, 1998 by Robin Miller. * Add hooks to handle tape device resets (DUNIX specific). * * 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. * When random I/O and lbdata options are both enabled, use the * file offset seeked to 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). * * February 29, 1996 by Robin Miller. * Added FindCapacity() function to obtain capacity for random * access devices. Must set limits for random I/O. * * February 28, 1996 by Robin Miller. * Added support for copying and verifying device/files. * Modified logic so read errors honor users' error limit. * * February 23, 1996 by Robin Miller. * Only report partial record warning for sequential I/O testing. * Random I/O can position us towards the end of media often, and * partial transfers are likely especially with large block sizes. * * November 11, 1995 by Robin Miller. * Fix bug with init'ing and performing pad byte verification. * This caused variable length reads with small increment values * to report an (invalid) pad byte data compare error. e.g.: * * % dt of=/dev/rmt0h min=10k max=64k incr=1 pattern=incr * * July 15, 1995 by Robin Miller. * Fix end of media error handling (ENOSPC), and cleanup code. * * January 20, 1994 by Robin Miller. * When initializing the data buffer, don't do the entire buffer since * init'ing large buffer (e.g. 100m) using min, max, and incr options cause * excessive paging and VERY poor performance. * * September 17, 1993 by Robin Miller. * Report record number on warning errors (for debug). * * September 4, 1993 by Robin Miller. * Moved memory mapped I/O logic to seperate module. * * Septemeber 1, 1993 by Robin Miller. * Add ability to read variable record sizes. * * August 31, 1993 by Robin Miller. * Rotate starting data buffer address through sizeof(long). * * August 27, 1993 by Robin MIller. * Added support for DEC OSF/1 POSIX Asynchronous I/O (AIO). * * August 18, 1992 by Robin Miller. * If "step=" option was specified, then seek that many bytes * before the next read request (for disks). * * August 10, 1993 by Robin Miller. * Added initializing and checking of buffer pad bytes to ensure * data corruption does *not* occur at the end of read buffers. * * August 5, 1993 by Robin Miller. * Added support for reading multiple tape files. * * September 11, 1992 by Robin Miller. * Ensure data limit specified by user is not exceeded, incase * the block size isn't modulo the data limit. * * September 5, 1992 by Robin Miller. * Initial port to QNX 4.1 Operating System. * * 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 7, 1990 by Robin Miller. * If "seek=n" option is specified, then seek that many records * before starting to read. The "skip=n" option skips records * by reading, while "seek=n" seeks past records. */ /************************************************************************ * * * read_data() - Read and optionally verify data read. * * * * Inputs: dip = The device information pointer. * * * * Outputs: Returns SUCCESS/FAILURE = Ok/Error. * * * ************************************************************************/ int read_data (struct dinfo *dip) { register ssize_t count; register size_t bsize, dsize; int status = SUCCESS; struct dtfuncs *dtf = dip->di_funcs; u_int32 lba; /* * For variable length records, initialize to minimum record size. */ if (min_size) { if (variable_flag) { dsize = get_variable (dip); } else { dsize = min_size; } } else { dsize = block_size; } if (dip->di_random_access) { if (io_dir == REVERSE) { (void)set_position(dip, (off_t)rdata_limit); } lba = get_lba(dip); dip->di_offset = get_position(dip); } else { lba = make_lbdata (dip, dip->di_offset); } /* * Now read and optionally verify the input records. */ while ( (error_count < error_limit) && (dip->di_fbytes_read < data_limit) && (dip->di_records_read < record_limit) ) { if (volumes_flag && (multi_volume >= volume_limit) && (dip->di_volume_records >= volume_records)) { break; } if (rdelay_count) { /* Optional read delay. */ mySleep (rdelay_count); } /* * If data limit was specified, ensure we don't exceed it. */ if ( (dip->di_fbytes_read + dsize) > data_limit) { bsize = (size_t)(data_limit - dip->di_fbytes_read); } else { bsize = dsize; } if (io_dir == REVERSE) { bsize = MIN((dip->di_offset-file_position), bsize); dip->di_offset = set_position(dip, (off_t)(dip->di_offset - bsize)); } else if (io_type == RANDOM_IO) { dip->di_offset = do_random (dip, TRUE, bsize); } if (debug_flag && (bsize != dsize) && !variable_flag) { Printf ("Record #%lu, Reading a partial record of %lu bytes...\n", (dip->di_records_read + 1), bsize); } if (iot_pattern || lbdata_flag) { lba = make_lbdata (dip, (off_t)(dip->di_volume_bytes + dip->di_offset)); } /* * If requested, rotate the data buffer through ROTATE_SIZE bytes * to force various unaligned buffer accesses. */ if (rotate_flag) { data_buffer = (base_buffer + (rotate_offset++ % ROTATE_SIZE)); } /* * If we'll be doing a data compare after the read, then * fill the data buffer with the inverted pattern to ensure * the buffer actually gets written into (driver debug mostly). */ if ((io_mode == TEST_MODE) && compare_flag) { init_buffer (data_buffer, bsize, ~pattern); init_padbytes (data_buffer, bsize, ~pattern); if (iot_pattern) { lba = init_iotdata (bsize, lba, lbdata_size); } } if (Debug_flag) { u_int32 iolba = NO_LBA; if (dip->di_random_access) { iolba = (get_position(dip) / dip->di_dsize); } else if (lbdata_flag || iot_pattern) { iolba = make_lbdata (dip, (off_t)(dip->di_volume_bytes + dip->di_offset)); } report_record(dip, (dip->di_files_read + 1), (dip->di_records_read + 1), iolba, READ_MODE, data_buffer, bsize); } count = read_record (dip, data_buffer, bsize, dsize, &status); if (end_of_file) break; /* Stop reading at end of file. */ if (status == FAILURE) { if (error_count >= error_limit) break; } else if (io_mode == COPY_MODE) { status = copy_record (output_dinfo, data_buffer, count); if ( (error_count >= error_limit) || end_of_file) break; } else if (io_mode == VERIFY_MODE) { status = verify_record (output_dinfo, data_buffer, count); if ( (error_count >= error_limit) || end_of_file) break; } /* * Verify the data (unless disabled). */ if ( (status != FAILURE) && compare_flag && (io_mode == TEST_MODE) ) { ssize_t vsize = count; status = (*dtf->tf_verify_data)(dip, data_buffer, vsize, pattern, &lba); /* * Verify the pad bytes (if enabled). */ if ( (status == SUCCESS) && pad_check) { (void) verify_padbytes (dip, data_buffer, vsize, ~pattern, bsize); } } /* * If we had a partial transfer, perhaps due to an error, adjust * the logical block address in preparation for the next request. */ if (iot_pattern && ((size_t)count < bsize)) { size_t resid = (bsize - count); lba -= howmany(resid, lbdata_size); } /* * For variable length records, adjust the next record size. */ if (min_size) { if (variable_flag) { dsize = get_variable (dip); } else { dsize += incr_count; if (dsize > max_size) dsize = min_size; } } dip->di_records_read++; dip->di_volume_records++; if (io_dir == FORWARD) { dip->di_offset += count; /* Maintain our own position too! */ } else if ( (io_type == SEQUENTIAL_IO) && (dip->di_offset == (off_t) file_position) ) { set_Eof(dip); break; } if (step_offset) { if (io_dir == FORWARD) { dip->di_offset = incr_position (dip, step_offset); } else if ((dip->di_offset -= step_offset) <= (off_t) file_position) { set_Eof(dip); break; } } } return (status); } /************************************************************************ * * * check_read() - Check status of last read operation. * * * * Inputs: dip = The device information pointer. * * count = Number of bytes read. * * size = Number of bytes expected. * * * * Outputs: Returns SUCCESS/FAILURE/WARNING = Ok/Error/Warning * * * ************************************************************************/ int check_read (struct dinfo *dip, ssize_t count, size_t size) { int status = SUCCESS; if ((size_t)count != size) { if (count == FAILURE) { report_error ("read", FALSE); ReportDeviceInfo (dip, 0, 0, (bool)(errno == EIO)); ExecuteTrigger(dip, "read"); } else { /* * For reads at end of file or reads at end of block * devices, we'll read less than the requested count. * In this case, we'll treat this as a warning since * this is to be expected. In the case of tape, the * next read will indicate end of tape (in my driver). * * NOTE: The raw device should be used for disks. */ if ( (debug_flag || verbose_flag || ((size_t)count > size)) && (io_mode == TEST_MODE) /*&& (io_type == SEQUENTIAL_IO)*/ ) { Printf( "WARNING: Record #%lu, attempted to read %lu bytes, read only %lu bytes.\n", (dip->di_records_read + 1), size, count); } if ((size_t)count < size) { /* Partial read is a warning. */ warning_errors++; return (WARNING); } ReportDeviceInfo (dip, count, 0, FALSE); } (void)RecordError(); dip->di_read_errors++; status = FAILURE; } return (status); } /* * This function is envoked when reading multiple tape files, to * position past an expected file mark. This is especially important * when using the lbdata or iot options, since encountering an expected * EOF throws off the offset being maintained, resulting in an lba error. */ int read_eof(struct dinfo *dip) { ssize_t count; size_t bsize = block_size; int status = SUCCESS; if (debug_flag) { Printf("Processing end of file... [file #%lu, record #%lu]\n", (dip->di_files_read + 1), (dip->di_records_read + 1)); } dip->di_eof_processing = TRUE; count = read_record (dip, data_buffer, bsize, bsize, &status); dip->di_eof_processing = FALSE; if (!end_of_file) { Fprintf("ERROR: File %lu, Record %lu, expected EOF was NOT detected!\n", (dip->di_files_read + 1), (dip->di_records_read + 1)); ReportDeviceInfo (dip, count, 0, FALSE); (void)RecordError(); dip->di_read_errors++; status = FAILURE; } return (status); } /* * This function is called after EOF is detected, to read the next record * which checks for reaching the end of logical tape (i.e. two successive * file marks). For multi-volume tapes, the user will be prompted for the * next volume via read_record(), and the end of file flag gets reset when * the tape is re-open'ed. */ int read_eom(struct dinfo *dip) { ssize_t count; size_t bsize = block_size; int status = SUCCESS; if (debug_flag) { Printf("Processing end of media... [file #%lu, record #%lu]\n", (dip->di_files_read + 1), (dip->di_records_read + 1)); } dip->di_eom_processing = TRUE; count = read_record (dip, data_buffer, bsize, bsize, &status); dip->di_eom_processing = FALSE; if (multi_flag) { if (end_of_file) { Fprintf("ERROR: File %lu, Record %lu, expected EOM was NOT detected!\n", (dip->di_files_read + 1), (dip->di_records_read + 1)); ReportDeviceInfo (dip, count, 0, FALSE); (void)RecordError(); return (FAILURE); } } else if ( !dip->di_end_of_logical ) { Fprintf("ERROR: File %lu, Record %lu, expected EOM was NOT detected!\n", (dip->di_files_read + 1), (dip->di_records_read + 1)); ReportDeviceInfo (dip, count, 0, FALSE); (void)RecordError(); dip->di_read_errors++; return (FAILURE); } return (SUCCESS); /* We don't care about the read status! */ } /************************************************************************ * * * read_record() - Read record from device or file. * * * * Inputs: dip = The device information pointer. * * buffer = The data buffer to read into. * * bsize = The number of bytes read. * * dsize = The users' requested size. * * status = Pointer to status variable. * * * * Outputs: status = SUCCESS/FAILURE/WARNING = Ok/Error/Warning * * Return value is number of bytes from read() request. * * * ************************************************************************/ ssize_t read_record ( struct dinfo *dip, u_char *buffer, size_t bsize, size_t dsize, int *status ) { ssize_t count; retry: *status = SUCCESS; count = read (dip->di_fd, buffer, bsize); #if defined(EEI) if ( (count == FAILURE) && (dip->di_dtype->dt_dtype == DT_TAPE) ) { if ( (errno == EIO) && eei_resets) { if ( HandleTapeResets(dip) ) { goto retry; } } else if (eei_flag) { (void) get_eei_status(dip->di_fd, dip->di_mt); } } #endif /* defined(EEI) */ /* * Allow terminal reads to continue on end of file (eof). * [ NOTE: This allows reads with timeouts to continue. ] */ if ( count || (dip->di_dtype->dt_dtype != DT_TERMINAL) ) { if ( is_Eof (dip, count, status) ) { if (multi_flag && (!stdin_flag || (dip->di_ftype == OUTPUT_FILE)) ) { if ( (dip->di_dtype->dt_dtype == DT_TAPE) && !dip->di_end_of_logical ) { return (count); /* Expect two file marks @ EOM. */ } *status = HandleMultiVolume (dip); dip->di_offset = (off_t) 0; if ( !dip->di_eof_processing && !dip->di_eom_processing ) { if (*status == SUCCESS) goto retry; } } return (count); /* Stop reading at end of file. */ } } if ( dip->di_eof_processing || dip->di_eom_processing ) { return (count); } dip->di_end_of_file = FALSE; /* Reset saved end of file state. */ /* * If something was read, adjust counts and statistics. */ if (count > (ssize_t) 0) { dip->di_dbytes_read += count; dip->di_fbytes_read += count; dip->di_vbytes_read += count; if ((size_t)count == dsize) { records_processed++; } else { partial_records++; } } *status = check_read (dip, count, bsize); return (count); } /************************************************************************ * * * verify_record() - Verify record with selected output device/file. * * * * Inputs: dip = The device information pointer. * * buffer = The data buffer to compare. * * bsize = The number of bytes read. * * * * Outputs: Returns SUCCESS/FAILURE/WARNING = Ok/Error/Warning * * * ************************************************************************/ int verify_record ( struct dinfo *dip, u_char *buffer, size_t bsize ) { struct dtfuncs *dtf = dip->di_funcs; ssize_t count; int status; u_int32 lba = lbdata_addr; /* * TODO: Re-write this using the verify buffer (when I have time). */ count = read_record (dip, pattern_buffer, bsize, bsize, &status); if ( (status == FAILURE) || end_of_file) return (status); /* * I realize this is real ugly, but I wanted to use existing code. */ patbuf_size = count; pattern_bufptr = pattern_buffer; pattern_bufend = pattern_buffer + count; status = (*dtf->tf_verify_data)(dip, buffer, count, pattern, &lba); /* TODO: Get this into read_record() where it belongs! */ dip->di_records_read++; return (status); } /************************************************************************ * * * FindCapacity() - Find capacity of a random access device. * * * * Inputs: dip = The device information pointer. * * * * Outputs: Fills in device capacity and data limit on success. * * * * Return Value: Returns SUCCESS/FAILURE/WARNING = Ok/Error/Warning * * * ************************************************************************/ int FindCapacity (struct dinfo *dip) { u_int32 dsize = dip->di_dsize; off_t lba, max_seek = (MAX_SEEK - dsize); long adjust = ((250 * MBYTE_SIZE) / dsize); int attempts = 0; ssize_t count, last; u_char *buffer; int fd, saved_fd = NoFd, status = SUCCESS; bool temp_fd = FALSE; /* * Use the user specified capacity (if specified). */ if (user_capacity) { lba = (off_t)(user_capacity / dsize); goto set_capacity; } else if (dip->di_data_limit) { return (status); } if (debug_flag || Debug_flag || rDebugFlag) { Printf ("Attempting to calculate capacity via seek/read algorithm...\n"); } /* * If the device is open in write mode, open another * file descriptor for reading. */ if ( (dip->di_fd == NoFd) || (dip->di_mode == WRITE_MODE) ) { temp_fd = TRUE; saved_fd = dip->di_fd; #if defined(__WIN32__) if ( (fd = open (dip->di_dname, (O_RDONLY|O_BINARY))) < 0) { #else /* !defined(__WIN32__) */ if ( (fd = open (dip->di_dname, O_RDONLY)) < 0) { #endif /* defined(__WIN32__) */ report_error ("FindCapacity() open", FALSE); return (FAILURE); } dip->di_fd = fd; } buffer = (u_char *) malloc (dsize); if (buffer == NULL) return (FAILURE); /* * Algorthim: * There maybe a better may, but this works... */ lba = adjust; adjust /= 2; while (TRUE) { attempts++; #if defined(DEBUG) Printf("lba = " LUF ", adjust = %lu\n", lba, adjust); #endif /* * We cannot seek past the maximum allowable file position, * otherwise lseek() fails, and 'dt' exits. So, we must * limit seeks appropriately, and break out of this loop * if we hit the upper seek limit. */ if ( (off_t)(lba * dsize) < (off_t) 0 ) { lba = (max_seek / dsize); } (void) set_position (dip, (off_t)(lba * dsize)); if ( (count = read (dip->di_fd, buffer, dsize)) == (ssize_t)dsize) { if (lba == (off_t)(max_seek / dsize)) break; lba += adjust; if (adjust == 1) break; #if defined(SCO) || defined(HP_UX) || defined(AIX) } else if ( (count == 0) || ( (count < 0) && ((errno == ENOSPC) || (errno == ENXIO)) ) ) { #elif defined(BrokenEOF) } else if ( (count == 0) || ( (count < 0) && ((errno == ENOSPC) || (errno == EIO)) ) ) { #else /* !defined(SCO) */ } else if ( (count == 0) || ( (count < 0) && (errno == ENOSPC) ) ) { #endif /* defined(SCO) || defined(HP_UX) || defined(AIX) */ if (last) adjust /= 2; if (!adjust) adjust++; lba -= adjust; } else { report_error ("FindCapacity() read", FALSE); status = FAILURE; break; } last = count; } free (buffer); if (temp_fd) { (void) close (dip->di_fd); dip->di_fd = saved_fd; } else { (void) set_position (dip, (off_t) 0); } /* * If the read failed, set the lba to the last successful read, * and continue. Won't be perfect, but at least we can run. * Note: Workaround for problem seen on Linux w/ATAPI CD-ROM's. */ if (status == FAILURE) { #if 1 #if defined(DEBUG) Printf("failed, last good lba was " LUF ", adjust was %ld\n", lba, adjust); #endif /* defined(DEBUG) */ lba -= adjust; exit_status = SUCCESS; #else return (status); /* Return the failure! */ #endif } #if defined(DEBUG) Printf ("read attempts was %d, the max lba is " LUF "\n", attempts, lba); #endif /* defined(DEBUG) */ set_capacity: dip->di_capacity = lba; dip->di_data_limit = (large_t)(lba * dsize); if (!record_limit) record_limit = INFINITY; if (data_limit == INFINITY) data_limit = dip->di_data_limit; /* * The proper data limit is necessary for random I/O processing. */ if ( (io_dir == REVERSE) || (io_type == RANDOM_IO) ) { if ( (rdata_limit == (large_t)0) || (rdata_limit > dip->di_data_limit) ) { rdata_limit = dip->di_data_limit; } if (debug_flag || Debug_flag || rDebugFlag || (status == FAILURE)) { /* TODO: Cleanup this mess! */ #if !defined(__GNUC__) && defined(_NT_SOURCE) /* Avoid: error C2520: conversion from unsigned __int64 to double not implemented, use signed __int64 */ Printf ("Random data limit set to " LUF " bytes (%.3f Mbytes), " LUF " blocks.\n", rdata_limit, ((double)(slarge_t)rdata_limit/(double)MBYTE_SIZE), (rdata_limit / dsize)); #else /* !defined(_NT_SOURCE) */ Printf ("Random data limit set to " LUF " bytes (%.3f Mbytes), " LUF " blocks.\n", rdata_limit, ((double)rdata_limit/(double)MBYTE_SIZE), (rdata_limit / dsize)); #endif /* !defined(__GNUC__) && defined(_NT_SOURCE) */ } if (rdata_limit <= file_position) { LogMsg (efp, logLevelCrit, 0, "Please specify a random data limit > file position!\n"); exit (FATAL_ERROR); } } else if (debug_flag || Debug_flag || (status == FAILURE)) { #if !defined(__GNUC__) && defined(_NT_SOURCE) /* Avoid: error C2520: conversion from unsigned __int64 to double not implemented, use signed __int64 */ Printf ("Data limit set to " LUF " bytes (%.3f Mbytes), " LUF " blocks.\n", dip->di_data_limit, ((double)(slarge_t)dip->di_data_limit / (double)MBYTE_SIZE), (dip->di_data_limit / dsize)); #else /* !defined(_NT_SOURCE) */ Printf ("Data limit set to " LUF " bytes (%.3f Mbytes), " LUF " blocks.\n", dip->di_data_limit, ((double)dip->di_data_limit/(double)MBYTE_SIZE), (dip->di_data_limit / dsize)); #endif /* !defined(__GNUC__) && defined(_NT_SOURCE) */ if (file_position > dip->di_data_limit) { LogMsg (efp, logLevelCrit, 0, "Please specify a file position < media capacity!\n"); exit (FATAL_ERROR); } } return (SUCCESS); }