Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
599
Src/nde/win/Vfs.cpp
Normal file
599
Src/nde/win/Vfs.cpp
Normal file
|
@ -0,0 +1,599 @@
|
|||
/* ---------------------------------------------------------------------------
|
||||
Nullsoft Database Engine
|
||||
--------------------
|
||||
codename: Near Death Experience
|
||||
--------------------------------------------------------------------------- */
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
|
||||
Virtual File System
|
||||
|
||||
--------------------------------------------------------------------------- */
|
||||
#include "../nde.h"
|
||||
|
||||
#include "vfs.h"
|
||||
#include <malloc.h>
|
||||
#ifndef EOF
|
||||
#define EOF -1
|
||||
#endif
|
||||
#include <Sddl.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#if defined(NDE_ALLOW_NONCACHED)
|
||||
size_t ReadFileN(void *buffer, size_t size, VFILE *f)
|
||||
{
|
||||
uint8_t *b = (uint8_t *) buffer;
|
||||
size_t total_size=0;
|
||||
while (size)
|
||||
{
|
||||
DWORD bytesRead = 0;
|
||||
DWORD toRead = min(0xffffffffUL, size);
|
||||
ReadFile(f->hfile, b, toRead, &bytesRead, NULL);
|
||||
if (bytesRead != toRead)
|
||||
{
|
||||
f->endoffile=true;
|
||||
// TODO: rewind
|
||||
return total_size+bytesRead;
|
||||
}
|
||||
size-=toRead;
|
||||
b+=toRead;
|
||||
total_size+=toRead;
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
size_t WriteFileN(void *buffer, size_t size, VFILE *f)
|
||||
{
|
||||
uint8_t *b = (uint8_t *) buffer;
|
||||
size_t total_size=0;
|
||||
while (size)
|
||||
{
|
||||
DWORD bytesRead = 0;
|
||||
DWORD toRead = min(0xffffffffUL, size);
|
||||
WriteFile(f->hfile, b, toRead, &bytesRead, NULL);
|
||||
if (bytesRead != toRead)
|
||||
{
|
||||
f->endoffile=true;
|
||||
// TODO: rewind
|
||||
return total_size+bytesRead;
|
||||
}
|
||||
size-=toRead;
|
||||
b+=toRead;
|
||||
total_size+=toRead;
|
||||
}
|
||||
|
||||
return total_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
// benski> i havn't the slightest fucking clue why this works, it's copypasta code from the internets
|
||||
static LPCWSTR LOW_INTEGRITY_SDDL_SACL_W = L"S:(ML;;NW;;;LW)";
|
||||
static bool GetLowIntegrity(SECURITY_ATTRIBUTES *attributes)
|
||||
{
|
||||
PSECURITY_DESCRIPTOR pSD = NULL;
|
||||
|
||||
if ( ConvertStringSecurityDescriptorToSecurityDescriptorW (
|
||||
LOW_INTEGRITY_SDDL_SACL_W, SDDL_REVISION_1, &pSD, NULL ) )
|
||||
{
|
||||
attributes->nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
attributes->lpSecurityDescriptor = pSD;
|
||||
attributes->bInheritHandle = FALSE;
|
||||
|
||||
//LocalFree ( pSD );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
VFILE *Vfnew(const wchar_t *fl, const char *mode, BOOL Cached)
|
||||
{
|
||||
if (!fl) return NULL;
|
||||
VFILE *f = (VFILE *)calloc(1, sizeof(VFILE));
|
||||
if (!f) return NULL;
|
||||
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
if (!Cached)
|
||||
{
|
||||
f->r.reserve(256); // heuristically determined
|
||||
}
|
||||
f->hfile = INVALID_HANDLE_VALUE;
|
||||
#endif
|
||||
|
||||
#ifndef NO_TABLE_WIN32_LOCKING
|
||||
// TODO: should we retrieve a better filename, e.g. GetLongPathName, or GetFinalPathNameByHandle on vista+
|
||||
wchar_t mutex_name[1024] = {0};
|
||||
StringCbPrintfW(mutex_name, sizeof(mutex_name), L"Global\\nde-%s", fl);
|
||||
|
||||
CharLowerW(mutex_name+7);
|
||||
wchar_t *sw = mutex_name+7;
|
||||
wchar_t *has_extension=0;
|
||||
while (sw && *sw)
|
||||
{
|
||||
if (*sw == L'\\')
|
||||
{
|
||||
has_extension=0;
|
||||
*sw = L'/';
|
||||
}
|
||||
else if (*sw == L'.')
|
||||
has_extension=sw;
|
||||
sw++;
|
||||
}
|
||||
if (has_extension)
|
||||
*has_extension = 0;
|
||||
|
||||
SECURITY_ATTRIBUTES attr = {0};
|
||||
if (GetLowIntegrity(&attr))
|
||||
{
|
||||
f->mutex = CreateMutexW(&attr, FALSE, mutex_name);
|
||||
LocalFree(attr.lpSecurityDescriptor);
|
||||
}
|
||||
else
|
||||
f->mutex = CreateMutexW(0, FALSE, mutex_name);
|
||||
|
||||
#endif
|
||||
return f;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
VFILE *Vfopen(VFILE *f, wchar_t *fl, const char *mode, BOOL Cached)
|
||||
{
|
||||
if (!fl) return NULL;
|
||||
if (!f)
|
||||
{
|
||||
f = Vfnew(fl, mode, Cached);
|
||||
if (!f)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
f->cached = Cached;
|
||||
#else
|
||||
f->cached = TRUE;
|
||||
#endif
|
||||
|
||||
if (!strchr(mode, '+'))
|
||||
{
|
||||
if (strchr(mode, 'r'))
|
||||
f->mode = VFS_READ | VFS_MUSTEXIST;
|
||||
if (strchr(mode, 'w'))
|
||||
f->mode = VFS_WRITE | VFS_CREATE | VFS_NEWCONTENT;
|
||||
if (strchr(mode, 'a'))
|
||||
f->mode = VFS_WRITE | VFS_CREATE | VFS_SEEKEOF;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strstr(mode, "r+"))
|
||||
f->mode = VFS_WRITE | VFS_MUSTEXIST;
|
||||
if (strstr(mode, "w+"))
|
||||
f->mode = VFS_WRITE | VFS_CREATE | VFS_NEWCONTENT;
|
||||
if (strstr(mode, "a+"))
|
||||
f->mode = VFS_WRITE | VFS_CREATE | VFS_SEEKEOF;
|
||||
}
|
||||
|
||||
if (f->mode == 0 || ((f->mode & VFS_READ) && (f->mode & VFS_WRITE)))
|
||||
{
|
||||
Vfdestroy(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
if (!f->cached)
|
||||
{
|
||||
f->endoffile=false;
|
||||
int readFlags=GENERIC_READ, openFlags=0;
|
||||
if (f->mode & VFS_WRITE) readFlags|=GENERIC_WRITE;
|
||||
if (f->mode & VFS_MUSTEXIST) openFlags=OPEN_EXISTING;
|
||||
if (f->mode & VFS_CREATE) openFlags = OPEN_ALWAYS;
|
||||
if (f->mode & VFS_NEWCONTENT) openFlags = CREATE_ALWAYS;
|
||||
f->hfile=CreateFile(fl,readFlags,FILE_SHARE_READ,0,openFlags,0,0);
|
||||
if (f->hfile!=INVALID_HANDLE_VALUE)
|
||||
f->filename = _strdup(fl);
|
||||
else
|
||||
{
|
||||
Vfdestroy(f);
|
||||
return NULL;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (f->mode & VFS_MUSTEXIST)
|
||||
{
|
||||
if (GetFileAttributesW(fl) == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
Vfdestroy(f);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(f->mode & VFS_NEWCONTENT))
|
||||
{
|
||||
int attempts=0;
|
||||
HANDLE hFile=INVALID_HANDLE_VALUE;
|
||||
again:
|
||||
if (attempts<100) // we'll try for 10 seconds
|
||||
{
|
||||
hFile=CreateFileW(fl,GENERIC_READ,FILE_SHARE_READ/*|FILE_SHARE_WRITE*/,0,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,0);
|
||||
if (hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_SHARING_VIOLATION)
|
||||
{
|
||||
Sleep(100); // let's try again
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
else if (hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_SHARING_VIOLATION)
|
||||
{
|
||||
// screwed up STILL? eeergh I bet it's another program locking it, let's try with more sharing flags
|
||||
hFile=CreateFileW(fl,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,0);
|
||||
}
|
||||
|
||||
if (hFile==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
f->data = (uint8_t *)calloc(VFILE_INC, 1);
|
||||
if (f->data == NULL)
|
||||
{
|
||||
Vfdestroy(f);
|
||||
return NULL;
|
||||
}
|
||||
f->filesize = 0;
|
||||
f->maxsize = VFILE_INC;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t fsize_ret_value=GetFileSize(hFile,NULL);
|
||||
if (fsize_ret_value==INVALID_FILE_SIZE)
|
||||
{
|
||||
Vfdestroy(f);
|
||||
return NULL;
|
||||
}
|
||||
f->filesize = (uint32_t)fsize_ret_value;
|
||||
f->data = (uint8_t *)calloc(f->filesize, 1);
|
||||
if (f->data == NULL)
|
||||
{
|
||||
CloseHandle(hFile);
|
||||
Vfdestroy(f);
|
||||
return NULL;
|
||||
}
|
||||
f->maxsize = f->filesize;
|
||||
DWORD r = 0;
|
||||
// TODO: benski> I think we should switch this to overlapped I/O (to allow I/O to happen as we're parsing)
|
||||
// or switch to a memory mapped file... but we'll need to check with the profiler
|
||||
if (!ReadFile(hFile,f->data,f->filesize,&r,NULL) || r != f->filesize)
|
||||
{
|
||||
CloseHandle(hFile);
|
||||
Vfdestroy(f);
|
||||
return NULL;
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (f->mode & VFS_SEEKEOF)
|
||||
f->ptr = f->filesize;
|
||||
|
||||
f->filename = fl;
|
||||
ndestring_retain(f->filename);
|
||||
return f;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Vfclose(VFILE *f)
|
||||
{
|
||||
if (!f) return;
|
||||
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
if (!f->cached)
|
||||
{
|
||||
if (f->hfile!=INVALID_HANDLE_VALUE)
|
||||
CloseHandle(f->hfile);
|
||||
f->hfile=INVALID_HANDLE_VALUE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (f->mode & VFS_WRITE)
|
||||
{
|
||||
Vsync(f);
|
||||
}
|
||||
}
|
||||
|
||||
ndestring_release(f->filename);
|
||||
f->filename=0;
|
||||
free(f->data);
|
||||
f->data = 0;
|
||||
f->ptr=0;
|
||||
f->filesize=0;
|
||||
f->maxsize=0;
|
||||
f->dirty=0;
|
||||
}
|
||||
|
||||
void Vfdestroy(VFILE *f)
|
||||
{
|
||||
// benski> TODO:
|
||||
if (f)
|
||||
{
|
||||
Vfclose(f);
|
||||
|
||||
while (f->locks)
|
||||
Vfunlock(f, 1);
|
||||
|
||||
if (f->mutex) CloseHandle(f->mutex);
|
||||
free(f);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
size_t Vfread( void *ptr, size_t size, VFILE *f )
|
||||
{
|
||||
assert( ptr && f );
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
if ( !f->cached )
|
||||
{
|
||||
size_t read = f->r.read( ptr, size );
|
||||
ptr = (uint8_t *)ptr + read;
|
||||
size -= read;
|
||||
if ( size == 0 ) return 1; // yay fully buffered read
|
||||
// if we got here, the ring buffer is empty
|
||||
f->r.clear(); // reset back to normal
|
||||
if ( size > f->r.avail() )
|
||||
{
|
||||
return ReadFileN( ptr, size, f ) == size;
|
||||
}
|
||||
void *data = f->r.LockBuffer();
|
||||
size_t bytes_read = ReadFileN( data, f->r.avail(), f );
|
||||
f->r.UnlockBuffer( bytes_read );
|
||||
read = f->r.read( ptr, size );
|
||||
return read == size;
|
||||
}
|
||||
#endif
|
||||
//if (!size) return 0;
|
||||
if ( size + f->ptr > f->filesize )
|
||||
{
|
||||
//FUCKO: remove this
|
||||
if ( !( f->ptr < f->filesize ) )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
char buf[ 128 ] = { 0 };
|
||||
StringCbPrintfA( buf, sizeof( buf ), "NDE/VFS: VFS read at %d/%d (%d bytes) is bad\n", f->ptr, f->filesize, size );
|
||||
OutputDebugStringA( buf );
|
||||
#endif
|
||||
|
||||
// if (!f->flushtable) // this would be ideal, if we could figure out f->flushtable
|
||||
// f->flushtable=MessageBox(g_hwnd,"DB read failed, DB may be corrupted.\r\n\r\n"
|
||||
// "Hit Retry to continue, or Cancel to clear the DB and start over","Winamp Library Error",MB_RETRYCANCEL) == IDCANCEL; //fucko
|
||||
//MessageBox(g_hwnd,"DB read failed, DB may be corrupted. If this error persists, remove all files from the library.",
|
||||
// "Winamp Library Error",MB_OK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = f->filesize - f->ptr;
|
||||
}
|
||||
|
||||
memcpy( ptr, f->data + f->ptr, size );
|
||||
f->ptr += (uint32_t)size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Vfwrite(const void *ptr, size_t size, VFILE *f)
|
||||
{
|
||||
if (!ptr || !f) return;
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
if (!f->cached)
|
||||
{
|
||||
// TODO: with some cleverness we might be able to make this write to the read cache
|
||||
|
||||
// if we're cached, then our file position is off and we need to adjust
|
||||
if (!f->r.empty())
|
||||
Vfseek(f, -(f->r.size()), SEEK_CUR);
|
||||
|
||||
f->r.clear();
|
||||
WriteFileN(ptr, size, f);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
f->dirty=1;
|
||||
size_t s = (size);
|
||||
if (s + f->ptr > f->maxsize)
|
||||
{
|
||||
// grow f->data,f->maxsize to be (s + f->ptr + VFILE_INC-1)&~(VFILE_INC-1)
|
||||
// instead of calling Vgrow again which gets kinda slow
|
||||
size_t newsize=(s + f->ptr + VFILE_INC-1)&~(VFILE_INC-1);
|
||||
uint8_t *newdata=(uint8_t *)realloc(f->data,newsize);
|
||||
if (newdata == NULL) return;
|
||||
f->data = newdata;
|
||||
memset(f->data+f->maxsize,0,newsize-f->maxsize);
|
||||
f->maxsize=(uint32_t)newsize;
|
||||
}
|
||||
memcpy(f->data + f->ptr, ptr, s);
|
||||
f->ptr += (uint32_t)s;
|
||||
if (f->ptr > f->filesize)
|
||||
f->filesize = f->ptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Vgrow(VFILE *f)
|
||||
{
|
||||
if (!f) return;
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
if (!f->cached) return;
|
||||
#endif
|
||||
uint8_t *newdata=(uint8_t *)realloc(f->data, f->maxsize + VFILE_INC);
|
||||
if (newdata == NULL) return;
|
||||
f->data = newdata;
|
||||
f->maxsize += VFILE_INC;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
uint32_t Vftell(VFILE *f)
|
||||
{
|
||||
if (!f) return (unsigned)-1;
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
if (!f->cached)
|
||||
{
|
||||
return SetFilePointer(f->hfile, 0, NULL, FILE_CURRENT);
|
||||
}
|
||||
#endif
|
||||
return f->ptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Vfseek(VFILE *f, uint32_t i, int whence)
|
||||
{
|
||||
if (!f) return;
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
if (!f->cached)
|
||||
{
|
||||
if (whence == SEEK_CUR && i > 0 && i <f->r.size())
|
||||
{
|
||||
f->r.advance(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
f->r.clear();
|
||||
SetFilePointer(f->hfile, i, NULL, whence);
|
||||
f->endoffile = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
f->ptr = i;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
f->ptr += i;
|
||||
break;
|
||||
case SEEK_END:
|
||||
f->ptr = f->filesize+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Vfeof(VFILE *f)
|
||||
{
|
||||
if (!f) return -1;
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
if (!f->cached)
|
||||
{
|
||||
return !!f->endoffile;
|
||||
}
|
||||
#endif
|
||||
return (f->ptr >= f->filesize);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Vsync(VFILE *f)
|
||||
{
|
||||
if (!f) return 0;
|
||||
if (!f->dirty) return 0;
|
||||
|
||||
if (f->mode & VFS_WRITE)
|
||||
{
|
||||
#ifdef NDE_ALLOW_NONCACHED
|
||||
if (!f->cached)
|
||||
{
|
||||
LONG p = SetFilePointer(f->hfile, 0, NULL, FILE_CURRENT);
|
||||
CloseHandle(f->hfile);
|
||||
f->hfile = CreateFileW(f->filename,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,NULL);
|
||||
if (f->hfile == INVALID_HANDLE_VALUE)
|
||||
return 1;
|
||||
SetFilePointer(f->hfile, p, NULL, SEEK_SET);
|
||||
f->endoffile=false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
wchar_t newfn[MAX_PATH] = {0};
|
||||
wchar_t oldfn[MAX_PATH] = {0};
|
||||
DWORD mypid=GetCurrentProcessId();
|
||||
|
||||
StringCchPrintfW(newfn, MAX_PATH, L"%s.n3w%08X",f->filename,mypid);
|
||||
StringCchPrintfW(oldfn, MAX_PATH, L"%s.o1d%08X",f->filename,mypid);
|
||||
|
||||
DeleteFileW(newfn);
|
||||
DeleteFileW(oldfn);
|
||||
|
||||
HANDLE hFile = CreateFileW(newfn,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,NULL);
|
||||
int success=0;
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD o = 0;
|
||||
if (WriteFile(hFile,f->data,f->filesize,&o,NULL) && o == f->filesize) success++;
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
DeleteFileW(newfn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO use this to keep a backup of the database file for edit fails, etc
|
||||
if (MoveFileW(f->filename,oldfn) == 0) // if the function fails
|
||||
{
|
||||
CopyFileW(f->filename,oldfn, FALSE);
|
||||
DeleteFileW(f->filename);
|
||||
}
|
||||
|
||||
int rv=0;
|
||||
if (MoveFileW(newfn,f->filename) == 0 && CopyFileW(newfn,f->filename, FALSE) == 0)
|
||||
{
|
||||
MoveFileW(oldfn,f->filename); // restore old file
|
||||
rv=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->dirty=0;
|
||||
}
|
||||
|
||||
// clean up our temp files
|
||||
DeleteFileW(oldfn);
|
||||
DeleteFileW(newfn);
|
||||
|
||||
return rv;
|
||||
}
|
||||
f->dirty=0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// returns 0 on failure
|
||||
int Vflock(VFILE *fl, BOOL is_sync)
|
||||
{
|
||||
#ifndef NO_TABLE_WIN32_LOCKING
|
||||
if (!fl) return 0;
|
||||
if (!is_sync && fl->cached)
|
||||
return 1;
|
||||
// try for 10 seconds
|
||||
if (fl->locks++ == 0)
|
||||
{
|
||||
if (WaitForSingleObject(fl->mutex, 10000) != WAIT_OBJECT_0)
|
||||
{
|
||||
fl->locks--;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Vfunlock(VFILE *fl, BOOL is_sync)
|
||||
{
|
||||
#ifndef NO_TABLE_WIN32_LOCKING
|
||||
if (!is_sync && fl->cached)
|
||||
return;
|
||||
|
||||
if (fl && fl->locks == 0)
|
||||
DebugBreak();
|
||||
if (--fl->locks == 0)
|
||||
{
|
||||
if (fl && fl->mutex)
|
||||
{
|
||||
ReleaseMutex(fl->mutex);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue