1 using System;
  2 using System.Collections;
  3 using System.Threading;
  4 using cmsx.machine;
  5 using cmsx.util;
  6 
  7 namespace cmsx.kernel
  8 {
  9     public const uint INVALID_FILE_ATTRIBUTES = cast<uint>(-1);
 10     public const uint FILE_ATTRIBUTE_ARCHIVE = 32u;
 11     public const uint FILE_ATTRIBUTE_DIRECTORY = 16u;
 12     public const uint FILE_ATTRIBUTE_NORMAL = 128u;
 13 
 14     public class HostFileSystem : FileSystem
 15     {
 16         public HostFileSystem(int index_const string& hostPath_const string& mountDirPath_) : 
 17             base(hostPath_index_true)blockManager(this)inodeManager(this)hostPath(hostPath_)mountDirPath(mountDirPath_)
 18         {
 19         }
 20         public override nothrow bool IsHostFileSystem() const
 21         {
 22             return true;
 23         }
 24         public override void Init()
 25         {
 26             if (Log())
 27             {
 28                 LogMessage("fs.host""init.begin.hostPath=" + hostPath + ".mountDirPath=" + mountDirPath);
 29             }
 30             FileSystem* rootFS = GetMountTable().GetFileSystem(0);
 31             INode* parent = null;
 32             DirectorySlot freeDirectorySlot;
 33             string name;
 34             INode* mountDirINode = rootFS->GetINodeManager()->PathToINode(nullmountDirPathPathToINodeFlags.ignoreMountPointparentfreeDirectorySlotname);
 35             if (mountDirINode == null)
 36             {
 37                 rootFS->MakeDirectory(nullmountDirPathcast<int>(ParseOctal("755")));
 38                 mountDirINode = rootFS->GetINodeManager()->PathToINode(nullmountDirPathPathToINodeFlags.ignoreMountPointparentfreeDirectorySlotname);
 39                 mountDirINode->SetFlag(INode.Flags.mountPoint);
 40                 mountDirINode->SetFlag(INode.Flags.dirty);
 41             }
 42             INodePutter putter(mountDirINode);
 43             mountDirINodeKey = mountDirINode->Key();
 44             if (Log())
 45             {
 46                 LogMessage("fs.host""init.end.inode=" + mountDirINode->ToString());
 47             }
 48         }
 49         public override int Open(Process* processINode* inodeOpenFlags flagsconst string& pathINodePutter& inodePutter)
 50         {
 51             HostFile* hostFile = GetHostFile(inode->Key().fsNumber);
 52             void* fileHandle = hostFile->GetFileHandle();
 53             long fileSize = OsGetFileSize(fileHandle);
 54             inode->SetFileSize(fileSize);
 55             return base->Open(processinodeflagspathinodePutter);
 56         }
 57         public override int OpenDirectory(Process* processconst string& pathINode* dirINodeINodePutter& inodePutter)
 58         {
 59             int fd = process->fileTable.GetEmptyFileSlot();
 60             process->fileTable.SetFile(fdnew HostDirectoryFile(dirINode));
 61             inodePutter.ResetINode();
 62             dirINode->ResetFlag(INode.Flags.locked);
 63             return fd;
 64         }
 65         public override int ReadDirectory(Process* processDirectoryFile* dirFileulong inodeNumberAddressulong entryNameAddress)
 66         {
 67             #assert(dirFile is HostDirectoryFile*);
 68             HostDirectoryFile* hostDirFile = cast<HostDirectoryFile*>(dirFile);
 69             void* findHandle = hostDirFile->GetFindHandle();
 70             if (findHandle == null)
 71             {
 72                 INode* dirINode = dirFile->DirINode();
 73                 const string& hostFilePath = inodeManager.GetHostFilePath(GetHostFileIndex(dirINode->Key().fsNumber));
 74                 string pathMask = Path.Combine(hostFilePath"*.*");
 75                 char[nameMax] fileName;
 76                 void* findHandle = OsFindFirstFile(pathMask.Chars()&fileName[0]);
 77                 if (findHandle != null)
 78                 {
 79                     hostDirFile->SetFindHandle(findHandle);
 80                     WriteProcessMemory(GetMachine()processinodeNumberAddress0u4uProtection.write);
 81                     WriteProcessMemory(GetMachine()processentryNameAddresscast<byte*>(cast<void*>(&fileName[0]))cast<ulong>(nameMax)Protection.write);
 82                     return 1;
 83                 }
 84                 else
 85                 {
 86                     return 0;
 87                 }
 88             }
 89             else
 90             {
 91                 char[nameMax] fileName;
 92                 bool result = OsFindNextFile(findHandle&fileName[0]);
 93                 if (result)
 94                 {
 95                     WriteProcessMemory(GetMachine()processinodeNumberAddress0u4uProtection.write);
 96                     WriteProcessMemory(GetMachine()processentryNameAddresscast<byte*>(cast<void*>(&fileName[0]))cast<ulong>(nameMax)Protection.write);
 97                     return 1;
 98                 }
 99                 else
100                 {
101                     return 0;
102                 }
103             }
104         }
105         public override void FillINode(INode* inode)
106         {
107             if (inode->Type() == FileType.regular)
108             {
109                 HostFile* hostFile = GetHostFile(inode->Key().fsNumber);
110                 void* fileHandle = hostFile->GetFileHandle();
111                 long fileSize = OsGetFileSize(fileHandle);
112                 inode->SetFileSize(fileSize);
113                 inode->SetUID(0);
114                 inode->SetGID(0);
115                 inode->SetOwnerAccess(cast<Access>(Access.read | Access.write));
116                 inode->SetGroupAccess(cast<Access>(Access.read | Access.write));
117                 inode->SetOtherAccess(cast<Access>(Access.read | Access.write));
118                 byte[8] ctimeBuf;
119                 byte[8] mtimeBuf;
120                 byte[8] atimeBuf;
121                 if (OsGetFileTimes(hostFile->GetHostFilePath().Chars()&ctimeBuf[0]&mtimeBuf[0]&atimeBuf[0]))
122                 {
123                     MemoryReader ctimeReader(&ctimeBuf[0]8);
124                     DateTime ctime = ctimeReader.ReadDateTime();
125                     inode->SetCTime(ctime);
126                     MemoryReader mtimeReader(&mtimeBuf[0]8);
127                     DateTime mtime = mtimeReader.ReadDateTime();
128                     inode->SetMTime(mtime);
129                     MemoryReader atimeReader(&atimeBuf[0]8);
130                     DateTime atime = atimeReader.ReadDateTime();
131                     inode->SetATime(atime);
132                 }
133                 else
134                 {
135                     throw SystemError(EFAIL"could not retrieve file times for host file '" + hostFile->GetHostFilePath() + "'");
136                 }
137             }
138             else if (inode->Type() == FileType.directory)
139             {
140                 inode->SetUID(0);
141                 inode->SetGID(0);
142                 inode->SetOwnerAccess(cast<Access>(Access.read | Access.write | Access.execute));
143                 inode->SetGroupAccess(cast<Access>(Access.read | Access.write | Access.execute));
144                 inode->SetOtherAccess(cast<Access>(Access.read | Access.write | Access.execute));
145                 string hostFilePath = inodeManager.GetHostFilePath(GetHostFileIndex(inode->Key().fsNumber));
146                 byte[8] ctimeBuf;
147                 byte[8] mtimeBuf;
148                 byte[8] atimeBuf;
149                 if (OsGetFileTimes(hostFilePath.Chars()&ctimeBuf[0]&mtimeBuf[0]&atimeBuf[0]))
150                 {
151                     MemoryReader ctimeReader(&ctimeBuf[0]8);
152                     DateTime ctime = ctimeReader.ReadDateTime();
153                     inode->SetCTime(ctime);
154                     MemoryReader mtimeReader(&mtimeBuf[0]8);
155                     DateTime mtime = mtimeReader.ReadDateTime();
156                     inode->SetMTime(mtime);
157                     MemoryReader atimeReader(&atimeBuf[0]8);
158                     DateTime atime = atimeReader.ReadDateTime();
159                     inode->SetATime(atime);
160                 }
161                 else
162                 {
163                     throw SystemError(EFAIL"could not retrieve file times for host file '" + hostFilePath + "'");
164                 }
165             }
166             inode->SetMode();
167         }
168         public override HostFile* GetHostFile(int fsNumber) const
169         {
170             return inodeManager.GetHostFile(GetHostFileIndex(fsNumber));
171         }
172         public override int AllocateBlockNumber()
173         {
174             throw SystemError(EFAIL"cannot allocate block number: file system '" + Name() + "' is read-only");
175         }
176         public override void SetBlockFree(int blockNumber)
177         {
178             throw SystemError(EFAIL"cannot set block free: file system '" + Name() + "' is read-only");
179         }
180         public override int GetFreeINodeNumber()
181         {
182             throw SystemError(EFAIL"cannot get free inode number: file system '" + Name() + "' is read-only");
183         }
184         public override void SetFreeINodeNumber(int inodeNumber)
185         {
186             throw SystemError(EFAIL"cannot set free inode number: file system '" + Name() + "' is read-only");
187         }
188         public override int GetFirstINodeBlockNumber() const
189         {
190             throw SystemError(EFAIL"cannot get first inode block number: file system '" + Name() + "' is read-only");
191         }
192         public override INodeKey GetRootDirINodeKey() const
193         {
194             throw SystemError(EFAIL"cannot get root inode key: file system '" + Name() + "' is read-only");
195         }
196         public override int LastBlockNumber() const
197         {
198             throw SystemError(EFAIL"cannot get last block number: file system '" + Name() + "' is read-only");
199         }
200         public override void SetLastBlockNumber(int blockNumberSuperBlock* sb)
201         {
202             throw SystemError(EFAIL"cannot set last block number: file system '" + Name() + "' is read-only");
203         }
204         public override nothrow bool HasMountDirKey(const INodeKey& mountDirKey) const
205         {
206             return mountDirINodeKey == mountDirKey;
207         }
208         public override nothrow BlockManager* GetBlockManager()
209         {
210             return &blockManager;
211         }
212         public override nothrow INodeManager* GetINodeManager()
213         {
214             return &inodeManager;
215         }
216         public inline nothrow const string& HostPath() const
217         {
218             return hostPath;
219         }
220         private HostBlockManager blockManager;
221         private HostINodeManager inodeManager;
222         private string hostPath;
223         private string mountDirPath;
224         private INodeKey mountDirINodeKey;
225     }
226 
227     public class HostBlockManager : BlockManager
228     {
229         public nothrow HostBlockManager(HostFileSystem* fs_) : fs(fs_)cache(thismaxCachedBlocks)
230         {
231         }
232         public override nothrow string Name() const
233         {
234             return "fs.host.bmgr";
235         }
236         public override Block* GetBlock(const BlockKey& keySuperBlock* sbbool sleepbool setOwner)
237         {
238             if (Log())
239             {
240                 LogMessage("fs.host.bmgr.getblock.begin"key.ToString());
241             }
242             while (true)
243             {
244                 HashMap<BlockKeyBlock*BlockKeyHash>.ConstIterator it = blockMap.CFind(key);
245                 if (it != blockMap.CEnd())
246                 {
247                     Block* block = it->second;
248                     if (block->GetFlag(Block.Flags.locked))
249                     {
250                         if (!sleep)
251                         {
252                             return null;
253                         }
254                         void* fiberData = OsGetFiberData();
255                         Process* process = cast<Process*>(fiberData);
256                         if (block->Owner() != process)
257                         {
258                             block->AddWaitingProcess(process);
259                             SleepProcess(processblockUnlockedEventcast<ulong>(cast<void*>(block))0u);
260                             continue;
261                         }
262                         else
263                         {
264                             if (Log())
265                             {
266                                 LogMessage("fs.host.bmgr.getblock.end.found.sameOwner"block->ToString());
267                             }
268                             return block;
269                         }
270                     }
271                     else
272                     {
273                         block->SetFlag(Block.Flags.locked);
274                         if (setOwner)
275                         {
276                             void* fiberData = OsGetFiberData();
277                             Process* process = cast<Process*>(fiberData);
278                             block->SetOwner(process);
279                         }
280                         else
281                         {
282                             block->SetOwner(null);
283                         }
284                         RemoveBlockFromFreeList(block);
285                         if (Log())
286                         {
287                             LogMessage("fs.host.bmgr.getblock.end.found.notLocked"block->ToString());
288                         }
289                         return block;
290                     }
291                 }
292                 else
293                 {
294                     LinkedList<Block*>* list = cache.GetFreeBlockList();
295                     #assert(list != null);
296                     if (list->IsEmpty())
297                     {
298                         if (!sleep)
299                         {
300                             return null;
301                         }
302                         void* fiberData = OsGetFiberData();
303                         Process* process = cast<Process*>(fiberData);
304                         AddWaitingProcess(process);
305                         SleepProcess(processanyBlockFreeEvent0u0u);
306                         continue;
307                     }
308                     else
309                     {
310                         Block* block = list->Front();
311                         list->RemoveFirst();
312                         block->SetIterator(list->End());
313                         blockMap.Remove(block->Key());
314                         block->SetKey(key);
315                         blockMap[key] = block;
316                         block->ResetFlags();
317                         block->SetFlag(Block.Flags.locked);
318                         if (setOwner)
319                         {
320                             void* fiberData = OsGetFiberData();
321                             Process* process = cast<Process*>(fiberData);
322                             block->SetOwner(process);
323                         }
324                         else
325                         {
326                             block->SetOwner(null);
327                         }
328                         if (Log())
329                         {
330                             LogMessage("fs.host.bmgr.getblock.end.free"block->ToString());
331                         }
332                         return block;
333                     }
334                 }
335             }
336         }
337         public override nothrow void PutBlock(Block* block)
338         {
339             if (Log())
340             {
341                 LogMessage("fs.host.bmgr.putblock.begin"block->ToString());
342             }
343             List<Process*> anyProcesses = GetWaitingProcesses();
344             Kernel& kernel = GetKernel();
345             ProcessTable& processTable = kernel.GetProcessTable();
346             for (Process* process : anyProcesses)
347             {
348                 WakeUpProcess(processTableprocess);
349             }
350             List<Process*> blockProcesses = block->GetWaitingProcesses();
351             for (Process* process : blockProcesses)
352             {
353                 WakeUpProcess(processTableprocess);
354             }
355             if (block->GetFlag(Block.Flags.valid))
356             {
357                 PutBlockToFreeList(blocktrue);
358             }
359             else
360             {
361                 PutBlockToFreeList(blockfalse);
362             }
363             block->ResetFlag(Block.Flags.locked);
364             if (Log())
365             {
366                 LogMessage("fs.host.bmgr.putblock.end"block->ToString());
367             }
368         }
369         public override nothrow Block* ReadBlock(const BlockKey& keySuperBlock* sb)
370         {
371             if (Log())
372             {
373                 LogMessage("fs.host.bmgr.readblock.begin"key.ToString());
374             }
375             Block* block = GetBlock(keysb);
376             if (block->GetFlag(Block.Flags.valid))
377             {
378                 if (Log())
379                 {
380                     LogMessage("fs.root.bmgr.readblock.end.cache"block->ToString());
381                 }
382                 return block;
383             }
384             long bytesRead = GetDiskDriver().Read(block);
385             block->SetFlag(Block.Flags.valid);
386             if (Log())
387             {
388                 LogMessage("fs.host.bmgr.readblock.end.read"block->ToString());
389             }
390             return block;
391         }
392         public override void WriteBlock(Block* blockSuperBlock* superBlock)
393         {
394             throw SystemError(EFAIL"block manager for file system '" + fs->Name() + "' cannot write blocks");
395         }
396         public override void FreeBlocks(INode* inode)
397         {
398             throw SystemError(EFAIL"block manager for file system '" + fs->Name() + "' cannot free blocks");
399         }
400         public override int GetBlockNumber(INode* inodeint logicalBlockNumber) const
401         {
402             return logicalBlockNumber;
403         }
404         public override void GetBlockNumber(INode* inodelong offsetint& blockNumberint& blockOffsetbool allocate)
405         {
406             if (Log())
407             {
408                 LogMessage("fs.host.bmgr.getblocknumber.begin"inode->ToString() + ".offset=" + ToString(offset) + ".allocate=" + ToString(allocate));
409             }
410             if (allocate)
411             {
412                 throw SystemError(EFAIL"block manager for file system '" + fs->Name() + " cannot allocate block numbers");
413             }
414             int logicalBlockNumber = cast<int>(offset / blockSize);
415             blockNumber = logicalBlockNumber;
416             blockOffset = cast<int>(offset % blockSize);
417             if (Log())
418             {
419                 LogMessage("fs.host.bmgr.getblocknumber.end""blocknumber=" + ToString(blockNumber) + ".blockoffset=" + ToString(blockOffset));
420             }
421         }
422         public override void Flush()
423         {
424         }
425         private void RemoveBlockFromFreeList(Block* block)
426         {
427             cache.RemoveBlockFromFreeList(block);
428         }
429         private void PutBlockToFreeList(Block* blockbool tail)
430         {
431             cache.PutBlockToFreeList(blocktail);
432         }
433         private LinkedList<Block*>* GetFreeBlockList()
434         {
435             return cache.GetFreeBlockList();
436         }
437         private void AddWaitingProcess(Process* process)
438         {
439             cache.AddWaitingProcess(process);
440         }
441         private List<Process*> GetWaitingProcesses()
442         {
443             return cache.GetWaitingProcesses();
444         }
445         private HostFileSystem* fs;
446         private HashMap<BlockKeyBlock*BlockKeyHash> blockMap;
447         private BlockCache cache;
448     }
449 
450     public class HostINodeManager : INodeManager
451     {
452         public HostINodeManager(HostFileSystem* fs_) : fs(fs_)
453         {
454         }
455         public override nothrow string Name() const
456         {
457             return "fs.host.imgr";
458         }
459         public override INode* GetINode(const INodeKey& key)
460         {
461             if (Log())
462             {
463                 LogMessage("fs.host.imgr.getinode.begin"key.ToString());
464             }
465             INode* inode = new INode(this);
466             inode->SetKey(key);
467             inode->SetUseCount(1);
468             return inode;
469         }
470         public override void PutINode(INode* inode)
471         {
472             if (Log())
473             {
474                 LogMessage("fs.host.imgr.putinode.begin"inode->ToString());
475             }
476             int fsNumber = inode->Key().fsNumber;
477             int hostFileIndex = GetHostFileIndex(fsNumber);
478             string hostFilePath;
479             HashMap<intPair<stringHostFile*>>.ConstIterator it = hostFileIndexPathMap.CFind(hostFileIndex);
480             if (it != hostFileIndexPathMap.CEnd())
481             {
482                 const Pair<stringHostFile*>& hostFilePair = it->second;
483                 hostFilePath = hostFilePair.first;
484                 if (hostFilePair.second != null)
485                 {
486                     GetDiskDriver().RemoveHostFile(hostFilePath);
487                 }
488                 hostFileIndexPathMap.Remove(it->first);
489             }
490             if (!hostFilePath.IsEmpty())
491             {
492                 pathINodeMap.Remove(hostFilePath);
493             }
494             inode->SetUseCount(inode->GetUseCount() - 1);
495             if (inode->GetUseCount() == 0)
496             {
497                 delete inode;
498             }
499         }
500         public override INode* PathToINode(Process* processconst string& path)
501         {
502             if (Log())
503             {
504                 LogMessage("fs.host.imgr.pathtoinode.begin"path);
505             }
506             INode* parent = null;
507             DirectorySlot freeDirectorySlot;
508             string name;
509             return PathToINode(processpathPathToINodeFlags.noneparentfreeDirectorySlotname);
510         }
511         public override INode* PathToINode(Process* processconst string& pathPathToINodeFlags flagsINode*& parentDirectorySlot& freeDirectorySlotstring& name)
512         {
513             string fullPath;
514             if (path.IsEmpty())
515             {
516                 fullPath = fs->HostPath();
517             }
518             else
519             {
520                 fullPath = GetFullPath(Path.Combine(fs->HostPath()path));
521             }
522             HashMap<stringINode*>.ConstIterator it = pathINodeMap.CFind(fullPath);
523             if (it != pathINodeMap.CEnd())
524             {
525                 INode* inode = it->second;
526                 inode->SetUseCount(inode->GetUseCount() + 1);
527                 return inode;
528             }
529             int hostFileIndex = nextHostFileIndex++;
530             INodeKey key(hostFileIndexMakeFSNumber(fs->Index()hostFileIndex));
531             INode* inode = GetINode(key);
532             uint attrs = OsGetFileAttributes(fullPath.Chars());
533             if (attrs == INVALID_FILE_ATTRIBUTES)
534             {
535                 return null;
536             }
537             else
538             {
539                 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0)
540                 {
541                     inode->SetType(FileType.directory);
542                 }
543                 else if ((attrs & (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NORMAL)) != 0)
544                 {
545                     inode->SetType(FileType.regular);
546                 }
547             }
548             pathINodeMap[fullPath] = inode;
549             hostFileIndexPathMap[hostFileIndex] = MakePair(fullPathcast<HostFile*>(null));
550             return inode;
551         }
552         public const string& GetHostFilePath(int hostFileIndex) const
553         {
554             HashMap<intPair<stringHostFile*>>.ConstIterator it = hostFileIndexPathMap.CFind(hostFileIndex);
555             if (it != hostFileIndexPathMap.CEnd())
556             {
557                 return it->second.first;
558             }
559             else
560             {
561                 throw SystemError(EFAIL"host file path for index " + ToString(hostFileIndex) + " not found from file system '" + fs->HostPath() + "'");
562             }
563         }
564         public HostFile* GetHostFile(int hostFileIndex) const
565         {
566             if (Log())
567             {
568                 LogMessage("fs.host.imgr.gethostfile.begin"ToString(hostFileIndex));
569             }
570             HashMap<intPair<stringHostFile*>>.Iterator it = hostFileIndexPathMap.Find(hostFileIndex);
571             if (it != hostFileIndexPathMap.End())
572             {
573                 Pair<stringHostFile*>& hostFilePair = it->second;
574                 if (hostFilePair.second != null)
575                 {
576                     return hostFilePair.second;
577                 }
578                 HostFile* hostFile = GetDiskDriver().GetOrInsertHostFile(hostFilePair.firstfalse);
579                 hostFilePair.second = hostFile;
580                 if (Log())
581                 {
582                     LogMessage("fs.host.imgr.gethostfile""end.hostFilePath=" + hostFilePair.first);
583                 }
584                 return hostFile;
585             }
586             else
587             {
588                 throw SystemError(EFAIL"host file for index " + ToString(hostFileIndex) + " not found from file system '" + fs->HostPath() + "'");
589             }
590         }
591         private HostFileSystem* fs;
592         private HashMap<stringINode*> pathINodeMap;
593         private HashMap<intPair<stringHostFile*>> hostFileIndexPathMap;
594         private int nextHostFileIndex;
595     }
596 }