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(null, mountDirPath, PathToINodeFlags.ignoreMountPoint, parent, freeDirectorySlot, name);
35 if (mountDirINode == null)
36 {
37 rootFS->MakeDirectory(null, mountDirPath, cast<int>(ParseOctal("755")));
38 mountDirINode = rootFS->GetINodeManager()->PathToINode(null, mountDirPath, PathToINodeFlags.ignoreMountPoint, parent, freeDirectorySlot, name);
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* process, INode* inode, OpenFlags flags, const string& path, INodePutter& 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(process, inode, flags, path, inodePutter);
56 }
57 public override int OpenDirectory(Process* process, const string& path, INode* dirINode, INodePutter& inodePutter)
58 {
59 int fd = process->fileTable.GetEmptyFileSlot();
60 process->fileTable.SetFile(fd, new HostDirectoryFile(dirINode));
61 inodePutter.ResetINode();
62 dirINode->ResetFlag(INode.Flags.locked);
63 return fd;
64 }
65 public override int ReadDirectory(Process* process, DirectoryFile* dirFile, ulong inodeNumberAddress, ulong 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(), process, inodeNumberAddress, 0u, 4u, Protection.write);
81 WriteProcessMemory(GetMachine(), process, entryNameAddress, cast<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(), process, inodeNumberAddress, 0u, 4u, Protection.write);
96 WriteProcessMemory(GetMachine(), process, entryNameAddress, cast<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 blockNumber, SuperBlock* 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(this, maxCachedBlocks)
230 {
231 }
232 public override nothrow string Name() const
233 {
234 return "fs.host.bmgr";
235 }
236 public override Block* GetBlock(const BlockKey& key, SuperBlock* sb, bool sleep, bool setOwner)
237 {
238 if (Log())
239 {
240 LogMessage("fs.host.bmgr.getblock.begin", key.ToString());
241 }
242 while (true)
243 {
244 HashMap<BlockKey, Block*, 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(process, blockUnlockedEvent, cast<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(process, anyBlockFreeEvent, 0u, 0u);
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(processTable, process);
349 }
350 List<Process*> blockProcesses = block->GetWaitingProcesses();
351 for (Process* process : blockProcesses)
352 {
353 WakeUpProcess(processTable, process);
354 }
355 if (block->GetFlag(Block.Flags.valid))
356 {
357 PutBlockToFreeList(block, true);
358 }
359 else
360 {
361 PutBlockToFreeList(block, false);
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& key, SuperBlock* sb)
370 {
371 if (Log())
372 {
373 LogMessage("fs.host.bmgr.readblock.begin", key.ToString());
374 }
375 Block* block = GetBlock(key, sb);
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* block, SuperBlock* 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* inode, int logicalBlockNumber) const
401 {
402 return logicalBlockNumber;
403 }
404 public override void GetBlockNumber(INode* inode, long offset, int& blockNumber, int& blockOffset, bool 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* block, bool tail)
430 {
431 cache.PutBlockToFreeList(block, tail);
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<BlockKey, Block*, 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<int, Pair<string, HostFile*>>.ConstIterator it = hostFileIndexPathMap.CFind(hostFileIndex);
480 if (it != hostFileIndexPathMap.CEnd())
481 {
482 const Pair<string, HostFile*>& 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* process, const 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(process, path, PathToINodeFlags.none, parent, freeDirectorySlot, name);
510 }
511 public override INode* PathToINode(Process* process, const string& path, PathToINodeFlags flags, INode*& parent, DirectorySlot& freeDirectorySlot, string& 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<string, INode*>.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(hostFileIndex, MakeFSNumber(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(fullPath, cast<HostFile*>(null));
550 return inode;
551 }
552 public const string& GetHostFilePath(int hostFileIndex) const
553 {
554 HashMap<int, Pair<string, HostFile*>>.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<int, Pair<string, HostFile*>>.Iterator it = hostFileIndexPathMap.Find(hostFileIndex);
571 if (it != hostFileIndexPathMap.End())
572 {
573 Pair<string, HostFile*>& hostFilePair = it->second;
574 if (hostFilePair.second != null)
575 {
576 return hostFilePair.second;
577 }
578 HostFile* hostFile = GetDiskDriver().GetOrInsertHostFile(hostFilePair.first, false);
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<string, INode*> pathINodeMap;
593 private HashMap<int, Pair<string, HostFile*>> hostFileIndexPathMap;
594 private int nextHostFileIndex;
595 }
596 }