#include "assertive.h"
#include <climits>
#include <cstdint>
#include <cstddef>
#include <cstdarg>
#include <cstring>
#include <cstdlib>
#include <wchar.h>
#include <new>
#include <cstdio>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "ss.h"
#include "files.h"
#include "include/pfmapi.h"
#include "include/pfmmarshaller.h"
#include "avfspfm.h"
#include "../common/vsutf16.h"
#include "../../src/core/version.h"
#include <VSScript4.h>
#define CCALL __cdecl
struct Volume;
enum {
fileTypeAny = 0,
fileTypeFolder = 1,
fileTypeScript = 2,
fileTypeLog = 3,
fileTypeMedia = 4,
maxScriptDataSize = 10*1000000,
maxLogDataSize = 250*1000000, };
struct ListState
{
Volume* volume;
ListState** prevList;
ListState* nextList;
int64_t listId;
size_t position;
ListState(Volume* volume,int64_t listId);
~ListState(void);
};
struct FileNode
{
Volume* volume;
FileNode** prevFile;
FileNode* nextFile;
wchar_t* name;
int64_t openId;
int64_t openSequence;
int64_t fileId;
bool open;
int8_t fileType;
int64_t writeTime;
uint64_t fileSize;
size_t maxFileDataSize;
void* fileData;
AvfsMediaFile_* mediaFile;
FileNode(Volume* volume,int64_t openId,int8_t fileType,int64_t writeTime);
~FileNode(void);
};
struct Volume final:
AvfsLog_,
AvfsVolume_,
PfmFormatterDispatch
{
PfmMarshaller* marshaller;
wchar_t* scriptFileName;
wchar_t* scriptFolderName;
const wchar_t* scriptEndName;
wchar_t* mediaName;
bool scriptWritable;
int64_t scriptWriteTime;
FileNode* scriptFile;
FileNode* logFile;
ListState* firstList;
FileNode* firstFile;
int64_t lastFileId;
void Print(const wchar_t* data);
void Vprintf(const wchar_t* format,va_list args);
void Printf(const wchar_t* format,...);
void Line(const wchar_t* data);
ListState* ListFind(int64_t listId);
int FileFindName(const PfmNamePart* nameParts,size_t namePartCount,FileNode** file);
int FileFindOpenId(int64_t openId,bool forModify,FileNode** file);
void FileOpened(FileNode* file,PfmOpenAttribs* openAttribs);
int FileFactory(int64_t openId,int8_t createFileType,int64_t writeTime,FileNode** outFile);
void FileTouchParent(FileNode* file,int64_t writeTime);
int FileOpenOrMove(const PfmNamePart* nameParts,size_t namePartCount,FileNode* sourceFile,int64_t newExistingOpenId,bool deleteSource,int notFoundError,int64_t writeTime,FileNode** outFile,bool* existed);
void FileDelete(FileNode* file,int64_t writeTime);
int FileReplace(FileNode* sourceFile,FileNode* targetFile,bool deleteSource,int64_t writeTime);
int FileWrite(FileNode* file,size_t maxFileDataSize,uint64_t fileOffset,const void* buffer,size_t requestedSize,size_t* actualSize);
int64_t FileParentId(FileNode* file)
{ return file?firstFile->fileId:0; }
const wchar_t* FileEndName(FileNode* file)
{ return file?file->name:0; }
void DeleteMediaFiles(int64_t writeTime);
void ProcessScript(void);
const wchar_t* GetScriptFileName(void);
const wchar_t* GetScriptEndName(void);
const wchar_t* GetMediaName(void);
const char* GetScriptData(void);
size_t GetScriptDataSize(void);
void CreateMediaFile(AvfsMediaFile_* mediaFile,const wchar_t* fileName,uint64_t fileSize);
void CCALL Open(PfmMarshallerOpenOp*,void*);
void CCALL Replace(PfmMarshallerReplaceOp*,void*);
void CCALL Move(PfmMarshallerMoveOp*,void*);
void CCALL MoveReplace(PfmMarshallerMoveReplaceOp*,void*);
void CCALL Delete(PfmMarshallerDeleteOp*,void*);
void CCALL Close(PfmMarshallerCloseOp*,void*);
void CCALL FlushFile(PfmMarshallerFlushFileOp*,void*);
void CCALL List(PfmMarshallerListOp*,void*);
void CCALL ListEnd(PfmMarshallerListEndOp*,void*);
void CCALL Read(PfmMarshallerReadOp*,void*);
void CCALL Write(PfmMarshallerWriteOp*,void*);
void CCALL SetSize(PfmMarshallerSetSizeOp*,void*);
void CCALL Capacity(PfmMarshallerCapacityOp*,void*);
void CCALL FlushMedia(PfmMarshallerFlushMediaOp*,void*);
void CCALL Control(PfmMarshallerControlOp*,void*);
void CCALL MediaInfo(PfmMarshallerMediaInfoOp*,void*);
void CCALL Access(PfmMarshallerAccessOp*,void*);
void CCALL ReadXattr(PfmMarshallerReadXattrOp*,void*);
void CCALL WriteXattr(PfmMarshallerWriteXattrOp*,void*);
Volume(void);
~Volume(void);
int Init(const wchar_t* scriptFileName);
void Destroy(void);
};
ListState::ListState(
Volume* inVolume,
int64_t inListId)
{
volume = inVolume;
prevList = &(volume->firstList);
while(*prevList)
{
prevList= &((*prevList)->nextList);
}
*prevList = this;
nextList = 0;
listId = inListId;
position = 0;
}
ListState::~ListState(void)
{
if(prevList)
{
ASSERT(*prevList == this);
*prevList = nextList;
if(nextList)
{
ASSERT(nextList->prevList == &nextList);
nextList->prevList = prevList;
}
}
}
FileNode::FileNode(
Volume* inVolume,
int64_t inOpenId,
int8_t inFileType,
int64_t inWriteTime)
{
volume = inVolume;
prevFile = &(volume->firstFile);
while(*prevFile)
{
prevFile= &((*prevFile)->nextFile);
}
*prevFile = this;
nextFile = 0;
name = 0;
openId = inOpenId;
openSequence = 0;
fileId = ++volume->lastFileId;
open = false;
fileType = inFileType;
writeTime = inWriteTime;
fileSize = 0;
maxFileDataSize = 0;
fileData = 0;
mediaFile = 0;
}
FileNode::~FileNode(void)
{
if(prevFile)
{
ASSERT(*prevFile == this);
*prevFile = nextFile;
if(nextFile)
{
ASSERT(nextFile->prevFile == &nextFile);
nextFile->prevFile = prevFile;
}
}
free(name);
if(fileData)
{
free(fileData);
}
if(mediaFile)
{
mediaFile->Release();
}
}
void Volume::Print(const wchar_t* data)
{
marshaller->Print(data);
std::string data8 = utf16_to_utf8(data);
if(!data8.empty())
{
FileWrite(logFile,maxLogDataSize,logFile->fileSize,data8.data(),
data8.size(),0);
}
}
void Volume::Vprintf(const wchar_t* format,va_list args)
{
marshaller->Vprintf(format,args);
wchar_t* data = ssvformatalloc(format,args);
Print(data);
free(data);
}
void Volume::Printf(const wchar_t* format,...)
{
va_list args;
va_start(args,format);
Vprintf(format,args);
}
void Volume::Line(const wchar_t* data)
{
Printf(L"%s\r\n",data);
}
ListState* Volume::ListFind(
int64_t listId)
{
ListState* list = firstList;
while(list && list->listId != listId)
{
list = list->nextList;
}
return list;
}
int Volume::FileFindName(
const PfmNamePart* nameParts,
size_t namePartCount,
FileNode** outFile)
{
int error = pfmErrorParentNotFound;
FileNode* file = 0;
const wchar_t* name = L".";
if(namePartCount < 2)
{
error = pfmErrorNotFound;
if(namePartCount == 1)
{
name = nameParts[0].name;
}
file = firstFile;
while(file && sscmpi(file->name,name) != 0)
{
file = file->nextFile;
}
if(file)
{
error = 0;
}
}
*outFile = file;
return error;
}
int Volume::FileFindOpenId(
int64_t openId,
bool forModify,
FileNode** outFile)
{
int error = 0;
FileNode* file = firstFile;
while(file && (!file->open || file->openId != openId))
{
file = file->nextFile;
}
if(!file)
{
error = pfmErrorNotFound;
}
else if(forModify && (!scriptWritable ||
file->fileType != fileTypeScript))
{
error = pfmErrorAccessDenied;
}
*outFile = file;
return error;
}
void Volume::FileOpened(
FileNode* file,
PfmOpenAttribs* openAttribs)
{
ASSERT(file);
file->open = true;
openAttribs->openId = file->openId;
openAttribs->openSequence = ++(file->openSequence);
{
openAttribs->accessLevel = pfmAccessLevelWriteData;
}
openAttribs->attribs.fileType = pfmFileTypeFile;
if(file->fileType == fileTypeFolder)
{
openAttribs->attribs.fileType = pfmFileTypeFolder;
}
openAttribs->attribs.fileSize = file->fileSize;
openAttribs->attribs.writeTime = file->writeTime;
if(file->fileType == fileTypeMedia)
{
openAttribs->attribs.extraFlags |= pfmExtraFlagOffline;
}
}
int Volume::FileFactory(
int64_t openId,
int8_t createFileType,
int64_t writeTime,
FileNode** outFile)
{
FileNode* file = 0;
int error = pfmErrorAccessDenied;
switch(createFileType)
{
case pfmFileTypeFile:
error = pfmErrorAccessDenied;
if(scriptWritable)
{
error = pfmErrorOutOfMemory;
file = new(std::nothrow) FileNode(this,openId,fileTypeScript,writeTime);
if(file)
{
error = 0;
}
}
break;
case pfmFileTypeNone:
error = pfmErrorNotFound;
break;
}
*outFile = file;
return error;
}
void Volume::FileTouchParent(FileNode* file,int64_t writeTime)
{
firstFile->writeTime = writeTime;
}
int Volume::FileOpenOrMove(
const PfmNamePart* nameParts,
size_t namePartCount,
FileNode* sourceFile,
int64_t newExistingOpenId,
bool deleteSource,
int notFoundError,
int64_t writeTime,
FileNode** outFile,
bool* outExisted)
{
int perr;
FileNode* file;
bool existed = false;
wchar_t* name;
perr = FileFindName(nameParts,namePartCount,&file);
if(!perr)
{
ASSERT(file);
existed = true;
if(!file->openId)
{
file->openId = newExistingOpenId;
}
}
else if(perr == pfmErrorNotFound)
{
ASSERT(namePartCount == 1);
if(notFoundError)
{
perr = notFoundError;
}
if(sourceFile)
{
ASSERT(sourceFile->openId);
perr = pfmErrorInvalid;
if(deleteSource || !sourceFile->name)
{
perr = pfmErrorOutOfMemory;
name = ssdup(nameParts[0].name);
if(name)
{
perr = 0;
free(sourceFile->name);
sourceFile->name = name;
file = sourceFile;
FileTouchParent(file,writeTime);
}
}
}
}
if(outFile)
{
*outFile = file;
}
if(outExisted)
{
*outExisted = existed;
}
return perr;
}
void Volume::FileDelete(FileNode* file,int64_t writeTime)
{
if(file)
{
if(file->name)
{
free(file->name);
file->name = 0;
FileTouchParent(file,writeTime);
}
if(!file->open)
{
delete file;
}
}
}
int Volume::FileReplace(
FileNode* sourceFile,
FileNode* targetFile,
bool deleteSource,
int64_t writeTime)
{
int perr = 0;
if(sourceFile->fileType != fileTypeScript ||
targetFile->fileType != fileTypeScript)
{
perr = pfmErrorAccessDenied;
}
else if(!targetFile->name || (!deleteSource && sourceFile->name))
{
perr = pfmErrorInvalid;
}
if(!perr)
{
free(sourceFile->name);
sourceFile->name = targetFile->name;
targetFile->name = 0;
FileTouchParent(sourceFile,writeTime);
}
return perr;
}
int Volume::FileWrite(
FileNode* file,
size_t maxFileDataSize,
uint64_t fileOffset,
const void* buffer,
size_t requestedSize,
size_t* outActualSize)
{
int error = 0;
size_t actualSize = 0;
size_t endOffset;
size_t newMaxFileDataSize;
void* newFileData;
if(fileOffset > maxFileDataSize)
{
error = pfmErrorNoSpace;
}
else
{
actualSize = maxFileDataSize-
static_cast<size_t>(fileOffset);
}
if(!error)
{
if(actualSize > requestedSize)
{
actualSize = requestedSize;
}
endOffset = static_cast<size_t>(fileOffset)+actualSize;
if(endOffset > file->maxFileDataSize)
{
newMaxFileDataSize = endOffset+endOffset/4+1000;
newFileData = malloc(newMaxFileDataSize);
if(!newFileData)
{
error = pfmErrorOutOfMemory;
}
else
{
if(file->fileData)
{
if(file->fileSize)
{
memcpy(newFileData,file->fileData,
static_cast<size_t>(file->fileSize));
}
free(file->fileData);
}
file->maxFileDataSize = newMaxFileDataSize;
file->fileData = newFileData;
}
}
if(!error)
{
memcpy(static_cast<uint8_t*>(file->fileData)+
static_cast<size_t>(fileOffset),buffer,actualSize);
if(endOffset > file->fileSize)
{
file->fileSize = endOffset;
}
}
}
if(outActualSize)
{
*outActualSize = actualSize;
}
return error;
}
void Volume::DeleteMediaFiles(int64_t writeTime)
{
FileNode* file;
FileNode* next = firstFile;
while((file = next) != 0)
{
next = file->nextFile;
if(file->fileType == fileTypeMedia && file->name)
{
if(file->mediaFile)
{
file->mediaFile->Release();
file->mediaFile = 0;
}
FileDelete(file,writeTime);
}
}
}
void Volume::ProcessScript(void)
{
int error = 0;
HANDLE handle = INVALID_HANDLE_VALUE;
size_t transferredSize;
FileNode* newLogFile;
uint64_t fileSize;
size_t dataSize = 0;
FileNode* file;
int64_t writeTime;
file = firstFile;
while(file && (file->fileType != fileTypeScript ||
sscmpi(file->name,scriptEndName) != 0))
{
file = file->nextFile;
}
if(::FileGetTime2(scriptFileName,&writeTime) == 0 &&
writeTime != scriptWriteTime)
{
if(file)
{
FileDelete(file,writeTime);
}
file = new(std::nothrow) FileNode(this,0,fileTypeScript,writeTime);
if(!file)
{
error = ERROR_OUTOFMEMORY;
}
else
{
file->name = ssdup(scriptEndName);
if(!file->name)
{
delete file;
file = 0;
}
}
if(!error)
{
scriptWritable = true;
error = ::FileOpenWrite(scriptFileName,&handle);
if(error)
{
scriptWritable = false;
error = ::FileOpenRead(scriptFileName,&handle);
}
}
if(!error)
{
fileSize = ::FileGetSize(handle);
dataSize = 0;
if(fileSize <= maxScriptDataSize)
{
dataSize = static_cast<size_t>(fileSize);
}
free(file->fileData);
file->fileData = malloc(dataSize);
if(file->fileData)
{
memset(file->fileData,0,dataSize);
::FileSetPointer(handle,0);
::FileRead(handle,file->fileData,dataSize,0);
file->maxFileDataSize = dataSize;
file->fileSize = dataSize;
}
}
::FileCleanup(&handle);
}
else if(file && file->writeTime != scriptWriteTime)
{
error = ::FileOpenWrite(scriptFileName,&handle);
if(!error)
{
if(file->fileSize)
{
::FileWrite(handle,file->fileData,
static_cast<size_t>(file->fileSize),
&transferredSize);
}
::FileSetSize(handle,file->fileSize);
::FileSetTime(handle,file->writeTime);
::FileCleanup(&handle);
::FileGetTime2(scriptFileName,&file->writeTime);
}
::FileCleanup(&handle);
::FileGetTime2(scriptFolderName,&writeTime);
}
if(file && file->writeTime != scriptWriteTime)
{
scriptWriteTime = file->writeTime;
DeleteMediaFiles(writeTime);
newLogFile = new(std::nothrow) FileNode(this,0,fileTypeLog,
scriptWriteTime);
if(newLogFile)
{
newLogFile->name = ssdup(L"error.log");
if(!newLogFile->name)
{
delete newLogFile;
}
else
{
FileDelete(logFile,writeTime);
logFile = newLogFile;
}
}
scriptFile = file;
static HMODULE lib = LoadLibrary(L"VSScript.dll");
if ((!sscmpi(ssrchr(scriptFile->name, '.'), L".vpy") || !sscmpi(ssrchr(scriptFile->name, '.'), L".py")) && lib && getVSScriptAPI(VSSCRIPT_API_VERSION))
VsfsProcessScript(this, this);
else
AvfsProcessScript(this, this);
scriptFile = 0;
}
}
const wchar_t* Volume::GetScriptFileName(void)
{
return scriptFileName;
}
const wchar_t* Volume::GetScriptEndName(void)
{
return scriptEndName;
}
const wchar_t* Volume::GetMediaName(void)
{
return mediaName;
}
const char* Volume::GetScriptData()
{
ASSERT(scriptFile);
return static_cast<char*>(scriptFile->fileData);
}
size_t Volume::GetScriptDataSize(void)
{
ASSERT(scriptFile);
return static_cast<size_t>(scriptFile->fileSize);
}
void Volume::CreateMediaFile(
AvfsMediaFile_* mediaFile,
const wchar_t* endName,
uint64_t fileSize)
{
FileNode* file;
file = new(std::nothrow) FileNode(this,0,fileTypeMedia,
scriptWriteTime);
ASSERT(!wcschr(endName,'\\'));
if(file)
{
file->name = ssdup(endName);
file->fileSize = fileSize;
if(!file->name)
{
delete file;
}
else if(mediaFile)
{
mediaFile->AddRef();
file->mediaFile = mediaFile;
}
}
}
static const PfmOpenAttribs zeroOpenAttribs = {};
void CCALL Volume::Open(PfmMarshallerOpenOp* op,void* unused)
{
const PfmNamePart* nameParts = op->NameParts();
size_t namePartCount = op->NamePartCount();
int8_t createFileType = op->CreateFileType();
int64_t writeTime = op->WriteTime();
int64_t newCreateOpenId = op->NewCreateOpenId();
int64_t newExistingOpenId = op->NewExistingOpenId();
bool existed = false;
PfmOpenAttribs openAttribs = zeroOpenAttribs;
FileNode* newFile;
FileNode* file;
int notFoundPerr;
int perr;
notFoundPerr = FileFactory(newCreateOpenId,createFileType,
writeTime,&newFile);
perr = FileOpenOrMove(nameParts,namePartCount,newFile,
newExistingOpenId,false,notFoundPerr,writeTime,
&file,&existed);
if(!perr)
{
FileOpened(file,&openAttribs);
}
op->Complete(perr,existed,&openAttribs,FileParentId(file),
FileEndName(file),0,0,
0,0);
if(newFile && !newFile->open)
{
delete newFile;
}
}
void CCALL Volume::Replace(PfmMarshallerReplaceOp* op,void* unused)
{
int64_t targetOpenId = op->TargetOpenId();
int64_t writeTime = op->WriteTime();
int64_t newCreateOpenId = op->NewCreateOpenId();
PfmOpenAttribs openAttribs = zeroOpenAttribs;
FileNode* newFile = 0;
FileNode* file = 0;
FileNode* targetFile;
int perr;
perr = FileFindOpenId(targetOpenId,true,&targetFile);
if(!perr)
{
perr = FileFactory(newCreateOpenId,targetFile->fileType,writeTime,
&newFile);
}
if(!perr)
{
perr = FileReplace(newFile,targetFile,false,writeTime);
if(!perr)
{
file = newFile;
}
}
if(!perr)
{
FileOpened(file,&openAttribs);
}
op->Complete(perr,&openAttribs,0);
if(newFile && !newFile->open)
{
delete newFile;
}
}
void CCALL Volume::Move(PfmMarshallerMoveOp* op,void* unused)
{
int64_t sourceOpenId = op->SourceOpenId();
const PfmNamePart* targetNameParts = op->TargetNameParts();
size_t targetNamePartCount = op->TargetNamePartCount();
bool deleteSource = !!op->DeleteSource();
int64_t writeTime = op->WriteTime();
int64_t newExistingOpenId = op->NewExistingOpenId();
bool existed = false;
PfmOpenAttribs openAttribs = zeroOpenAttribs;
FileNode* sourceFile;
FileNode* file = 0;
int perr;
perr = FileFindOpenId(sourceOpenId,true,&sourceFile);
if(!perr)
{
perr = FileOpenOrMove(targetNameParts,targetNamePartCount,
sourceFile,newExistingOpenId,deleteSource,pfmErrorNotFound,
writeTime,&file,&existed);
ASSERT(perr != pfmErrorNotFound);
}
if(!perr)
{
FileOpened(file,&openAttribs);
}
op->Complete(perr,existed,&openAttribs,FileParentId(file),
FileEndName(file),0,0,
0,0);
}
void CCALL Volume::MoveReplace(PfmMarshallerMoveReplaceOp* op,void* unused)
{
int64_t sourceOpenId = op->SourceOpenId();
int64_t targetOpenId = op->TargetOpenId();
bool deleteSource = !!op->DeleteSource();
int64_t writeTime = op->WriteTime();
FileNode* sourceFile = 0;
FileNode* targetFile;
int perr;
perr = FileFindOpenId(targetOpenId,true,&targetFile);
if(!perr)
{
perr = FileFindOpenId(sourceOpenId,true,&sourceFile);
}
if(!perr)
{
perr = FileReplace(sourceFile,targetFile,deleteSource,writeTime);
}
op->Complete(perr);
}
void CCALL Volume::Delete(PfmMarshallerDeleteOp* op,void* unused)
{
int64_t openId = op->OpenId();
int64_t writeTime = op->WriteTime();
FileNode* file;
int perr;
perr = FileFindOpenId(openId,true,&file);
if(!perr)
{
FileDelete(file,writeTime);
}
op->Complete(perr);
}
void CCALL Volume::Close(PfmMarshallerCloseOp* op,void* unused)
{
int64_t openId = op->OpenId();
int64_t openSequence = op->OpenSequence();
FileNode* file;
int perr;
perr = FileFindOpenId(openId,false,&file);
if(!perr)
{
if(openSequence >= file->openSequence)
{
file->open = false;
switch(file->fileType)
{
case fileTypeFolder:
while(firstList)
{
delete firstList;
}
break;
case fileTypeLog:
file->openId = 0;
break;
}
if(!file->name)
{
delete file;
}
}
}
op->Complete(perr);
}
void CCALL Volume::FlushFile(PfmMarshallerFlushFileOp* op,void* unused)
{
int64_t openId = op->OpenId();
int64_t writeTime = op->WriteTime();
PfmOpenAttribs openAttribs = zeroOpenAttribs;
FileNode* file;
int perr;
perr = FileFindOpenId(openId,false,&file);
if(!perr)
{
if(writeTime != pfmTimeInvalid)
{
file->writeTime = writeTime;
}
FileOpened(file,&openAttribs);
}
op->Complete(perr,&openAttribs,0);
}
void CCALL Volume::List(PfmMarshallerListOp* op,void* unused)
{
int64_t openId = op->OpenId();
int64_t listId = op->ListId();
size_t position = 0;
FileNode* folder;
FileNode* file;
ListState* list = 0;
int perr;
bool noMore = true;
bool added = true;
PfmAttribs attribs;
perr = FileFindOpenId(openId,false,&folder);
if(!perr && folder->fileType != fileTypeFolder)
{
perr = pfmErrorAccessDenied;
}
if(!perr)
{
list = ListFind(listId);
if(!list)
{
list = new(std::nothrow) ListState(this,listId);
if(!list)
{
perr = pfmErrorOutOfMemory;
}
}
}
if(!perr)
{
file = firstFile;
while(file && position < list->position)
{
file = file->nextFile;
}
while(file && added)
{
attribs = {};
attribs.fileType = pfmFileTypeFile;
if(file->fileType == fileTypeFolder)
{
attribs.fileType = pfmFileTypeFolder;
}
attribs.fileSize = file->fileSize;
attribs.writeTime = file->writeTime;
if(file->fileType == fileTypeMedia)
{
attribs.extraFlags |= pfmExtraFlagOffline;
}
added = !!op->Add(&attribs,file->name);
list->position += !!added;
file = file->nextFile;
}
noMore = !file;
}
op->Complete(perr,noMore);
}
void CCALL Volume::ListEnd(PfmMarshallerListEndOp* op,void* unused)
{
int64_t openId = op->OpenId();
int64_t listId = op->ListId();
FileNode* file;
ListState* list;
int perr;
perr = FileFindOpenId(openId,false,&file);
if(!perr && file->fileType == fileTypeFolder)
{
list = ListFind(listId);
if(!list)
{
perr = pfmErrorInvalid;
}
else
{
delete list;
}
}
op->Complete(perr);
}
void CCALL Volume::Read(PfmMarshallerReadOp* op,void* unused)
{
int64_t openId = op->OpenId();
uint64_t fileOffset = op->FileOffset();
void* buffer = op->Data();
size_t requestedSize = op->RequestedSize();
size_t actualSize = 0;
uint64_t maxSize;
FileNode* file;
int perr;
perr = FileFindOpenId(openId,false,&file);
if(!perr)
{
maxSize = file->fileSize;
if(fileOffset < maxSize)
{
maxSize -= fileOffset;
actualSize = requestedSize;
if(maxSize < requestedSize)
{
actualSize = static_cast<size_t>(maxSize);
}
}
switch(file->fileType)
{
default:
case fileTypeFolder:
perr = pfmErrorAccessDenied;
break;
case fileTypeScript:
case fileTypeLog:
if(actualSize)
{
ASSERT(file->fileSize <= maxScriptDataSize);
memcpy(buffer,static_cast<uint8_t*>(file->fileData)+
static_cast<size_t>(fileOffset),actualSize);
}
break;
case fileTypeMedia:
perr = pfmErrorDeleted;
if(file->mediaFile)
{
perr = 0;
if(actualSize)
{
if(!file->mediaFile->ReadMedia(this,fileOffset,buffer,
actualSize))
{
actualSize = 0;
perr = pfmErrorCorruptData;
}
}
}
break;
}
}
if(perr)
{
actualSize = 0;
}
op->Complete(perr,actualSize);
}
void CCALL Volume::Write(PfmMarshallerWriteOp* op,void* unused)
{
int64_t openId = op->OpenId();
uint64_t fileOffset = op->FileOffset();
const void* buffer = op->Data();
size_t requestedSize = op->RequestedSize();
size_t actualSize = 0;
FileNode* file;
int perr;
perr = FileFindOpenId(openId,true,&file);
if(!perr)
{
perr = FileWrite(file,maxScriptDataSize,fileOffset,buffer,
requestedSize,&actualSize);
}
op->Complete(perr,actualSize);
}
void CCALL Volume::SetSize(PfmMarshallerSetSizeOp* op,void* unused)
{
int64_t openId = op->OpenId();
uint64_t fileSize = op->FileSize();
FileNode* file;
int perr;
perr = FileFindOpenId(openId,true,&file);
if(!perr)
{
if(fileSize < file->fileSize)
{
file->fileSize = fileSize;
}
else
{
perr = FileWrite(file,maxScriptDataSize,fileSize,0,0,0);
}
}
op->Complete(perr);
}
void CCALL Volume::Capacity(PfmMarshallerCapacityOp* op,void* unused)
{
uint64_t totalCapacity;
uint64_t availableCapacity;
FileNode* file;
totalCapacity = availableCapacity = 1000000;
file = firstFile;
while(file)
{
totalCapacity += file->fileSize;
file = file->nextFile;
}
op->Complete(0,totalCapacity,availableCapacity);
}
void CCALL Volume::FlushMedia(PfmMarshallerFlushMediaOp* op,void* unused)
{
ProcessScript();
op->Complete(0,2000);
}
void CCALL Volume::Control(PfmMarshallerControlOp* op,void* unused)
{
op->Complete(pfmErrorInvalid,0);
}
static const PfmMediaInfo zeroMediaInfo = {};
void CCALL Volume::MediaInfo(PfmMarshallerMediaInfoOp* op,void* unused)
{
PfmMediaInfo mediaInfo = zeroMediaInfo;
op->Complete(0,&mediaInfo,0);
}
void CCALL Volume::Access(PfmMarshallerAccessOp* op,void* unused)
{
int64_t openId = op->OpenId();
PfmOpenAttribs openAttribs = zeroOpenAttribs;
FileNode* file;
int perr;
perr = FileFindOpenId(openId,false,&file);
if(!perr)
{
FileOpened(file,&openAttribs);
}
op->Complete(perr,&openAttribs,0);
}
void CCALL Volume::ReadXattr(PfmMarshallerReadXattrOp* op,void* unused)
{
op->Complete(pfmErrorInvalid,0,0);
}
void CCALL Volume::WriteXattr(PfmMarshallerWriteXattrOp* op,void* unused)
{
op->Complete(pfmErrorInvalid,0);
}
Volume::Volume(void)
{
marshaller = 0;
scriptFileName = 0;
scriptFolderName = 0;
scriptEndName = 0;
mediaName = 0;
scriptWritable = false;
scriptFile = 0;
scriptWriteTime = 0;
logFile = 0;
firstList = 0;
firstFile = 0;
lastFileId = 2;
}
Volume::~Volume(void)
{
if(marshaller)
{
marshaller->Release();
}
free(scriptFileName);
free(mediaName);
while(firstList)
{
delete firstList;
}
while(firstFile)
{
delete firstFile;
}
}
int Volume::Init(
const wchar_t* inScriptFileName)
{
wchar_t* dot;
FileNode* folder;
int error = PfmMarshallerFactory(&marshaller);
if(!error)
{
marshaller->SetTrace(L"AVFS-PFM");
scriptFileName = ssdup(inScriptFileName);
scriptEndName = ssrchr(scriptFileName,'\\');
if(scriptEndName)
{
scriptEndName ++;
scriptFolderName = ssdupn(scriptFileName,scriptEndName-scriptFileName);
}
else
{
scriptEndName = scriptFileName;
scriptFolderName = ssdup(L".\\");
}
mediaName = ssdup(scriptEndName);
dot = ssrchr(mediaName,'.');
if(dot && dot > mediaName)
{
*dot = 0;
}
if(!scriptFileName || !scriptFolderName || !mediaName)
{
error = ERROR_OUTOFMEMORY;
}
}
if(!error)
{
error = ERROR_OUTOFMEMORY;
folder = new(std::nothrow) FileNode(this,0,fileTypeFolder,
pfmTimeInvalid);
if(folder)
{
folder->name = ssdup(L".");
if(folder->name)
{
error = 0;
}
}
}
if(!error)
{
ProcessScript();
}
return error;
}
int wmain(int argc,const wchar_t*const* argv)
{
int error = 0;
int argi;
const wchar_t* arg;
bool argProcessed;
bool showHelp = false;
PfmMountCreateParams mcp;
bool diagnosticMode = false;
bool serve = false;
PfmFastPipeCreateParams pcp;
PfmApi* pfm = 0;
PfmMount* mount = 0;
Volume* volume = 0;
FILE* fstatus = stdout;
HANDLE status = GetStdHandle(STD_OUTPUT_HANDLE);
PfmMarshallerServeParams msp;
PfmMountCreateParams_Init(&mcp);
PfmMarshallerServeParams_Init(&msp);
msp.toFormatterRead = INVALID_HANDLE_VALUE;
msp.fromFormatterWrite = INVALID_HANDLE_VALUE;
argi = 1;
while(!error && argi < argc)
{
arg = argv[argi++];
argProcessed = false;
if((arg[0] == '-' || arg[0] == '/') && sslen(arg) == 2)
{
argProcessed = true;
switch(tolower(arg[1]))
{
case 'h':
case '?':
showHelp = true;
break;
case 'd':
diagnosticMode = true;
break;
case 's':
serve = true;
fstatus = stderr;
status = GetStdHandle(STD_ERROR_HANDLE);
break;
default:
fprintf(fstatus,"WARNING: Ignoring switch \"%ls\".\n",arg);
}
}
if(!argProcessed)
{
if(mcp.mountName)
{
fprintf(fstatus,"WARNING: Ignoring arg \"%ls\".\n",arg);
}
else
{
mcp.mountName = arg;
}
}
}
if(argc == 1)
{
error = -2;
fprintf(fstatus,
"Avisynth Virtual File System with VapourSynth support\n"
"Version R" XSTR(VAPOURSYNTH_CORE_VERSION) "\n"
"Copyright 2008-2015 Ben Rudiak-Gould et al.\n"
"For help run: avfs -h\n");
}
else if(showHelp)
{
error = -2;
fprintf(fstatus,
"syntax: avfs [<switch> ...] <script file>\n"
"switches:\n"
" -d Print diagnostic info to stdout.\n"
" -s Serve to stdin/stdout.\n");
}
size_t fnlen = sslen(mcp.mountName);
if(!error && !fnlen)
{
error = -1;
fprintf(fstatus,"ERROR: Must provide name of script to mount\n");
}
if (!error && (fnlen < 4 || (sscmpi(mcp.mountName + fnlen - 4, L".avs") && sscmpi(mcp.mountName + fnlen - 4, L".vpy")))) {
error = -1;
fprintf(fstatus, "ERROR: Only files with the extensions .vpy and .avs can be mounted\n");
}
if(!error)
{
error = PfmApiFactory(&pfm);
if(error)
{
fprintf(fstatus,"ERROR: Unable to open PFM interface. "
"PFM probably not installed.\n");
}
}
if(serve)
{
msp.toFormatterRead = GetStdHandle(STD_INPUT_HANDLE);
msp.fromFormatterWrite = GetStdHandle(STD_OUTPUT_HANDLE);
}
else
{
if(!error)
{
PfmFastPipeCreateParams_Init(&pcp);
pcp.fastPipeFlags = pfmFastPipeFlagAsyncClient|
pfmFastPipeFlagAsyncServer;
error = pfm->FastPipeCreate(&pcp,&mcp.toFormatterWrite,
&msp.toFormatterRead);
if(!error)
{
msp.fromFormatterWrite = msp.toFormatterRead;
mcp.fromFormatterRead = mcp.toFormatterWrite;
}
else
{
error = FileCreatePipe(&msp.toFormatterRead,&mcp.toFormatterWrite);
if(!error)
{
error = FileCreatePipe(&mcp.fromFormatterRead,
&msp.fromFormatterWrite);
}
}
if(error)
{
fprintf(fstatus,"ERROR: %i Unable to create pipes.\n",error);
}
}
if(!error)
{
mcp.mountFlags |= pfmMountFlagWorldRead|pfmMountFlagWorldWrite|
pfmMountFlagUnmountOnRelease;
error = pfm->MountCreate(&mcp,&mount);
if(error)
{
fprintf(fstatus,"ERROR: %i Unable to create mount.\n",error);
}
}
FileCleanupPair(&mcp.toFormatterWrite,&mcp.fromFormatterRead);
}
if(!error)
{
volume = new(std::nothrow) Volume;
if(!volume)
{
error = ERROR_OUTOFMEMORY;
}
else
{
error = volume->Init(mcp.mountName);
}
if(error)
{
fprintf(fstatus,"ERROR: %i Initializing volume.\n",error);
}
}
if(!error)
{
if(diagnosticMode)
{
volume->marshaller->SetStatus(status);
}
msp.dispatch = volume;
msp.formatterName = "avfs";
msp.dataAlign = 0x40;
msp.maxDirectMsgSize = 0x100000;
msp.volumeFlags = pfmVolumeFlagNoCreateTime;
msp.volumeFlags = pfmVolumeFlagNoAccessTime;
msp.volumeFlags = pfmVolumeFlagNoChangeTime;
fwprintf(fstatus, L"Mount point: %s\n", mount->GetMountPoint());
fprintf(fstatus,"Press CTRL+C to exit.\n");
error = volume->marshaller->ServeDispatch(&msp);
if(error)
{
fprintf(fstatus,"ERROR: %i Serving volume.\n",error);
}
}
FileCleanupPair(&msp.toFormatterRead,&msp.fromFormatterWrite);
delete volume;
if(pfm)
{
pfm->Release();
}
if(mount)
{
mount->Release();
}
if(error == -2)
{
error = 0;
}
PfmApiUnload();
return error;
}