PageRenderTime 8ms CodeModel.GetById 2ms app.highlight 77ms RepoModel.GetById 1ms app.codeStats 0ms

/src/vsfs/vsfspfm.cpp

https://bitbucket.org/sergem/vapoursynth
C++ | 1457 lines | 1221 code | 64 blank | 172 comment | 164 complexity | c894ba1e45fde67ddc7166e56ede2cb3 MD5 | raw file
   1//
   2// VapourSynth modifications Copyright 2012 Fredrik Mellbin
   3//
   4//----------------------------------------------------------------------------
   5// Copyright 2008-2010 Joe Lowe
   6//
   7// Permission is granted to any person obtaining a copy of this Software,
   8// to deal in the Software without restriction, including the rights to use,
   9// copy, modify, merge, publish, distribute, sublicense, and sell copies of
  10// the Software.
  11//
  12// The above copyright and permission notice must be left intact in all
  13// copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS WITHOUT WARRANTY.
  16//----------------------------------------------------------------------------
  17// file name:  vsfspfm.cpp
  18// created:    2008.01.04
  19//----------------------------------------------------------------------------
  20// Notes:
  21//
  22// This module implements a simple virtual file system for
  23// use with Avisynth and the Pismo File Mount Audit Package.
  24// The file system mounts an Avisynth script file, presenting
  25// it as a virtual folder containing virtual files that
  26// represent the the media stream produced by the script.
  27//
  28// The file system has write support for a virtual "echo"
  29// script file. This echo script file allows the script to be
  30// edited and the virtual media stream reinitialized without
  31// having to unmount and remount the real script file.
  32//
  33// A few seconds after the echo script file is modified,
  34// Avisynth will be reinitialized and the virtual media files
  35// will change. You can force Avisynth to be reinitialized by
  36// touching the echo script file (modifying the time stamp).
  37//
  38// Avisynth errors should be logged to the provided log
  39// interface. This will write the errors to a virtual
  40// error.log file.
  41//
  42// The PfmMarshaller interface provided by PFM contains
  43// diagnostic tracing support and generates diagnostic traces
  44// related to file system activity. Developers can view these
  45// traces by installing Pismo Trace Monitor. Errors written
  46// to the log interface are also visible through the trace
  47// monitor.
  48//
  49//----------------------------------------------------------------------------
  50#include "assertive.h"
  51#include <limits.h>
  52#include "stdint.h"
  53#include <stddef.h>
  54#include <stdarg.h>
  55#include <string.h>
  56#include <malloc.h>
  57#include <wchar.h>
  58#include <new>
  59#define WIN32_LEAN_AND_MEAN
  60#include <windows.h>
  61#include "ss.h"
  62#include "files.h"
  63#include "pfmenum.h"
  64#include "pfmformatter.h"
  65#include "pfmmarshaller.h"
  66#include "vsfspfm.h"
  67
  68#define CCALL __cdecl
  69
  70struct AvfsFormatter: PfmFormatter
  71{
  72    struct Volume;
  73
  74    enum {
  75        fileTypeAny       = 0,
  76        fileTypeFolder    = 1,
  77        fileTypeScript    = 2,
  78        fileTypeLog       = 3,
  79        fileTypeMedia     = 4,
  80        maxScriptDataSize = 10*1000000,
  81        maxLogDataSize    = 250*1000000, };
  82
  83        struct ListState
  84        {
  85            Volume* volume;
  86            ListState** prevList;
  87            ListState* nextList;
  88            int64_t listId;
  89            size_t position;
  90            ListState(Volume* volume,int64_t listId);
  91            ~ListState(void);
  92        };
  93
  94        struct FileNode
  95        {
  96            Volume* volume;
  97            FileNode** prevFile;
  98            FileNode* nextFile;
  99            wchar_t* name;
 100            int64_t openId;
 101            int64_t openSequence;
 102            bool open;
 103            int8_t fileType;
 104            int64_t writeTime;
 105            uint64_t fileSize;
 106            size_t maxFileDataSize;
 107            void* fileData;
 108            AvfsMediaFile_* mediaFile;
 109            FileNode(Volume* volume,int64_t openId,int8_t fileType,int64_t writeTime);
 110            ~FileNode(void);
 111        };
 112
 113        struct Volume:
 114            AvfsLog_,
 115            AvfsVolume_,
 116            PfmFormatterOps
 117        {
 118            PfmMarshaller* marshaller;
 119            wchar_t* scriptFileName;
 120            const wchar_t* scriptEndName;
 121            wchar_t* mediaName;
 122            bool scriptWritable;
 123            int64_t scriptWriteTime;
 124            FileNode* scriptFile;
 125            FileNode* logFile;
 126            ListState* firstList;
 127            FileNode* firstFile;
 128
 129            // AvfsLog_
 130            void Print(const wchar_t* data);
 131            void Vprintf(const wchar_t* format,va_list args);
 132            void Printf(const wchar_t* format,...);
 133            void Line(const wchar_t* data);
 134
 135            ListState* ListFind(int64_t listId);
 136            int/*pfmError*/ FileFindName(const PfmNamePart* nameParts,size_t namePartCount,FileNode** file);
 137            int/*pfmError*/ FileFindOpenId(int64_t openId,bool forModify,FileNode** file);
 138            void FileOpened(FileNode* file,PfmOpenAttribs* openAttribs);
 139            int/*pfmError*/ FileCreate(int64_t openId,int8_t createFileType,int64_t writeTime,FileNode** outFile);
 140            int/*pfmError*/ FileOpenOrMove(const PfmNamePart* nameParts,size_t namePartCount,FileNode* sourceFile,int64_t newExistingOpenId,bool deleteSource,int notFoundError,bool* existed,PfmOpenAttribs* openAttribs,wchar_t** endName);
 141            int/*pfmError*/ FileReplace(FileNode* sourceFile,FileNode* targetFile,bool deleteSource,PfmOpenAttribs* openAttribs);
 142            int/*pfmError*/ FileWrite(FileNode* file,size_t maxFileDataSize,uint64_t fileOffset,const void* buffer,size_t requestedSize,size_t* actualSize);
 143            void DeleteMediaFiles(void);
 144
 145            // AvfsVolume_
 146            const wchar_t* GetScriptFileName(void);
 147            const wchar_t* GetScriptEndName(void);
 148            const wchar_t* GetMediaName(void);
 149            const char* GetScriptData(void);
 150            size_t GetScriptDataSize(void);
 151            void CreateMediaFile(AvfsMediaFile_* mediaFile,const wchar_t* fileName,uint64_t fileSize);
 152
 153            // PfmFormatterOps
 154            void CCALL ReleaseName(wchar_t* name);
 155            int/*pfmError*/ CCALL Open(const PfmNamePart* nameParts,size_t namePartCount,int8_t createFileType,uint8_t createFileFlags,int64_t writeTime,int64_t newCreateOpenId,int8_t existingAccessLevel,int64_t newExistingOpenId,bool* existed,PfmOpenAttribs* openAttribs,int64_t* parentFileId,wchar_t** endName);
 156            int/*pfmError*/ CCALL Replace(int64_t targetOpenId,int64_t targetParentFileId,const PfmNamePart* targetEndName,uint8_t createFileFlags,int64_t writeTime,int64_t newCreateOpenId,PfmOpenAttribs* openAttribs);
 157            int/*pfmError*/ CCALL Move(int64_t sourceOpenId,int64_t sourceParentFileId,const PfmNamePart* sourceEndName,const PfmNamePart* targetNameParts,size_t targetNamePartCount,bool deleteSource,int64_t writeTime,int64_t newExistingOpenId,bool* existed,PfmOpenAttribs* openAttribs,int64_t* parentFileId,wchar_t** endName);
 158            int/*pfmError*/ CCALL MoveReplace(int64_t sourceOpenId,int64_t sourceParentFileId,const PfmNamePart* sourceEndName,int64_t targetOpenId,int64_t targetParentFileId,const PfmNamePart* targetEndName,bool deleteSource,int64_t writeTime);
 159            int/*pfmError*/ CCALL Delete(int64_t openId,int64_t parentFileId,const PfmNamePart* endName,int64_t writeTime);
 160            int/*pfmError*/ CCALL Close(int64_t openId,int64_t openSequence);
 161            int/*pfmError*/ CCALL FlushFile(int64_t openId,uint8_t fileFlags,int64_t createTime,int64_t accessTime,int64_t writeTime,int64_t changeTime);
 162            int/*pfmError*/ CCALL List(int64_t openId,int64_t listId,PfmMarshallerListResult* listResult);
 163            int/*pfmError*/ CCALL ListEnd(int64_t openId,int64_t listId);
 164            int/*pfmError*/ CCALL Read(int64_t openId,uint64_t fileOffset,void* data,size_t requestedSize,size_t* outActualSize);
 165            int/*pfmError*/ CCALL Write(int64_t openId,uint64_t fileOffset,const void* data,size_t requestedSize,size_t* outActualSize);
 166            int/*pfmError*/ CCALL SetSize(int64_t openId,uint64_t fileSize);
 167            int/*pfmError*/ CCALL Capacity(uint64_t* totalCapacity,uint64_t* availableCapacity);
 168            int/*pfmError*/ CCALL FlushMedia(bool* mediaClean);
 169            int/*pfmError*/ CCALL Control(int64_t openId,int8_t accessLevel,int controlCode,const void* input,size_t inputSize,void* output,size_t maxOutputSize,size_t* outputSize);
 170            int/*pfmError*/ CCALL MediaInfo(int64_t openId,PfmMediaInfo* mediaInfo,wchar_t** mediaLabel);
 171
 172            Volume(void);
 173            ~Volume(void);
 174            int/*systemError*/ Init(const wchar_t* scriptFileName);
 175            void Destroy(void);
 176        };
 177
 178        // PfmFormatter
 179        void CCALL Release(void);
 180        int/*systemError*/ CCALL Identify(HANDLE statusWrite,const wchar_t* mountFileName,HANDLE mountFileHandle,const void* mountFileData,size_t mountFileDataSize);
 181        int/*systemError*/ CCALL Serve(const wchar_t* mountFileName,int mountFlags,HANDLE read,HANDLE write);
 182};
 183
 184AvfsFormatter avfsFormatter;
 185
 186AvfsFormatter::ListState::ListState(
 187    Volume* inVolume,
 188    int64_t inListId)
 189{
 190    volume = inVolume;
 191    prevList = &(volume->firstList);
 192    while(*prevList)
 193    {
 194        prevList= &((*prevList)->nextList);
 195    }
 196    *prevList = this;
 197    nextList = 0;
 198    listId = inListId;
 199    position = 0;
 200}
 201
 202AvfsFormatter::ListState::~ListState(void)
 203{
 204    if(prevList)
 205    {
 206        ASSERT(*prevList == this);
 207        *prevList = nextList;
 208        if(nextList)
 209        {
 210            ASSERT(nextList->prevList == &nextList);
 211            nextList->prevList = prevList;
 212        }
 213    }
 214}
 215
 216AvfsFormatter::FileNode::FileNode(
 217    Volume* inVolume,
 218    int64_t inOpenId,
 219    int8_t inFileType,
 220    int64_t inWriteTime)
 221{
 222    volume = inVolume;
 223    prevFile = &(volume->firstFile);
 224    while(*prevFile)
 225    {
 226        prevFile= &((*prevFile)->nextFile);
 227    }
 228    *prevFile = this;
 229    nextFile = 0;
 230    name = 0;
 231    openId = inOpenId;
 232    openSequence = 0;
 233    open = false;
 234    fileType = inFileType;
 235    writeTime = inWriteTime;
 236    fileSize = 0;
 237    maxFileDataSize = 0;
 238    fileData = 0;
 239    mediaFile = 0;
 240}
 241
 242AvfsFormatter::FileNode::~FileNode(void)
 243{
 244    if(prevFile)
 245    {
 246        ASSERT(*prevFile == this);
 247        *prevFile = nextFile;
 248        if(nextFile)
 249        {
 250            ASSERT(nextFile->prevFile == &nextFile);
 251            nextFile->prevFile = prevFile;
 252        }
 253    }
 254    ssfree(name);
 255    if(fileData)
 256    {
 257        free(fileData);
 258    }
 259    if(mediaFile)
 260    {
 261        mediaFile->Release();
 262    }
 263}
 264
 265void AvfsFormatter::Volume::Print(const wchar_t* data)
 266{
 267    marshaller->Print(data);
 268    char* data8 = ssconvalloc(data);
 269    if(data8)
 270    {
 271        FileWrite(logFile,maxLogDataSize,logFile->fileSize,data8,
 272            sssize(data8),0);
 273    }
 274    ssfree(data8);
 275}
 276
 277void AvfsFormatter::Volume::Vprintf(const wchar_t* format,va_list args)
 278{
 279    marshaller->Vprintf(format,args);
 280    wchar_t* data = ssvformatalloc(format,args);
 281    Print(data);
 282    ssfree(data);
 283}
 284
 285void AvfsFormatter::Volume::Printf(const wchar_t* format,...)
 286{
 287    va_list args;
 288    va_start(args,format);
 289    Vprintf(format,args);
 290}
 291
 292void AvfsFormatter::Volume::Line(const wchar_t* data)
 293{
 294    Printf(L"%s\r\n",data);
 295}
 296
 297AvfsFormatter::ListState* AvfsFormatter::Volume::ListFind(
 298    int64_t listId)
 299{
 300    ListState* list = firstList;
 301    while(list && list->listId != listId)
 302    {
 303        list = list->nextList;
 304    }
 305    return list;
 306}
 307
 308int/*pfmError*/ AvfsFormatter::Volume::FileFindName(
 309    const PfmNamePart* nameParts,
 310    size_t namePartCount,
 311    FileNode** outFile)
 312{
 313    int error = pfmErrorParentNotFound;
 314    FileNode* file = 0;
 315    // Root folder has fixed name.
 316    const wchar_t* name = L".";
 317    // Subfolders not supported, so names can only have one
 318    // part.
 319    if(namePartCount < 2)
 320    {
 321        error = pfmErrorNotFound;
 322        if(namePartCount == 1)
 323        {
 324            name = nameParts[0].name;
 325        }
 326        file = firstFile;
 327        while(file && sscmpi(file->name,name) != 0)
 328        {
 329            file = file->nextFile;
 330        }
 331        if(file)
 332        {
 333            error = 0;
 334        }
 335    }
 336    *outFile = file;
 337    return error;
 338}
 339
 340int/*pfmError*/ AvfsFormatter::Volume::FileFindOpenId(
 341    int64_t openId,
 342    bool forModify,
 343    FileNode** outFile)
 344{
 345    int error = 0;
 346    FileNode* file = firstFile;
 347    while(file && (!file->open || file->openId != openId))
 348    {
 349        file = file->nextFile;
 350    }
 351    if(!file)
 352    {
 353        error = pfmErrorNotFound;
 354    }
 355    // Can only move/delete/write script files, and only if real
 356    // script file was writable.
 357    else if(forModify && (!scriptWritable ||
 358        file->fileType != fileTypeScript))
 359    {
 360        error = pfmErrorAccessDenied;
 361    }
 362    *outFile = file;
 363    return error;
 364}
 365
 366void AvfsFormatter::Volume::FileOpened(
 367    FileNode* file,
 368    PfmOpenAttribs* openAttribs)
 369{
 370    ASSERT(file);
 371    file->open = true;
 372    openAttribs->openId = file->openId;
 373    openAttribs->openSequence = ++(file->openSequence);
 374    // HACKHACK: VirtualDub hex editor will not open read-only files.
 375    // openAttribs->accessLevel = pfmReadDataAccess;
 376    // if(file->fileType == fileTypeScript)
 377    {
 378        openAttribs->accessLevel = pfmWriteDataAccess;
 379    }
 380    openAttribs->attribs.fileType = pfmFileTypeFile;
 381    if(file->fileType == fileTypeFolder)
 382    {
 383        openAttribs->attribs.fileType = pfmFileTypeFolder;
 384    }
 385    openAttribs->attribs.fileSize = file->fileSize;
 386    openAttribs->attribs.writeTime = file->writeTime;
 387    if(file->fileType == fileTypeMedia)
 388    {
 389        openAttribs->attribs.extraFlags |=
 390            (pfmExtraFlagOffline|pfmExtraFlagNoIndex);
 391    }
 392}
 393
 394int/*pfmError*/ AvfsFormatter::Volume::FileCreate(
 395    int64_t openId,
 396    int8_t createFileType,
 397    int64_t writeTime,
 398    FileNode** outFile)
 399{
 400    FileNode* file = 0;
 401    // Only allow files to be created.
 402    int error = pfmErrorAccessDenied;
 403    switch(createFileType)
 404    {
 405    case pfmFileTypeFile:
 406        error = pfmErrorAccessDenied;
 407        if(scriptWritable)
 408        {
 409            error = pfmErrorOutOfMemory;
 410            file = new(std::nothrow) FileNode(this,openId,fileTypeScript,writeTime);
 411            if(file)
 412            {
 413                error = 0;
 414            }
 415        }
 416        break;
 417    case pfmFileTypeNone:
 418        error = pfmErrorNotFound;
 419        break;
 420    }
 421    *outFile = file;
 422    return error;
 423}
 424
 425int/*pfmError*/ AvfsFormatter::Volume::FileOpenOrMove(
 426    const PfmNamePart* nameParts,
 427    size_t namePartCount,
 428    FileNode* sourceFile,
 429    int64_t newExistingOpenId,
 430    bool deleteSource,
 431    int notFoundError,
 432    bool* existed,
 433    PfmOpenAttribs* openAttribs,
 434    wchar_t** endName)
 435{
 436    // Open existing file, or move source file to
 437    // the non-existing file name.
 438    FileNode* file;
 439    int error = FileFindName(nameParts,namePartCount,&file);
 440    wchar_t* name;
 441    if(!error)
 442    {
 443        ASSERT(file);
 444        *existed = true;
 445        // Use driver supplied open ID if this file has never
 446        // been opened.
 447        if(!file->openId)
 448        {
 449            file->openId = newExistingOpenId;
 450        }
 451    }
 452    else if(error == pfmErrorNotFound)
 453    {
 454        ASSERT(namePartCount == 1);
 455        *existed = false;
 456        if(notFoundError)
 457        {
 458            error = notFoundError;
 459        }
 460        if(sourceFile)
 461        {
 462            ASSERT(sourceFile->openId);
 463            // Don't support hard links, but do support restoring
 464            // deleted (no name) files.
 465            error = pfmErrorInvalid;
 466            if(deleteSource || !sourceFile->name)
 467            {
 468                error = pfmErrorOutOfMemory;
 469                name = ssdup(nameParts[0].name);
 470                if(name)
 471                {
 472                    error = 0;
 473                    ssfree(sourceFile->name);
 474                    sourceFile->name = name;
 475                    file = sourceFile;
 476                }
 477            }
 478        }
 479    }
 480    if(!error)
 481    {
 482        FileOpened(file,openAttribs);
 483        if(sscmpi(file->name,L".") != 0)
 484        {
 485            *endName = ssdup(file->name);
 486        }
 487    }
 488    return error;
 489}
 490
 491int/*pfmError*/ AvfsFormatter::Volume::FileReplace(
 492    FileNode* sourceFile,
 493    FileNode* targetFile,
 494    bool deleteSource,
 495    PfmOpenAttribs* openAttribs)
 496{
 497    // Delete target file and rename source file to target
 498    // files name.
 499    int error = 0;
 500    // Only script files can be moved/deleted/modified.
 501    if(sourceFile->fileType != fileTypeScript ||
 502        targetFile->fileType != fileTypeScript)
 503    {
 504        error = pfmErrorAccessDenied;
 505    }
 506    // Don't support hard links, but do support restoring
 507    // deleted (no name) files. Target can not already be
 508    // deleted.
 509    else if(!targetFile->name || (!deleteSource && sourceFile->name))
 510    {
 511        error = pfmErrorInvalid;
 512    }
 513    if(!error)
 514    {
 515        ssfree(sourceFile->name);
 516        sourceFile->name = targetFile->name;
 517        targetFile->name = 0;
 518        if(openAttribs)
 519        {
 520            FileOpened(sourceFile,openAttribs);
 521        }
 522    }
 523    return error;
 524}
 525
 526int/*error*/ AvfsFormatter::Volume::FileWrite(
 527    FileNode* file,
 528    size_t maxFileDataSize,
 529    uint64_t fileOffset,
 530    const void* buffer,
 531    size_t requestedSize,
 532    size_t* outActualSize)
 533{
 534    int error = 0;
 535    size_t actualSize = 0;
 536    size_t endOffset;
 537    size_t newMaxFileDataSize;
 538    void* newFileData;
 539    if(fileOffset > maxFileDataSize)
 540    {
 541        error = pfmErrorNoSpace;
 542    }
 543    else
 544    {
 545        actualSize = maxFileDataSize-
 546            static_cast<size_t>(fileOffset);
 547    }
 548    if(!error)
 549    {
 550        if(actualSize > requestedSize)
 551        {
 552            actualSize = requestedSize;
 553        }
 554        endOffset = static_cast<size_t>(fileOffset)+actualSize;
 555        if(endOffset > file->maxFileDataSize)
 556        {
 557            newMaxFileDataSize = endOffset+endOffset/4+1000;
 558            newFileData = malloc(newMaxFileDataSize);
 559            if(!newFileData)
 560            {
 561                error = pfmErrorOutOfMemory;
 562            }
 563            else
 564            {
 565                if(file->fileData)
 566                {
 567                    if(file->fileSize)
 568                    {
 569                        memcpy(newFileData,file->fileData,
 570                            static_cast<size_t>(file->fileSize));
 571                    }
 572                    free(file->fileData);
 573                }
 574                file->maxFileDataSize = newMaxFileDataSize;
 575                file->fileData = newFileData;
 576            }
 577        }
 578        if(!error)
 579        {
 580            memcpy(static_cast<uint8_t*>(file->fileData)+
 581                static_cast<size_t>(fileOffset),buffer,actualSize);
 582            if(endOffset > file->fileSize)
 583            {
 584                file->fileSize = endOffset;
 585            }
 586        }
 587    }
 588    if(outActualSize)
 589    {
 590        *outActualSize = actualSize;
 591    }
 592    return error;
 593}
 594
 595void AvfsFormatter::Volume::DeleteMediaFiles(void)
 596{
 597    // Delete all media files.
 598    FileNode* file = firstFile;
 599    while(file)
 600    {
 601        if(file->fileType == fileTypeMedia)
 602        {
 603            ssfree(file->name);
 604            file->name = 0;
 605            if(file->mediaFile)
 606            {
 607                file->mediaFile->Release();
 608                file->mediaFile = 0;
 609            }
 610            if(file->open)
 611            {
 612                // The file is still open. It will be deleted
 613                // when it is finally closed.
 614                file = file->nextFile;
 615            }
 616            else
 617            {
 618                delete file;
 619                file = firstFile;
 620            }
 621        }
 622        else
 623        {
 624            file = file->nextFile;
 625        }
 626    }
 627}
 628
 629const wchar_t* AvfsFormatter::Volume::GetScriptFileName(void)
 630{
 631    return scriptFileName;
 632}
 633
 634const wchar_t* AvfsFormatter::Volume::GetScriptEndName(void)
 635{
 636    return scriptEndName;
 637}
 638
 639const wchar_t* AvfsFormatter::Volume::GetMediaName(void)
 640{
 641    return mediaName;
 642}
 643
 644const char* AvfsFormatter::Volume::GetScriptData()
 645{
 646    ASSERT(scriptFile);
 647    return static_cast<char*>(scriptFile->fileData);
 648}
 649
 650size_t AvfsFormatter::Volume::GetScriptDataSize(void)
 651{
 652    ASSERT(scriptFile);
 653    return static_cast<size_t>(scriptFile->fileSize);
 654}
 655
 656void AvfsFormatter::Volume::CreateMediaFile(
 657    AvfsMediaFile_* mediaFile,
 658    const wchar_t* endName,
 659    uint64_t fileSize)
 660{
 661    // Create a media file whose data will be satisfied by
 662    // read calls to the media object.
 663    FileNode* file;
 664    file = new(std::nothrow) FileNode(this,0,fileTypeMedia,
 665        scriptWriteTime);
 666    ASSERT(!wcschr(endName,'\\'));
 667    if(file)
 668    {
 669        file->name = ssdup(endName);
 670        file->fileSize = fileSize;
 671        if(!file->name)
 672        {
 673            delete file;
 674        }
 675        else if(mediaFile)
 676        {
 677            mediaFile->AddRef();
 678            file->mediaFile = mediaFile;
 679        }
 680    }
 681}
 682
 683void CCALL AvfsFormatter::Volume::ReleaseName(
 684    wchar_t* name)
 685{
 686    ssfree(name);
 687}
 688
 689int/*pfmError*/ CCALL AvfsFormatter::Volume::Open(
 690    const PfmNamePart* nameParts,
 691    size_t namePartCount,
 692    int8_t createFileType,
 693    uint8_t createFileFlags,
 694    int64_t writeTime,
 695    int64_t newCreateOpenId,
 696    int8_t existingAccessLevel,
 697    int64_t newExistingOpenId,
 698    bool* existed,
 699    PfmOpenAttribs* openAttribs,
 700    int64_t* parentFileId,
 701    wchar_t** endName)
 702{
 703    // Open or create of a file or folder.
 704    FileNode* createFile;
 705    // Many editors save files using a create/delete/rename
 706    // sequence. Must support file creation in order to allow
 707    // script file to be edited.
 708    // Create a _new unnamed source file to be moved to the
 709    // specified file name if it does not exist.
 710    int notFoundError = FileCreate(newCreateOpenId,createFileType,
 711        writeTime,&createFile);
 712    // Use common open/move logic.
 713    int error = FileOpenOrMove(nameParts,namePartCount,createFile,
 714        newExistingOpenId,false/*deleteSource*/,notFoundError,existed,
 715        openAttribs,endName);
 716    if(createFile && !createFile->open)
 717    {
 718        delete createFile;
 719    }
 720    return error;
 721}
 722
 723int/*error*/ CCALL AvfsFormatter::Volume::Replace(
 724    int64_t targetOpenId,
 725    int64_t targetParentFileId,
 726    const PfmNamePart* targetEndName,
 727    uint8_t createFileFlags,
 728    int64_t writeTime,
 729    int64_t newCreateOpenId,
 730    PfmOpenAttribs* openAttribs)
 731{
 732    // Replace an existing file with a _new file.
 733    FileNode* createFile = 0;
 734    FileNode* targetFile;
 735    int error = FileFindOpenId(targetOpenId,true/*forModify*/,&targetFile);
 736    // Create the _new unnamed file object to move to the name
 737    // of the target.
 738    if(!error)
 739    {
 740        error = FileCreate(newCreateOpenId,targetFile->fileType,writeTime,
 741            &createFile);
 742    }
 743    if(!error)
 744    {
 745        // Delete the target and move _new file to target name.
 746        error = FileReplace(createFile,targetFile,false/*deleteSource*/,
 747            openAttribs);
 748    }
 749    if(createFile && !createFile->open)
 750    {
 751        delete createFile;
 752    }
 753    return error;
 754}
 755
 756int/*error*/ CCALL AvfsFormatter::Volume::Move(
 757    int64_t sourceOpenId,
 758    int64_t sourceParentFileId,
 759    const PfmNamePart* sourceEndName,
 760    const PfmNamePart* targetNameParts,
 761    size_t targetNamePartCount,
 762    bool deleteSource,
 763    int64_t writeTime,
 764    int64_t newExistingOpenId,
 765    bool* existed,
 766    PfmOpenAttribs* openAttribs,
 767    int64_t* parentFileId,
 768    wchar_t** endName)
 769{
 770    // Open an existing target file, or move a previously opened
 771    // file to a _new target name if the target does not exist.
 772    FileNode* sourceFile = 0;
 773    int error = FileFindOpenId(sourceOpenId,true/*forModify*/,&sourceFile);
 774    if(!error)
 775    {
 776        // Use common open/move logic.
 777        error = FileOpenOrMove(targetNameParts,targetNamePartCount,
 778            sourceFile,newExistingOpenId,deleteSource,pfmErrorNotFound,
 779            existed,openAttribs,endName);
 780        ASSERT(error != pfmErrorNotFound);
 781    }
 782    return error;
 783}
 784
 785int/*error*/ CCALL AvfsFormatter::Volume::MoveReplace(
 786    int64_t sourceOpenId,
 787    int64_t sourceParentFileId,
 788    const PfmNamePart* sourceEndName,
 789    int64_t targetOpenId,
 790    int64_t targetParentFileId,
 791    const PfmNamePart* targetEndName,
 792    bool deleteSource,
 793    int64_t writeTime)
 794{
 795    // Delete an previously opened target file and move a
 796    // previously opened source file to the target files name.
 797    FileNode* sourceFile = 0;
 798    FileNode* targetFile;
 799    int error = FileFindOpenId(targetOpenId,true/*forModify*/,&targetFile);
 800    if(!error)
 801    {
 802        error = FileFindOpenId(sourceOpenId,true/*forModify*/,&sourceFile);
 803    }
 804    if(!error)
 805    {
 806        // Delete the target and move _new file to target
 807        // name.
 808        error = FileReplace(sourceFile,targetFile,deleteSource,0);
 809    }
 810    return error;
 811}
 812
 813int/*error*/ CCALL AvfsFormatter::Volume::Delete(
 814    int64_t openId,
 815    int64_t parentFileId,
 816    const PfmNamePart* endName,
 817    int64_t writeTime)
 818{
 819    // Delete a previously opened file.
 820    FileNode* file;
 821    int error = FileFindOpenId(openId,true/*forModify*/,&file);
 822    if(!error)
 823    {
 824        // Mark file deleted by freeing name.
 825        ssfree(file->name);
 826        file->name = 0;
 827    }
 828    return error;
 829}
 830
 831int/*error*/ CCALL AvfsFormatter::Volume::Close(
 832    int64_t openId,
 833    int64_t openSequence)
 834{
 835    // If no more references to file then free associated
 836    // resources.
 837    FileNode* file;
 838    int error = FileFindOpenId(openId,false/*forModify*/,&file);
 839    if(!error)
 840    {
 841        // Driver avoids race conditions between open and close
 842        // by returning highest open sequence it had seen for
 843        // the file when the close request was generated. If the
 844        // supplied open sequence is less than the last one
 845        // returned then the file is still open.
 846        if(openSequence >= file->openSequence)
 847        {
 848            file->open = false;
 849            switch(file->fileType)
 850            {
 851            case fileTypeFolder:
 852                // Clean up any lists when folder is closed.
 853                while(firstList)
 854                {
 855                    delete firstList;
 856                }
 857                break;
 858            case fileTypeLog:
 859                // Use a new open ID on next open, so cached
 860                // data will be discarded. This is a workaround
 861                // for the lack of support in PFM for externally
 862                // modified files.
 863                file->openId = 0;
 864                break;
 865            }
 866            // If file has no name then it is deleted so
 867            // can now be freed.
 868            if(!file->name)
 869            {
 870                delete file;
 871            }
 872        }
 873    }
 874    return error;
 875}
 876
 877int/*error*/ CCALL AvfsFormatter::Volume::FlushFile(
 878    int64_t openId,
 879    uint8_t fileFlags,
 880    int64_t createTime,
 881    int64_t accessTime,
 882    int64_t writeTime,
 883    int64_t changeTime)
 884{
 885    // Update file attributes and commit file data to disk.
 886    FileNode* file;
 887    int error = FileFindOpenId(openId,false/*forModify*/,&file);
 888    if(!error)
 889    {
 890        if(writeTime != pfmTimeInvalid)
 891        {
 892            file->writeTime = writeTime;
 893        }
 894    }
 895    return error;
 896}
 897
 898int/*error*/ CCALL AvfsFormatter::Volume::List(
 899    int64_t openId,
 900    int64_t listId,
 901    PfmMarshallerListResult* listResult)
 902{
 903    // List the contents of a folder.
 904    size_t position = 0;
 905    FileNode* file;
 906    ListState* list = 0;
 907    int error = FileFindOpenId(openId,false/*forModify*/,&file);
 908    bool needMore = true;
 909    bool added = true;
 910    PfmAttribs attribs;
 911    // AvfsFormatter only supports one folder, so just need to verify
 912    // the file type.
 913    if(!error && file->fileType != fileTypeFolder)
 914    {
 915        error = pfmErrorAccessDenied;
 916    }
 917    if(!error)
 918    {
 919        // Find the associated list state, or if first time we've
 920        // seen this list ID then create a _new list state.
 921        list = ListFind(listId);
 922        if(!list)
 923        {
 924            list = new(std::nothrow) ListState(this,listId);
 925            if(!list)
 926            {
 927                error = pfmErrorOutOfMemory;
 928            }
 929        }
 930    }
 931    if(!error)
 932    {
 933        // Using simple index to remember position in list.
 934        file = firstFile;
 935        while(file && position < list->position)
 936        {
 937            file = file->nextFile;
 938        }
 939        while(file && added && needMore)
 940        {
 941            memset(&attribs,0,sizeof(attribs));
 942            attribs.fileType = pfmFileTypeFile;
 943            if(file->fileType == fileTypeFolder)
 944            {
 945                attribs.fileType = pfmFileTypeFolder;
 946            }
 947            attribs.fileSize = file->fileSize;
 948            attribs.writeTime = file->writeTime;
 949            if(file->fileType == fileTypeMedia)
 950            {
 951                attribs.extraFlags |=
 952                    (pfmExtraFlagOffline|pfmExtraFlagNoIndex);
 953            }
 954            added = listResult->Add(&attribs,file->name,&needMore);
 955            list->position += !!added;
 956            file = file->nextFile;
 957        }
 958        if(!file)
 959        {
 960            // Save the driver calling us back again if there are
 961            // no more files.
 962            listResult->NoMore();
 963        }
 964    }
 965    return error;
 966}
 967
 968int/*error*/ CCALL AvfsFormatter::Volume::ListEnd(
 969    int64_t openId,
 970    int64_t listId)
 971{
 972    // Clean up any resources associated with an open folder
 973    // list operation.
 974    FileNode* file;
 975    ListState* list;
 976    int error = FileFindOpenId(openId,false/*forModify*/,&file);
 977    if(!error && file->fileType == fileTypeFolder)
 978    {
 979        list = ListFind(listId);
 980        if(!list)
 981        {
 982            error = pfmErrorInvalid;
 983        }
 984        else
 985        {
 986            delete list;
 987        }
 988    }
 989    return error;
 990}
 991
 992int/*error*/ CCALL AvfsFormatter::Volume::Read(
 993    int64_t openId,
 994    uint64_t fileOffset,
 995    void* buffer,
 996    size_t requestedSize,
 997    size_t* outActualSize)
 998{
 999    // Read data from open file.
1000    size_t actualSize = 0;
1001    uint64_t maxSize;
1002    FileNode* file;
1003    int error = FileFindOpenId(openId,false/*forModify*/,&file);
1004    if(!error)
1005    {
1006        maxSize = file->fileSize;
1007        if(fileOffset < maxSize)
1008        {
1009            maxSize -= fileOffset;
1010            actualSize = requestedSize;
1011            if(maxSize < requestedSize)
1012            {
1013                actualSize = static_cast<size_t>(maxSize);
1014            }
1015        }
1016        switch(file->fileType)
1017        {
1018        default:
1019        case fileTypeFolder:
1020            error = pfmErrorAccessDenied;
1021            break;
1022        case fileTypeScript:
1023        case fileTypeLog:
1024            // Echo data for script and log files.
1025            if(actualSize)
1026            {
1027                ASSERT(file->fileSize <= maxScriptDataSize);
1028                memcpy(buffer,static_cast<uint8_t*>(file->fileData)+
1029                    static_cast<size_t>(fileOffset),actualSize);
1030            }
1031            break;
1032        case fileTypeMedia:
1033            // Watch for deleted media files.
1034            error = pfmErrorDeleted;
1035            if(file->mediaFile)
1036            {
1037                error = 0;
1038                if(actualSize)
1039                {
1040                    // Let media logic generate data for media files.
1041                    if(!file->mediaFile->ReadMedia(this,fileOffset,buffer,
1042                        actualSize))
1043                    {
1044                        actualSize = 0;
1045                        error = pfmErrorCorruptData;
1046                    }
1047                }
1048            }
1049            break;
1050        }
1051    }
1052    if(error)
1053    {
1054        actualSize = 0;
1055    }
1056    *outActualSize = actualSize;
1057    return error;
1058}
1059
1060int/*error*/ CCALL AvfsFormatter::Volume::Write(
1061    int64_t openId,
1062    uint64_t fileOffset,
1063    const void* buffer,
1064    size_t requestedSize,
1065    size_t* outActualSize)
1066{
1067    // Write data to open file.
1068    size_t actualSize = 0;
1069    FileNode* file;
1070    int error = FileFindOpenId(openId,true/*forModify*/,&file);
1071    if(!error)
1072    {
1073        error = FileWrite(file,maxScriptDataSize,fileOffset,buffer,
1074            requestedSize,&actualSize);
1075    }
1076    *outActualSize = actualSize;
1077    return error;
1078}
1079
1080int/*error*/ CCALL AvfsFormatter::Volume::SetSize(
1081    int64_t openId,
1082    uint64_t fileSize)
1083{
1084    // Extend or truncate file data.
1085    FileNode* file;
1086    int error = FileFindOpenId(openId,true/*forModify*/,&file);
1087    if(!error)
1088    {
1089        if(fileSize < file->fileSize)
1090        {
1091            file->fileSize = fileSize;
1092        }
1093        else
1094        {
1095            error = FileWrite(file,maxScriptDataSize,fileSize,0,0,0);
1096        }
1097    }
1098    return error;
1099}
1100
1101int/*error*/ CCALL AvfsFormatter::Volume::Capacity(
1102    uint64_t* outTotalCapacity,
1103    uint64_t* availableCapacity)
1104{
1105    // Return total and available capacity of media.
1106    uint64_t totalCapacity = *availableCapacity = 1000000;
1107    // Won't make much difference, but return capacity that
1108    // accounts for the size of all the virtual files.
1109    FileNode* file = firstFile;
1110    while(file)
1111    {
1112        totalCapacity += file->fileSize;
1113        file = file->nextFile;
1114    }
1115    *outTotalCapacity = totalCapacity;
1116    return 0;
1117}
1118
1119int/*error*/ CCALL AvfsFormatter::Volume::FlushMedia(
1120    bool* mediaClean)
1121{
1122    // Called after ~1 sec of inactivity. Flush modified data to
1123    // disk.
1124
1125    // Check to see if echo script has been modified and
1126    // if so, copy to real script file and have media logic
1127    // process the _new script.
1128    int error = 0;
1129    HANDLE handle;
1130    size_t transferredSize;
1131    FileNode* newLogFile;
1132    // Find a script file that matches the original
1133    // echo file name.
1134    scriptFile = firstFile;
1135    while(scriptFile && (scriptFile->fileType != fileTypeScript ||
1136        sscmpi(scriptFile->name,scriptEndName) != 0))
1137    {
1138        scriptFile = scriptFile->nextFile;
1139    }
1140    // If the write time of the echo script file doesn't
1141    // match the real script file then it has been modified
1142    // and media needs to reinitialize.
1143    if(scriptFile && scriptFile->writeTime != scriptWriteTime)
1144    {
1145        // Rewrite real script file to match the echo script
1146        // file.
1147        ASSERT(scriptFile->fileSize <= maxScriptDataSize);
1148        scriptWriteTime = scriptFile->writeTime;
1149        // If unable to open or write the real script file
1150        // then will return an error to driver. Driver will
1151        // display a cache write failure notification. Script
1152        // changes will be lost.
1153        error = marshaller->ConvertSystemError(::FileOpenWrite(
1154            scriptFileName,&handle));
1155        if(!error)
1156        {
1157            if(scriptFile->fileSize)
1158            {
1159                error = marshaller->ConvertSystemError(::FileWrite(
1160                    handle,scriptFile->fileData,
1161                    static_cast<size_t>(scriptFile->fileSize),
1162                    &transferredSize));
1163                if(!error && transferredSize != scriptFile->fileSize)
1164                {
1165                    error = pfmErrorNoSpace;
1166                }
1167            }
1168            if(!error)
1169            {
1170                error = marshaller->ConvertSystemError(::FileSetSize(
1171                    handle,scriptFile->fileSize));
1172            }
1173            ::FileSetTime(handle,scriptWriteTime);
1174            ::FileClose(handle);
1175        }
1176        if(!error)
1177        {
1178            // Delete the existing media files. Any that are still open
1179            // will stick around until closed but won't work and will
1180            // not have names.
1181            DeleteMediaFiles();
1182
1183            // New log file.
1184            newLogFile = new(std::nothrow) FileNode(this,0,fileTypeLog,
1185                pfmTimeInvalid);
1186            if(newLogFile)
1187            {
1188                newLogFile->name = ssdup(L"error.log");
1189                if(!newLogFile->name)
1190                {
1191                    delete newLogFile;
1192                }
1193                else
1194                {
1195                    ssfree(logFile->name);
1196                    logFile->name = 0;
1197                    if(!logFile->open)
1198                    {
1199                        delete logFile;
1200                    }
1201                    logFile = newLogFile;
1202                }
1203            }
1204
1205            // No error handling here for bad scripts. User can edit
1206            // script to fix errors. Media logic will need to report
1207            // errors through error log file.
1208            AvfsProcessScript(this,this);
1209        }
1210    }
1211    scriptFile = 0;
1212    *mediaClean = true;
1213    return 0;
1214}
1215
1216int/*error*/ CCALL AvfsFormatter::Volume::Control(
1217    int64_t openId,
1218    int8_t accessLevel,
1219    int controlCode,
1220    const void* input,
1221    size_t inputSize,
1222    void* output,
1223    size_t maxOutputSize,
1224    size_t* outputSize)
1225{
1226    // If we needed to tunnel control codes through the file system
1227    // using the PFM API then this is where they would end up.
1228    return pfmErrorInvalid;
1229}
1230
1231int/*error*/ CCALL AvfsFormatter::Volume::MediaInfo(
1232    int64_t openId,
1233    PfmMediaInfo* mediaInfo,
1234    wchar_t** mediaLabel)
1235{
1236    return 0;
1237}
1238
1239AvfsFormatter::Volume::Volume(void)
1240{
1241    marshaller = 0;
1242    scriptFileName = 0;
1243    scriptEndName = 0;
1244    mediaName = 0;
1245    scriptWritable = false;
1246    scriptWriteTime = 0;
1247    scriptFile = 0;
1248    firstList = 0;
1249    firstFile = 0;
1250}
1251
1252AvfsFormatter::Volume::~Volume(void)
1253{
1254    if(marshaller)
1255    {
1256        marshaller->Release();
1257    }
1258    ssfree(scriptFileName);
1259    ssfree(mediaName);
1260    while(firstList)
1261    {
1262        delete firstList;
1263    }
1264    while(firstFile)
1265    {
1266        delete firstFile;
1267    }
1268}
1269
1270int/*systemError*/ AvfsFormatter::Volume::Init(
1271    const wchar_t* inScriptFileName)
1272{
1273    wchar_t* dot;
1274    FileNode* folder;
1275    HANDLE scriptHandle = INVALID_HANDLE_VALUE;
1276    uint64_t scriptFileSize;
1277    size_t scriptDataSize = 0;
1278    int error = PfmMarshallerFactory(&marshaller);
1279    if(!error)
1280    {
1281        marshaller->SetTrace(L"VSFS-PFM");
1282        scriptFileName = ssdup(inScriptFileName);
1283        scriptEndName = ssrchr(scriptFileName,'\\');
1284        if(scriptEndName)
1285        {
1286            scriptEndName ++;
1287        }
1288        else
1289        {
1290            scriptEndName = scriptFileName;
1291        }
1292        mediaName = ssdup(scriptEndName);
1293        dot = ssrchr(mediaName,'.');
1294        if(dot && dot > mediaName)
1295        {
1296            *dot = 0;
1297        }
1298        if(!scriptFileName || !mediaName)
1299        {
1300            error = ERROR_OUTOFMEMORY;
1301        }
1302    }
1303    if(!error)
1304    {
1305        // Create root folder file. Do this first so it will show
1306        // up first in listing.
1307        error = ERROR_OUTOFMEMORY;
1308        folder = new(std::nothrow) FileNode(this,0,fileTypeFolder,
1309            pfmTimeInvalid);
1310        if(folder)
1311        {
1312            folder->name = ssdup(L".");
1313            if(folder->name)
1314            {
1315                error = 0;
1316            }
1317        }
1318    }
1319    if(!error)
1320    {
1321        // Create initial script echo file, to allow reading/writing
1322        // the script while mounted.
1323        error = ERROR_OUTOFMEMORY;
1324        scriptFile = new(std::nothrow) FileNode(this,0,fileTypeScript,
1325            pfmTimeInvalid);
1326        if(scriptFile)
1327        {
1328            scriptFile->name = ssdup(scriptEndName);
1329            if(scriptFile->name)
1330            {
1331                error = 0;
1332            }
1333        }
1334        if(!error)
1335        {
1336            // If real script file can not be opened for write access
1337            // then do not allow the echo script file to be modified.
1338            scriptWritable = true;
1339            error = ::FileOpenWrite(scriptFileName,&scriptHandle);
1340            if(error)
1341            {
1342                scriptWritable = false;
1343                error = ::FileOpenRead(scriptFileName,&scriptHandle);
1344            }
1345        }
1346        if(!error)
1347        {
1348            scriptFileSize = ::FileGetSize(scriptHandle);
1349            scriptDataSize = static_cast<size_t>(scriptFileSize);
1350            if(scriptFileSize > maxScriptDataSize)
1351            {
1352                error = ERROR_HANDLE_DISK_FULL;
1353            }
1354        }
1355        if(!error)
1356        {
1357            // Save a copy of the script data for the volume and
1358            // for the echo file.
1359            scriptFile->fileData = malloc(scriptDataSize);
1360            if(!scriptFile->fileData)
1361            {
1362                error = ERROR_OUTOFMEMORY;
1363            }
1364            else
1365            {
1366                memset(scriptFile->fileData,0,scriptDataSize);
1367                ::FileSetPointer(scriptHandle,0);
1368                ::FileRead(scriptHandle,scriptFile->fileData,scriptDataSize,0);
1369                scriptFile->maxFileDataSize = scriptDataSize;
1370                scriptFile->fileSize = scriptDataSize;
1371            }
1372            ::FileGetTime(scriptHandle,&scriptWriteTime);
1373            scriptFile->writeTime = scriptWriteTime;
1374        }
1375    }
1376    if(!error)
1377    {
1378        // Create log file to report errors and log actions.
1379        error = ERROR_OUTOFMEMORY;
1380        logFile = new(std::nothrow) FileNode(this,0,fileTypeLog,
1381            pfmTimeInvalid);
1382        if(logFile)
1383        {
1384            logFile->name = ssdup(L"error.log");
1385            if(logFile->name)
1386            {
1387                error = 0;
1388            }
1389        }
1390    }
1391    ::FileClose(scriptHandle);
1392    if(!error)
1393    {
1394        // No error handling here for bad scripts. User can edit
1395        // script to fix errors. Media logic will need to report
1396        // errors through error log file.
1397        AvfsProcessScript(this,this);
1398    }
1399    scriptFile = 0;
1400    return error;
1401}
1402
1403void AvfsFormatter::Volume::Destroy(void)
1404{
1405    DeleteMediaFiles();
1406}
1407
1408void CCALL AvfsFormatter::Release(void)
1409{
1410}
1411
1412int/*systemError*/ CCALL AvfsFormatter::Identify(
1413    HANDLE statusWrite,
1414    const wchar_t* mountFileName,
1415    HANDLE mountFileHandle,
1416    const void* mountFileData,
1417    size_t mountFileDataSize)
1418{
1419    PfmMarshaller* marshaller;
1420    int error = PfmMarshallerFactory(&marshaller);
1421    if(!error)
1422    {
1423        // Mount any file with an .vpy extension
1424        if(sscmpi(ssrchr(mountFileName,'.'),L".vpy") != 0)
1425        {
1426            // or that has "#!vpy" at start of file.
1427            error = marshaller->Identify(
1428                static_cast<const char*>(mountFileData),
1429                mountFileDataSize/sizeof(char),avisynthFileTypeTag);
1430        }
1431        marshaller->Release();
1432    }
1433    return error;
1434}
1435
1436int/*systemError*/ CCALL AvfsFormatter::Serve(
1437    const wchar_t* mountFileName,
1438    int mountFlags,
1439    HANDLE read,
1440    HANDLE write)
1441{
1442    Volume volume;
1443    int error = volume.Init(mountFileName);
1444    if(!error)
1445    {
1446        volume.marshaller->ServeReadWrite(&volume,0,avfsFormatterName,read,write);
1447    }
1448    volume.Destroy();
1449    return error;
1450}
1451
1452extern "C" int/*systemError*/ CCALL PfmFormatterFactory1(
1453    PfmFormatter** formatter)
1454{
1455    *formatter = &avfsFormatter;
1456    return 0;
1457}