PageRenderTime 57ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/src/vsfs/vsfspfm.cpp

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