/*++ Copyright (c) 1994 Microsoft Corporation Module Name: yapt.c Abstract: This module contains the code for the Yet Another Performance Tool utility. Author: Chuck Park (chuckp) 07-Oct-1994 Revision History: --*/ #include #include #include #include #include #include #include #include #include #include #include #include "yapt.h" // // Test proc. declarations // BOOLEAN ReadSequential( ULONG Iterations ); BOOLEAN WriteSequential( ULONG Iterations ); BOOLEAN ReadRandom( ULONG Iterations ); BOOLEAN WriteRandom( ULONG Iterations ); // // Common util. functions // BOOLEAN CreateTestFile( ); ULONG GetRandomOffset( ULONG min, ULONG max ); BOOL GetSectorSize( PDWORD SectorSize, PCHAR DrvLetter ); VOID LogError( PCHAR ErrString, DWORD UniqueId, DWORD ErrCode ); BOOLEAN ParseCmdLine( INT Argc, CHAR *Argv[] ); BOOLEAN ValidateOption( CHAR Switch, CHAR *Value ); VOID VerifyFileName( IN CHAR *FileName ); VOID Usage( VOID ); VOID __cdecl Log( ULONG LogLevel, PCHAR String, ... ); DWORD YaptSetFileCompression( IN HANDLE FileHandle, IN USHORT CompressionFormat ); // // Default parameters // #define DEFAULT_BUFFER_SIZE 65536 #define DEFAULT_FILE_SIZE (20*1024*1024); ULONG SectorSize = 512; ULONG BufferSize = DEFAULT_BUFFER_SIZE; ULONG FileSize = DEFAULT_FILE_SIZE; UCHAR FileName[MAX_PATH] = "test.dat"; UCHAR Test = READ; UCHAR Access = SEQ; ULONG Verbose = 0; ULONG Iterations = 10; UCHAR ReuseFile = TRUE; UCHAR Yes = FALSE; BOOLEAN ReadOnly = FALSE; INT _cdecl main ( INT Argc, CHAR *Argv[] ) { UCHAR testCase; // // Parse cmd line // if (!ParseCmdLine(Argc,Argv)) { return 1; } // // Create the test file // if (!CreateTestFile()) { return 2; } // // Call appropriate test routine. // testCase = (Test << 1) | Access; switch (testCase) { case 0: if (!ReadSequential(Iterations)) { return 3; } break; case 1: // // Read random // if (!ReadRandom(Iterations)) { return 3; } break; case 2: if (!WriteSequential(Iterations)) { return 3; } break; case 3: // // Write random // if (!WriteRandom(Iterations)) { return 3; } break; default: printf("Invalid test case\n"); return 3; } return 0; } BOOLEAN CreateTestFile( VOID ) { PUCHAR buffer; HANDLE port = INVALID_HANDLE_VALUE; HANDLE fileHandle; OVERLAPPED overlapped,*overlapped2; DWORD bytesTransferred,bytesTransferred2; DWORD_PTR key; ULONG numberWrites; BOOL status; ULONG i; DWORD currentSize; buffer = VirtualAlloc(NULL, DEFAULT_BUFFER_SIZE, MEM_COMMIT, PAGE_READWRITE); if ( !buffer ) { printf("Error allocating buffer %x\n",GetLastError()); return FALSE; } if(ReuseFile) { fileHandle = CreateFile(FileName, ReadOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE), 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL); if(fileHandle != INVALID_HANDLE_VALUE) { Log(1, "Using existing test file %s\n", FileName); goto InitializeTestFile; } else { Log(1, "Error %d opening existing test file\n", GetLastError()); } } else { DeleteFile(FileName); } fileHandle = CreateFile(FileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL); InitializeTestFile: if ( fileHandle == INVALID_HANDLE_VALUE ) { printf("Error opening file %s %d\n",FileName,GetLastError()); return FALSE; } currentSize = GetFileSize(fileHandle, NULL); if(currentSize >= FileSize) { goto EndCreateTestFile; } else if(ReadOnly) { FileSize = currentSize; goto EndCreateTestFile; } // // Uncompress the file - compressed files will always do cached I/O even // if you tell them not to. If this fails assume it's because the // underlying file system doesn't handle compression. // Log(1, "Trying to decompress %s\n", FileName); YaptSetFileCompression(fileHandle, COMPRESSION_FORMAT_NONE); port = CreateIoCompletionPort(fileHandle, NULL, (DWORD_PTR)fileHandle, 0); if ( !port ) { printf("Error creating completion port %d\n",GetLastError()); return FALSE; } Log(1,"Creating test file %s",FileName); memset(&overlapped, 0, sizeof(overlapped)); numberWrites = FileSize / BufferSize; for (i = 0; i < numberWrites; i++) { retryWrite: status = WriteFile(fileHandle, buffer, BufferSize, &bytesTransferred, &overlapped); if ( !status && GetLastError() != ERROR_IO_PENDING ) { if (GetLastError() == ERROR_INVALID_USER_BUFFER || GetLastError() == ERROR_NOT_ENOUGH_QUOTA || GetLastError() == ERROR_WORKING_SET_QUOTA || GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { goto retryWrite; } printf("Error creating test file %x\n",GetLastError()); return FALSE; } Log(2,"."); overlapped.Offset += BufferSize; } for (i = 0; i < numberWrites; i++ ) { status = GetQueuedCompletionStatus(port, &bytesTransferred2, &key, &overlapped2, (DWORD)-1); if ( !status ) { printf("Error on completion during test file create %x\n",GetLastError()); return FALSE; } } Log(1,".. Complete.\n\n"); EndCreateTestFile: if(port != INVALID_HANDLE_VALUE) CloseHandle(port); if(fileHandle != INVALID_HANDLE_VALUE) CloseHandle(fileHandle); VirtualFree(buffer, DEFAULT_BUFFER_SIZE, MEM_DECOMMIT); return TRUE; } BOOLEAN ReadSequential( ULONG Iterations ) { ULONG j, outstandingRequests, remaining = Iterations; ULONG start,end; DOUBLE testTime,thruPut = 0.0,seconds = 0; HANDLE file, port; OVERLAPPED overlapped, *overlapped2; DWORD bytesRead, bytesRead2, errCode, version; DWORD_PTR completionKey; BOOL status; PUCHAR buffer; printf("Starting Sequential Reads of %d Meg file, using %d K I/O size\n", FileSize / 1024*1024, BufferSize / 1024); version = GetVersion() >> 16; buffer = VirtualAlloc(NULL, BufferSize + SectorSize - 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); (ULONG_PTR)buffer &= ~((ULONG_PTR)SectorSize - 1); if ( !buffer ) { Log(0,"Error allocating buffer: %x\n",GetLastError()); return FALSE; } file = CreateFile(FileName, ReadOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE), 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL ); if ( file == INVALID_HANDLE_VALUE ) { Log(0,"Error opening Target file: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } port = CreateIoCompletionPort(file, NULL, (DWORD_PTR)file, 0); if ( !port ) { Log(0,"Error creating completion port: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } while (remaining--) { start = GetTickCount(); memset(&overlapped,0,sizeof(overlapped)); outstandingRequests = 0; for (j = 0; j < FileSize / BufferSize; j++) { do { status = ReadFile(file, buffer, BufferSize, &bytesRead, &overlapped); errCode = GetLastError(); if (!status) { if (errCode == ERROR_IO_PENDING) { break; } else if (errCode == ERROR_NOT_ENOUGH_QUOTA || errCode == ERROR_INVALID_USER_BUFFER || errCode == ERROR_WORKING_SET_QUOTA || errCode == ERROR_NOT_ENOUGH_MEMORY) { // // Allow this to retry. // } else { Log(0,"Error in ReadFile: %x\n",errCode); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } } } while (!status); outstandingRequests++; overlapped.Offset += BufferSize; } for (j = 0; j < outstandingRequests; j++) { status = GetQueuedCompletionStatus(port, &bytesRead2, &completionKey, &overlapped2, (DWORD)-1); if (!status) { Log(0,"GetQueuedCompletionStatus error: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } } end = GetTickCount(); testTime = end - start; testTime /= 1000.0; seconds += testTime; Log(1,"Iteration %2d -> %4.3f MB/S\n",Iterations - remaining,(FileSize / testTime)/(1024*1024)); } CloseHandle(port); CloseHandle(file); thruPut = FileSize * Iterations / seconds; printf("\nAverage Throughput %4.3f MB/S\n",thruPut/(1024*1024)); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return TRUE; } BOOLEAN ReadRandom( ULONG Iterations ) { ULONG j, outstandingRequests, remaining = Iterations; ULONG start,end; DOUBLE testTime,thruPut = 0.0,seconds = 0; HANDLE file, port; OVERLAPPED overlapped, *overlapped2; DWORD bytesRead, bytesRead2, errCode, version; DWORD_PTR completionKey; BOOL status; PUCHAR buffer; ULONG min = FileSize,max = 0; printf("Starting Random Reads of %d Meg file, using %d K I/O size\n", FileSize / 1024*1024, BufferSize / 1024); version = GetVersion() >> 16; srand((unsigned)time(NULL)); buffer = VirtualAlloc(NULL, BufferSize + SectorSize - 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); (ULONG_PTR)buffer &= ~((ULONG_PTR)SectorSize - 1); if ( !buffer ) { Log(0,"Error allocating buffer: %x\n",GetLastError()); return FALSE; } file = CreateFile(FileName, ReadOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE), 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL ); if ( file == INVALID_HANDLE_VALUE ) { Log(0,"Error opening Target file: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } port = CreateIoCompletionPort(file, NULL, (DWORD_PTR)file, 0); if ( !port ) { Log(0,"Error creating completion port: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } while (remaining--) { start = GetTickCount(); memset(&overlapped,0,sizeof(overlapped)); outstandingRequests = 0; for (j = 0; j < FileSize / BufferSize; j++) { do { status = ReadFile(file, buffer, BufferSize, &bytesRead, &overlapped); errCode = GetLastError(); if (!status) { if (errCode == ERROR_IO_PENDING) { break; } else if (errCode == ERROR_NOT_ENOUGH_QUOTA || errCode == ERROR_INVALID_USER_BUFFER || errCode == ERROR_WORKING_SET_QUOTA || errCode == ERROR_NOT_ENOUGH_MEMORY) { // // Allow this to retry. // } else { Log(0,"Error in ReadFile: %x\n",errCode); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } } } while (!status); outstandingRequests++; overlapped.Offset = GetRandomOffset(0,FileSize - BufferSize); if (overlapped.Offset > max) { max = overlapped.Offset; } if (overlapped.Offset < min) { min = overlapped.Offset; } } for (j = 0; j < outstandingRequests; j++) { status = GetQueuedCompletionStatus(port, &bytesRead2, &completionKey, &overlapped2, (DWORD)-1); if (!status) { Log(0,"GetQueuedCompletionStatus error: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } } end = GetTickCount(); testTime = end - start; testTime /= 1000.0; seconds += testTime; Log(1,"Iteration %2d -> %4.3f MB/S\n",Iterations - remaining,(FileSize / testTime)/(1024*1024)); } CloseHandle(port); CloseHandle(file); thruPut = FileSize * Iterations / seconds; printf("\nAverage Throughput %4.3f MB/S\n",thruPut/(1024*1024)); Log(2,"Min = %Lu, Max = %Lu, FileSize = %Lu\n",min,max,FileSize); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return TRUE; } BOOLEAN WriteSequential( ULONG Iterations ) { ULONG j, outstandingRequests, remaining = Iterations; ULONG start,end; DOUBLE testTime,thruPut = 0.0,seconds = 0; HANDLE file, port; OVERLAPPED overlapped, *overlapped2; DWORD bytesWritten, bytesWritten2, errCode, version; DWORD_PTR completionKey; BOOL status; PUCHAR buffer; if(ReadOnly) { printf("Can't run write tests on a read only file\n"); return FALSE; } printf("Starting Sequential Writes of %d Meg file, using %d K I/O size\n", FileSize / 1024*1024, BufferSize / 1024); version = GetVersion() >> 16; buffer = VirtualAlloc(NULL, BufferSize + SectorSize - 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); (ULONG_PTR)buffer &= ~((ULONG_PTR)SectorSize - 1); if ( !buffer ) { Log(0,"Error allocating buffer: %x\n",GetLastError()); return FALSE; } file = CreateFile(FileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL ); if ( file == INVALID_HANDLE_VALUE ) { Log(0,"Error opening Target file: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } port = CreateIoCompletionPort(file, NULL, (DWORD_PTR)file, 0); if ( !port ) { Log(0,"Error creating completion port: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } while (remaining--) { start = GetTickCount(); memset(&overlapped,0,sizeof(overlapped)); outstandingRequests = 0; for (j = 0; j < FileSize / BufferSize; j++) { do { status = WriteFile(file, buffer, BufferSize, &bytesWritten, &overlapped); errCode = GetLastError(); if (!status) { if (errCode == ERROR_IO_PENDING) { break; } else if (errCode == ERROR_NOT_ENOUGH_QUOTA || errCode == ERROR_INVALID_USER_BUFFER || errCode == ERROR_WORKING_SET_QUOTA || errCode == ERROR_NOT_ENOUGH_MEMORY) { // // Allow this to retry. // } else { Log(0,"Error in WriteFile: %x\n",errCode); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } } } while (!status); outstandingRequests++; overlapped.Offset += BufferSize; } for (j = 0; j < outstandingRequests; j++) { status = GetQueuedCompletionStatus(port, &bytesWritten2, &completionKey, &overlapped2, (DWORD)-1); if (!status) { Log(0,"GetQueuedCompletionStatus error: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } } end = GetTickCount(); testTime = end - start; testTime /= 1000.0; seconds += testTime; Log(1,"Iteration %2d -> %4.3f MB/S\n",Iterations - remaining,(FileSize / testTime)/(1024*1024)); } CloseHandle(port); CloseHandle(file); thruPut = FileSize * Iterations / seconds; printf("\nAverage Throughput %4.3f MB/S\n",thruPut/(1024*1024)); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return TRUE; } BOOLEAN WriteRandom( ULONG Iterations ) { ULONG j, outstandingRequests, remaining = Iterations; ULONG start,end; DOUBLE testTime,thruPut = 0.0,seconds = 0; HANDLE file, port; OVERLAPPED overlapped, *overlapped2; DWORD bytesWritten, bytesWritten2, errCode, version; DWORD_PTR completionKey; BOOL status; PUCHAR buffer; ULONG min = FileSize,max = 0; if(ReadOnly) { printf("Can't run write tests on a read only file\n"); return FALSE; } printf("Starting Random Writes of %d Meg file, using %d K I/O size\n", FileSize / 1024*1024, BufferSize / 1024); version = GetVersion() >> 16; buffer = VirtualAlloc(NULL, BufferSize + SectorSize - 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); (ULONG_PTR)buffer &= ~((ULONG_PTR)SectorSize - 1); if ( !buffer ) { Log(0,"Error allocating buffer: %x\n",GetLastError()); return FALSE; } file = CreateFile(FileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL ); if ( file == INVALID_HANDLE_VALUE ) { Log(0,"Error opening Target file: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } port = CreateIoCompletionPort(file, NULL, (DWORD_PTR)file, 0); if ( !port ) { Log(0,"Error creating completion port: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } while (remaining--) { start = GetTickCount(); memset(&overlapped,0,sizeof(overlapped)); outstandingRequests = 0; for (j = 0; j < FileSize / BufferSize; j++) { do { status = WriteFile(file, buffer, BufferSize, &bytesWritten, &overlapped); errCode = GetLastError(); if (!status) { if (errCode == ERROR_IO_PENDING) { break; } else if (errCode == ERROR_NOT_ENOUGH_QUOTA || errCode == ERROR_INVALID_USER_BUFFER || errCode == ERROR_WORKING_SET_QUOTA || errCode == ERROR_NOT_ENOUGH_MEMORY) { // // Allow this to retry. // } else { Log(0,"Error in WriteFile: %x\n",errCode); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } } } while (!status); outstandingRequests++; overlapped.Offset = GetRandomOffset(0,FileSize - BufferSize); if (overlapped.Offset > max) { max = overlapped.Offset; } if (overlapped.Offset < min) { min = overlapped.Offset; } } for (j = 0; j < outstandingRequests; j++) { status = GetQueuedCompletionStatus(port, &bytesWritten2, &completionKey, &overlapped2, (DWORD)-1); if (!status) { Log(0,"GetQueuedCompletionStatus error: %x\n",GetLastError()); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return FALSE; } } end = GetTickCount(); testTime = end - start; testTime /= 1000.0; seconds += testTime; Log(1,"Iteration %2d -> %4.3f MB/S\n",Iterations - remaining,(FileSize / testTime)/(1024*1024)); } CloseHandle(port); CloseHandle(file); thruPut = FileSize * Iterations / seconds; printf("\nAverage Throughput %4.3f MB/S\n",thruPut/(1024*1024)); Log(2,"Min = %Lu, Max = %Lu, FileSize = %Lu\n",min,max,FileSize); VirtualFree(buffer, BufferSize + SectorSize - 1, MEM_DECOMMIT); return TRUE; } ULONG GetRandomOffset( ULONG min, ULONG max ) { INT base = rand(); ULONG retval = ((max - min) / RAND_MAX) * base; retval += SectorSize - 1; retval &= ~(SectorSize - 1); if (retval < min) { return min; } else if (retval > max ){ return max & ~(SectorSize - 1); } else{ return retval; } } BOOLEAN ParseCmdLine( INT Argc, CHAR *Argv[] ) { INT i; CHAR *switches = " ,-"; CHAR swtch,*value,*str; BOOLEAN gotSwitch = FALSE; if (Argc <= 1) { // // Using defaults // return TRUE; } for (i = 1; i < Argc; i++) { str = Argv[i]; value = strtok(str, switches); if (gotSwitch) { if (!ValidateOption(swtch,value)) { Usage(); return FALSE; } else { gotSwitch = FALSE; } } else { gotSwitch = TRUE; swtch = value[0]; if (value[1] || swtch == '?') { Usage(); return FALSE; } } } if (gotSwitch) { Usage(); return FALSE; } else { return TRUE; } } BOOLEAN ValidateOption( CHAR Switch, CHAR *Value ) { Switch = (CHAR)toupper(Switch); switch (Switch) { case 'F': VerifyFileName(Value); strcpy(FileName,Value); break; case 'T': if (_stricmp(Value,"READ")==0) { Test = READ; } else if (_stricmp(Value,"WRITE")==0) { Test = WRITE; } else { return FALSE; } break; case 'A': if (_strnicmp(Value,"SEQ",3)==0) { Access = SEQ; } else if (_strnicmp(Value,"RAND",4)==0) { Access = RAND; } else { return FALSE; } break; case 'B': BufferSize = atol(Value); // // TODO:Adjust buffersize to multiple of a sector and #of K. // BufferSize *= 1024; break; case 'S': FileSize = atol(Value); // // TODO: Adjust filesize to multiple of buffersize and #of Meg. // FileSize *= 1024*1024; break; case 'V': Verbose = atol(Value); break; case 'I': Iterations = atol(Value); break; case 'R': if(tolower(Value[0]) == 'y') { ReuseFile = TRUE; } else { ReuseFile = FALSE; } break; case 'O': if(tolower(Value[0]) == 'y') { ReadOnly = TRUE; } else { ReadOnly = FALSE; } break; default: return FALSE; } return TRUE; } VOID Usage( VOID ) { fprintf(stderr,"usage: YAPT (Yet another performance tool)\n" " -f [filename]\n" " -t [Test:Read,Write]\n" " -a [Access Mode:Seq or Random]\n" " -b [buffer size in KB]\n" " -s [filesize]\n" " -i [Iterations]\n" " -v [Verbosity: 0-2]\n" " -r [Reuse test file: yes, no]\n" " -o [Read Only: yes, no]\n" ); } VOID __cdecl Log( ULONG LogLevel, PCHAR String, ... ) { CHAR Buffer[256]; va_list argp; va_start(argp, String); if (LogLevel <= Verbose) { vsprintf(Buffer, String, argp); printf("%s",Buffer); } va_end(argp); } DWORD YaptSetFileCompression( IN HANDLE FileHandle, IN USHORT CompressionFormat ) { DWORD bytesReturned; assert(FileHandle != INVALID_HANDLE_VALUE); if(!DeviceIoControl( FileHandle, FSCTL_SET_COMPRESSION, &CompressionFormat, sizeof(CompressionFormat), NULL, 0, &bytesReturned, NULL)) { } return GetLastError(); } VOID VerifyFileName( IN CHAR *FileName ) { CHAR input[32]; if(Yes) return; if(((FileName[0] == '\\') || (FileName[0] == '/')) && ((FileName[1] == '\\') || (FileName[1] == '/')) && ((FileName[2] == '.') || (FileName[2] == '?')) && ((FileName[3] == '\\') || (FileName[3] == '/'))) { printf("\n\t%s looks like a raw device\n" "\tdo you want to continue? ", FileName); fflush(stdout); if(fgets(input, 32, stdin) == NULL) { printf("\nQuitting\n"); exit(-1); } if(tolower(input[0]) != 'y') { printf("\nNegative response - quitting\n"); exit(-1); } } return; }