|
|
#include "windows.h"
#include "ntverp.h"
#include <string>
#include <xstring>
#include <utility>
#include <vector>
using namespace std;
bool g_BufferStdInFully = false; bool g_LockPerItem = false;
HANDLE ObtainFileHandle(wstring& wsFileName);
class CTargetFile { wstring m_wsTargetName; HANDLE hFile; public: CTargetFile(wstring& wsT) : m_wsTargetName(wsT), hFile(INVALID_HANDLE_VALUE) { }; ~CTargetFile() { CloseFile(); }
BOOL EnsureOpen() { if (hFile == INVALID_HANDLE_VALUE) { hFile = ObtainFileHandle(m_wsTargetName); }
return hFile != INVALID_HANDLE_VALUE; }
BOOL CloseFile() { if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; }
return hFile == INVALID_HANDLE_VALUE; }
BOOL SetFilePointerToEnd() { DWORD dwResult = SetFilePointer(hFile, 0, NULL, FILE_END); if ((dwResult == INVALID_SET_FILE_POINTER) && GetLastError()) { dwResult = GetLastError(); return FALSE; }
return TRUE; }
BOOL AppendData(PBYTE pbData, DWORD dwDataSize, DWORD& dwWritten) { LONG dwHighFileOffset = 0; DWORD dwResult = 0; BOOL fResult = FALSE; OVERLAPPED ol;
if (EnsureOpen() && SetFilePointerToEnd()) { fResult = WriteFile(hFile, pbData, dwDataSize, &dwWritten, NULL); if (!fResult) { dwResult = GetLastError(); } }
return fResult; } };
//
// Append from stdin
//
BOOL AppendStdIn(CTargetFile& Target) { HANDLE hStdInput; DWORD dwReadChars, dwWritten; vector<BYTE> vbDataBlob; vector<BYTE> vbDataBlobIntermediate;
vbDataBlobIntermediate.resize(4096); hStdInput = GetStdHandle(STD_INPUT_HANDLE); if ((hStdInput == INVALID_HANDLE_VALUE) || !hStdInput) { return FALSE; } while (ReadFile(hStdInput, &vbDataBlobIntermediate.front(), vbDataBlobIntermediate.size(), &dwReadChars, NULL)) { if (!dwReadChars) { break; } else { if (g_BufferStdInFully) { vbDataBlob.insert( vbDataBlob.end(), vbDataBlobIntermediate.begin(), vbDataBlobIntermediate.end()); } else { if (!Target.AppendData(&vbDataBlobIntermediate.front(), dwReadChars, dwWritten)) { return FALSE; } } } }
if ((dwReadChars == 0) && g_BufferStdInFully) { if (!Target.AppendData(&vbDataBlob.front(), vbDataBlob.size(), dwWritten)) { return FALSE; } }
return (dwReadChars == 0); }
vector< wstring > AppendingSources;
BOOL WorkHorse(wstring& wsTargetFile) { BOOL bOk = FALSE; bool bHasReadStdIn = false; CTargetFile TargetFile(wsTargetFile);
if (!g_LockPerItem) TargetFile.EnsureOpen();
for (vector<wstring>::const_iterator i = AppendingSources.begin(); i != AppendingSources.end(); i++) { if (*i == L"-") { if (g_LockPerItem) TargetFile.EnsureOpen();
if (!bHasReadStdIn) { bHasReadStdIn = true; if (!AppendStdIn(TargetFile)) { goto Exit; } } else { fwprintf(stderr, L"Can't read from stdin multiple times - found '-' twice!\n"); SetLastError(ERROR_INVALID_PARAMETER); goto Exit; }
if (g_LockPerItem) TargetFile.CloseFile(); } else { fwprintf(stderr, L"We don't support reading in files yet, sorry.\n"); SetLastError(ERROR_INVALID_PARAMETER); goto Exit; } } bOk = TRUE;
Exit: return bOk;
}
HANDLE ObtainFileHandle(wstring& wsFileName) { HANDLE hFile = INVALID_HANDLE_VALUE; DWORD dwSleepTime = 250; // Start at 250ms sleep
float flBackoffRate = 1.1f; // Backoff at 1.1x the sleep length
DWORD dwMaxSleepTime = 5000; // Don't ever sleep for more than 5 seconds at a time
DWORD dwMaxTicksAtThisSleepTime = 10; // Try the sleep time 10 times in a row
DWORD dwTicksAtThisSleepTime = dwMaxTicksAtThisSleepTime; DWORD dwError = 0;
//
// We attempt to lock the file based on no sharing. If it fails with a sharing
// violation, then we back off for a while and try again later.
//
while (true) { hFile = CreateFileW( wsFileName.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
dwError = ::GetLastError();
// If we've gotten a good handle back, stop looking.
if ((hFile != INVALID_HANDLE_VALUE) && (hFile != NULL)) { break; } else { // If the error was a sharing violation, then back off for a bit and
// try again.
if (dwError == ERROR_SHARING_VIOLATION) { Sleep(dwSleepTime); if (dwTicksAtThisSleepTime == 0) { dwTicksAtThisSleepTime = dwMaxTicksAtThisSleepTime; dwSleepTime = (DWORD)((float)dwSleepTime * flBackoffRate); continue; } } // Otherwise, something else bad happened, so quit trying
else { hFile = INVALID_HANDLE_VALUE; break; } } }
return hFile;
}
void PrintUsage() { wprintf(L"<-file <output>> [-verbose] [-bufferstdin] [-atomicperitem] <[-] [file [...]]>\n"); wprintf(L"\n"); wprintf(L"-bufferstdin Buffer all of stdin (-) before writing\n"); wprintf(L"-atomicperitem Lock the file per item, not per run\n"); wprintf(L"-file <name> What file should be written out to\n"); wprintf(L"-verbose Up the verbosity of debug spew (if any)\n"); wprintf(L"- Read from stdin into the output file.\n"); }
int __cdecl wmain(int argc, WCHAR** argv) { wstring wsAppendTarget; vector<wstring> wstParams; bool bInGatheringData = false; DWORD dwVerbosity = 0; HANDLE hFile = INVALID_HANDLE_VALUE;
for (int i = 1; i < argc; i++) { wstParams.push_back(argv[i]); }
if (wstParams.empty()) { PrintUsage(); return 1; }
//
// Syntax:
//
// <-file <output>> [-verbose] [-] file1 file2 ...
//
// - - Indicates that the console should be read for input at this point
// -file <output file> - Specify output destination
// -verbose - How noisy? +1 noise level per instance
//
for (vector<wstring>::iterator i = wstParams.begin(); i != wstParams.end(); i++) { if (bInGatheringData) { AppendingSources.push_back(*i); } else if (*i == wstring(L"-file")) { wsAppendTarget = *++i; } else if (*i == wstring(L"-?")) { PrintUsage(); return 1; } else if (*i == wstring(L"-bufferstdin")) { g_BufferStdInFully = true; } else if (*i == wstring(L"-atomicperitem")) { g_LockPerItem = true; } else if (*i == L"-verbose") { dwVerbosity++; } else { bInGatheringData = true; AppendingSources.push_back(*i); } }
//
// No target or sources?
//
if ((wsAppendTarget.size() == 0) || (AppendingSources.size() == 0)) { PrintUsage(); return 1; }
WorkHorse(wsAppendTarget); }
|