1 using System;
  
     2 using System.IO;
  
     3 using cmsx.machine;
  
     4 using cmsx.util;
  
     5 
  
     6 namespace cmsx.kernel
  
     7 {
  
     8     public enum PathToINodeFlags : byte
  
     9     {
  
    10         none = 0u, 
  
    11         createEntry = 1u << 0u, 
  
    12         ignoreMountPoint = 1u << 1u
  
    13     }
  
    14 
  
    15     public const int statBufSize = 56;
  
    16 
  
    17     public abstract class FileSystem
  
    18     {
  
    19         public nothrow FileSystem(const string& name_, int index_, bool readOnly_) : name(name_), index(index_), readOnly(readOnly_)
  
    20         {
  
    21         }
  
    22         public virtual default ~FileSystem();
  
    23         public virtual nothrow bool IsRootFileSystem() const
  
    24         {
  
    25             return false;
  
    26         }
  
    27         public virtual nothrow bool IsHostFileSystem() const
  
    28         {
  
    29             return false;
  
    30         }
  
    31         public abstract void Init();
  
    32         public int Create(Process* process, const string& path, int mode, bool truncate)
  
    33         {
  
    34             if (Log())
  
    35             {
  
    36                 LogMessage("fs.create", path);
  
    37             }
  
    38             DirectorySlot freeDirectorySlot;
  
    39             INode* parent = null;
  
    40             string name;
  
    41             INode* inode = GetINodeManager()->PathToINode(process, path, PathToINodeFlags.createEntry, parent, freeDirectorySlot, name);
  
    42             if (inode != null && inode->Type() != FileType.regular)
  
    43             {
  
    44                 throw SystemError(EINVAL, "cannot create: file type of '" + path + "' is " + FileTypeStr(inode->Type()));
  
    45             }
  
    46             INodePutter parentPutter(parent);
  
    47             INodePutter inodePutter;
  
    48             if (inode != parent)
  
    49             {
  
    50                 inodePutter.ResetINode(inode);
  
    51             }
  
    52             if (name.IsEmpty())
  
    53             {
  
    54                 throw SystemError(EINVAL, "invalid path name '" + path + "'");
  
    55             }
  
    56             if (parent == null)
  
    57             {
  
    58                 throw SystemError(EINVAL, "parent path of path '" + path + "' not found ");
  
    59             }
  
    60             FileSystem* fs = GetMountTable().GetFileSystem(parent->Key().fsNumber);
  
    61             if (fs->IsReadOnly())
  
    62             {
  
    63                 throw SystemError(EINVAL, "cannot create file " + path + "': file system '" + fs->Name() + "' is read-only");
  
    64             }
  
    65             if (inode != null)
  
    66             {
  
    67                 if (truncate)
  
    68                 {
  
    69                     GetBlockManager()->FreeBlocks(inode);
  
    70                     inode->SetFileSize(0);
  
    71                     inode->SetMTime();
  
    72                 }
  
    73             }
  
    74             else
  
    75             {
  
    76                 try
  
    77                 {
  
    78                     parent->CheckPermissions(process->uid, process->gid, Access.write);
  
    79                 }
  
    80                 catch (const SystemError& ex)
  
    81                 {
  
    82                     throw SystemError(EPERM, "cannot write to directory '" + Path.GetDirectoryName(path) + "' using uid " + ToString(process->uid) + ": " + ex.message);
  
    83                 }
  
    84                 FileSystem* fs = GetMountTable().GetFileSystem(parent->Key().fsNumber);
  
    85                 int inodeNumber = fs->GetFreeINodeNumber();
  
    86                 INodeKey key(inodeNumber, parent->Key().fsNumber);
  
    87                 inode = GetINodeManager()->GetINode(key);
  
    88                 inodePutter.ResetINode(inode);
  
    89                 inode->SetType(FileType.regular);
  
    90                 if (mode == 0)
  
    91                 {
  
    92                     mode = EncodeMode(INode.Flags.none, FileType.regular, cast<Access>(Access.read | Access.write), cast<Access>(Access.read | Access.write), cast<Access>(Access.read | Access.write));
  
    93                 }
  
    94                 mode = mode & ~process->umask;
  
    95                 inode->SetAccessMode(mode);
  
    96                 inode->SetUID(process->uid);
  
    97                 inode->SetGID(process->gid);
  
    98                 inode->SetFileSize(0);
  
    99                 inode->SetMTime();
  
   100                 Block* block = null;
  
   101                 if (freeDirectorySlot.blockNumber != invalidBlockNumber)
  
   102                 {
  
   103                     block = GetBlockManager()->ReadBlock(BlockKey(freeDirectorySlot.blockNumber, parent->Key().fsNumber), null);
  
   104                 }
  
   105                 else
  
   106                 {
  
   107                     int blockNumber = 0;
  
   108                     int blockOffset = 0;
  
   109                     GetBlockManager()->GetBlockNumber(parent, freeDirectorySlot.offset, blockNumber, blockOffset, true);
  
   110                     block = GetBlockManager()->GetBlock(BlockKey(blockNumber, parent->Key().fsNumber), null);
  
   111                     block->Clear();
  
   112                 }
  
   113                 BlockPutter blockPutter(block);
  
   114                 DirectoryBlock* directoryBlock = cast<DirectoryBlock*>(block);
  
   115                 bool created = false;
  
   116                 for (int i = 0; i < numDirectoryEntriesInBlock; ++i;)
  
   117                 {
  
   118                     DirectoryEntry entry = directoryBlock->GetDirectoryEntry(i);
  
   119                     if (entry.inodeNumber == 0)
  
   120                     {
  
   121                         entry.inodeNumber = inode->Key().inodeNumber;
  
   122                         entry.name = name.Substring(0, nameMax - 1);
  
   123                         directoryBlock->SetDirectoryEntry(i, entry);
  
   124                         if (freeDirectorySlot.blockNumber == invalidBlockNumber || freeDirectorySlot.offset >= parent->GetFileSize())
  
   125                         {
  
   126                             parent->SetFileSize(parent->GetFileSize() + directoryEntrySize);
  
   127                         }
  
   128                         created = true;
  
   129                         break;
  
   130                     }
  
   131                 }
  
   132                 if (created)
  
   133                 {
  
   134                     directoryBlock->SetFlag(Block.Flags.dirty);
  
   135                     GetBlockManager()->WriteBlock(directoryBlock, null);
  
   136                     parent->SetMTime();
  
   137                 }
  
   138                 else
  
   139                 {
  
   140                     throw SystemError(EFAIL, "no room for directory entry");
  
   141                 }
  
   142             }
  
   143             INodeLock lock(inode);
  
   144             int fd = process->fileTable.GetEmptyFileSlot();
  
   145             FilePtr* filePtr = new FilePtr(inode, cast<OpenFlags>(OpenFlags.read | OpenFlags.write | OpenFlags.create | OpenFlags.truncate));
  
   146             RegularFile* regularFile = new RegularFile(name, filePtr);
  
   147             process->fileTable.SetFile(fd, regularFile);
  
   148             inodePutter.ResetINode();
  
   149             return fd;
  
   150         }
  
   151         public int Open(Process* process, const string& path, OpenFlags flags, int mode)
  
   152         {
  
   153             if (Log())
  
   154             {
  
   155                 LogMessage("fs.open", path);
  
   156             }
  
   157             if ((flags & OpenFlags.create) != OpenFlags.none)
  
   158             {
  
   159                 return Create(process, path, mode, (flags & OpenFlags.truncate) != OpenFlags.none);
  
   160             }
  
   161             INode* inode = GetINodeManager()->PathToINode(process, path);
  
   162             if (inode == null)
  
   163             {
  
   164                 throw SystemError(ENOENT, "path '" + path + "' does not exist");
  
   165             }
  
   166             INodePutter inodePutter(inode);
  
   167             if (inode->Type() != FileType.regular && (flags & OpenFlags.write) != OpenFlags.none)
  
   168             {
  
   169                 throw SystemError(EINVAL, "cannot open '" + path + "': for writing, file type is " + FileTypeStr(inode->Type()));
  
   170             }
  
   171             INodeLock lock(inode);
  
   172             FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
  
   173             if (fs->IsReadOnly() && (flags & (OpenFlags.write | OpenFlags.append)) != OpenFlags.none)
  
   174             {
  
   175                 throw SystemError(EINVAL, "cannot open '" + path + "' for writing: file system '" + fs->Name() + "' is read-only");
  
   176             }
  
   177             return fs->Open(process, inode, flags, path, inodePutter);
  
   178         }
  
   179         public virtual int Open(Process* process, INode* inode, OpenFlags flags, const string& path, INodePutter& inodePutter)
  
   180         {
  
   181             int fd = process->fileTable.GetEmptyFileSlot();
  
   182             FilePtr* filePtr = new FilePtr(inode, flags);
  
   183             RegularFile* regularFile = new RegularFile(Path.GetFileName(path), filePtr);
  
   184             process->fileTable.SetFile(fd, regularFile);
  
   185             inodePutter.ResetINode();
  
   186             return fd;
  
   187         }
  
   188         public void Link(Process* process, const string& sourcePath, const string& targetPath)
  
   189         {
  
   190             if (Log())
  
   191             {
  
   192                 LogMessage("fs.link", sourcePath + "->" + targetPath);
  
   193             }
  
   194             INode* source = GetINodeManager()->PathToINode(process, sourcePath);
  
   195             if (source == null)
  
   196             {
  
   197                 throw SystemError(ENOENT, "source path '" + sourcePath + "' does not exist");
  
   198             }
  
   199             if (Log())
  
   200             {
  
   201                 LogMessage("fs.link", "source=" + source->ToString());
  
   202             }
  
   203             INodePutter sourcePutter(source);
  
   204             DirectorySlot freeDirectorySlot;
  
   205             INode* parent = null;
  
   206             string name;
  
   207             INode* inode = GetINodeManager()->PathToINode(process, targetPath, PathToINodeFlags.createEntry, parent, freeDirectorySlot, name);
  
   208             INodePutter parentPutter(parent);
  
   209             if (inode != null)
  
   210             {
  
   211                 throw SystemError(EEXIST, "target path '" + targetPath + "' already exists");
  
   212             }
  
   213             if (name.IsEmpty())
  
   214             {
  
   215                 throw SystemError(EINVAL, "invalid path name '" + targetPath + "'");
  
   216             }
  
   217             if (parent == null)
  
   218             {
  
   219                 throw SystemError(EINVAL, "parent path of path '" + targetPath + "' not found ");
  
   220             }
  
   221             if (Log())
  
   222             {
  
   223                 LogMessage("fs.link", "parent=" + parent->ToString());
  
   224             }
  
   225             try
  
   226             {
  
   227                 parent->CheckPermissions(process->uid, process->gid, Access.write);
  
   228             }
  
   229             catch (const SystemError& ex)
  
   230             {
  
   231                 throw SystemError(EPERM, "cannot write to directory '" + Path.GetDirectoryName(targetPath) + "' using uid " + ToString(process->uid) + ": " + ex.message);
  
   232             }
  
   233             FileSystem* fs = GetMountTable().GetFileSystem(parent->Key().fsNumber);
  
   234             if (fs->IsReadOnly())
  
   235             {
  
   236                 throw SystemError(EINVAL, "cannot link to path '" + targetPath + "': target file system '" + fs->Name() + "' is read-only");
  
   237             }
  
   238             if (source->Key().fsNumber != parent->Key().fsNumber)
  
   239             {
  
   240                 throw SystemError(EINVAL, "cannot link across file systems: source file system number: " + ToString(source->Key().fsNumber) + ", target file system number: " + ToString(parent->Key().fsNumber));
  
   241             }
  
   242             Block* block = null;
  
   243             if (freeDirectorySlot.blockNumber != invalidBlockNumber)
  
   244             {
  
   245                 block = GetBlockManager()->ReadBlock(BlockKey(freeDirectorySlot.blockNumber, parent->Key().fsNumber), null);
  
   246             }
  
   247             else
  
   248             {
  
   249                 int blockNumber = 0;
  
   250                 int blockOffset = 0;
  
   251                 GetBlockManager()->GetBlockNumber(parent, freeDirectorySlot.offset, blockNumber, blockOffset, true);
  
   252                 block = GetBlockManager()->GetBlock(BlockKey(blockNumber, parent->Key().fsNumber), null);
  
   253                 block->Clear();
  
   254             }
  
   255             BlockPutter blockPutter(block);
  
   256             DirectoryBlock* directoryBlock = cast<DirectoryBlock*>(block);
  
   257             bool created = false;
  
   258             for (int i = 0; i < numDirectoryEntriesInBlock; ++i;)
  
   259             {
  
   260                 DirectoryEntry entry = directoryBlock->GetDirectoryEntry(i);
  
   261                 if (entry.inodeNumber == 0)
  
   262                 {
  
   263                     entry.inodeNumber = source->Key().inodeNumber;
  
   264                     entry.name = name.Substring(0, nameMax - 1);
  
   265                     directoryBlock->SetDirectoryEntry(i, entry);
  
   266                     if (freeDirectorySlot.blockNumber == invalidBlockNumber || freeDirectorySlot.offset >= parent->GetFileSize())
  
   267                     {
  
   268                         parent->SetFileSize(parent->GetFileSize() + directoryEntrySize);
  
   269                     }
  
   270                     created = true;
  
   271                     break;
  
   272                 }
  
   273             }
  
   274             if (created)
  
   275             {
  
   276                 directoryBlock->SetFlag(Block.Flags.dirty);
  
   277                 GetBlockManager()->WriteBlock(directoryBlock, null);
  
   278                 parent->SetMTime();
  
   279                 parent->SetFlag(INode.Flags.dirty);
  
   280                 source->SetNumLinks(source->GetNumLinks() + 1);
  
   281             }
  
   282             else
  
   283             {
  
   284                 throw SystemError(EFAIL, "no room for target directory entry");
  
   285             }
  
   286         }
  
   287         public void Unlink(Process* process, const string& path)
  
   288         {
  
   289             if (Log())
  
   290             {
  
   291                 LogMessage("fs.unlink", path);
  
   292             }
  
   293             string parentPath = Path.GetDirectoryName(path);
  
   294             if (parentPath.IsEmpty())
  
   295             {
  
   296                 if (path.StartsWith("/"))
  
   297                 {
  
   298                     parentPath = "/";
  
   299                 }
  
   300                 else
  
   301                 {
  
   302                     parentPath = ".";
  
   303                 }
  
   304             }
  
   305             string entryName = Path.GetFileName(path);
  
   306             INode* parent = GetINodeManager()->PathToINode(process, parentPath);
  
   307             if (parent == null)
  
   308             {
  
   309                 throw SystemError(ENOENT, "directory '" + parentPath + "' does not exist");
  
   310             }
  
   311             INodePutter parentPutter(parent);
  
   312             parent->ResetFlag(INode.Flags.locked);
  
   313             INode* inode = GetINodeManager()->PathToINode(process, path);
  
   314             parent->SetFlag(INode.Flags.locked);
  
   315             if (inode == null)
  
   316             {
  
   317                 throw SystemError(ENOENT, "path '" + path + "' does not exist");
  
   318             }
  
   319             FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
  
   320             if (fs->IsReadOnly())
  
   321             {
  
   322                 throw SystemError(EINVAL, "cannot unlink: file system '" + fs->Name() + "' is read-only");
  
   323             }
  
   324             try
  
   325             {
  
   326                 parent->CheckPermissions(process->uid, process->gid, Access.write);
  
   327             }
  
   328             catch (const SystemError& ex)
  
   329             {
  
   330                 throw SystemError(EPERM, "cannot write to directory '" + parentPath + "' using uid " + ToString(process->uid) + ": " + ex.message);
  
   331             }
  
   332             INodePutter inodePutter(inode);
  
   333             if (Log())
  
   334             {
  
   335                 LogMessage("fs.unlink", "parent=" + parent->ToString());
  
   336                 LogMessage("fs.unlink", "inode=" + inode->ToString());
  
   337             }
  
   338             long offset = 0;
  
   339             bool entryReset = false;
  
   340             while (!entryReset && offset < parent->GetFileSize())
  
   341             {
  
   342                 int blockNumber = 0;
  
   343                 int blockOffset = 0;
  
   344                 GetBlockManager()->GetBlockNumber(parent, offset, blockNumber, blockOffset, false);
  
   345                 if (blockNumber == 0)
  
   346                 {
  
   347                     throw SystemError(EFAIL, "directory block number not found from inode of '" + parentPath + "'");
  
   348                 }
  
   349                 Block* block = GetBlockManager()->ReadBlock(BlockKey(blockNumber, parent->Key().fsNumber), null);
  
   350                 BlockPutter putter(block);
  
   351                 DirectoryBlock* directoryBlock = cast<DirectoryBlock*>(block);
  
   352                 for (int index = blockOffset / directoryEntrySize; index < numDirectoryEntriesInBlock && offset < parent->GetFileSize(); ++index;)
  
   353                 {
  
   354                     DirectoryEntry entry = directoryBlock->GetDirectoryEntry(index);
  
   355                     if (entry.name == entryName)
  
   356                     {
  
   357                         entry.inodeNumber = 0;
  
   358                         directoryBlock->SetDirectoryEntry(index, entry);
  
   359                         block->SetFlag(Block.Flags.dirty);
  
   360                         GetBlockManager()->WriteBlock(block, null);
  
   361                         parent->SetFlag(INode.Flags.dirty);
  
   362                         entryReset = true;
  
   363                         break;
  
   364                     }
  
   365                     offset = offset + directoryEntrySize;
  
   366                 }
  
   367             }
  
   368             if (!entryReset)
  
   369             {
  
   370                 throw SystemError(EFAIL, "directory entry for '" + path + "' not found");
  
   371             }
  
   372             inode->SetNumLinks(inode->GetNumLinks() - 1);
  
   373             if (Log())
  
   374             {
  
   375                 LogMessage("fs.unlink", "return.inode=" + inode->ToString());
  
   376             }
  
   377         }
  
   378         public void Rename(Process* process, const string& sourcePath, const string& targetPath)
  
   379         {
  
   380             if (Log())
  
   381             {
  
   382                 LogMessage("fs.rename", "source=" + sourcePath + ".target=" + targetPath);
  
   383             }
  
   384             string sourceParentPath = Path.GetDirectoryName(sourcePath);
  
   385             if (sourceParentPath.IsEmpty())
  
   386             {
  
   387                 if (sourcePath.StartsWith("/"))
  
   388                 {
  
   389                     sourceParentPath = "/";
  
   390                 }
  
   391                 else
  
   392                 {
  
   393                     sourceParentPath = ".";
  
   394                 }
  
   395             }
  
   396             string sourceEntryName = Path.GetFileName(sourcePath);
  
   397             string targetParentPath = Path.GetDirectoryName(targetPath);
  
   398             if (targetParentPath.IsEmpty())
  
   399             {
  
   400                 if (targetPath.StartsWith("/"))
  
   401                 {
  
   402                     targetParentPath = "/";
  
   403                 }
  
   404                 else
  
   405                 {
  
   406                     targetParentPath = ".";
  
   407                 }
  
   408             }
  
   409             string targetEntryName = Path.GetFileName(targetPath);
  
   410             bool sameDirectory = false;
  
   411             if (GetFullPath(sourceParentPath) == GetFullPath(targetParentPath))
  
   412             {
  
   413                 sameDirectory = true;
  
   414             }
  
   415             INode* sourceParent = GetINodeManager()->PathToINode(process, sourceParentPath);
  
   416             if (sourceParent == null)
  
   417             {
  
   418                 throw SystemError(ENOENT, "directory '" + sourceParentPath + "' does not exist");
  
   419             }
  
   420             INodePutter sourceParentPutter(sourceParent);
  
   421             FileSystem* sourceFS = GetMountTable().GetFileSystem(sourceParent->Key().fsNumber);
  
   422             if (sourceFS->IsReadOnly())
  
   423             {
  
   424                 throw SystemError(EINVAL, "cannot rename: file system '" + sourceFS->Name() + "' is read-only");
  
   425             }
  
   426             try
  
   427             {
  
   428                 sourceParent->CheckPermissions(process->uid, process->gid, Access.write);
  
   429             }
  
   430             catch (const SystemError& ex)
  
   431             {
  
   432                 throw SystemError(EPERM, "cannot write to directory '" + sourceParentPath + "' using uid " + ToString(process->uid) + ": " + ex.message);
  
   433             }
  
   434             BlockPutter sourcePutter;
  
   435             DirectoryBlock* sourceDirectoryBlock = null;
  
   436             int sourceIndex = -1;
  
   437             long sourceOffset = 0;
  
   438             bool sourceEntryFound = false;
  
   439             while (!sourceEntryFound && sourceOffset < sourceParent->GetFileSize())
  
   440             {
  
   441                 int blockNumber = 0;
  
   442                 int blockOffset = 0;
  
   443                 GetBlockManager()->GetBlockNumber(sourceParent, sourceOffset, blockNumber, blockOffset, false);
  
   444                 if (blockNumber == 0)
  
   445                 {
  
   446                     throw SystemError(EFAIL, "directory block number not found from inode of '" + sourceParentPath + "'");
  
   447                 }
  
   448                 Block* block = GetBlockManager()->ReadBlock(BlockKey(blockNumber, sourceParent->Key().fsNumber), null);
  
   449                 BlockPutter putter(block);
  
   450                 DirectoryBlock* directoryBlock = cast<DirectoryBlock*>(block);
  
   451                 for (int index = blockOffset / directoryEntrySize; index < numDirectoryEntriesInBlock && sourceOffset < sourceParent->GetFileSize(); ++index;)
  
   452                 {
  
   453                     DirectoryEntry entry = directoryBlock->GetDirectoryEntry(index);
  
   454                     if (entry.name == sourceEntryName)
  
   455                     {
  
   456                         if (sameDirectory)
  
   457                         {
  
   458                             entry.name = targetEntryName.Substring(0, nameMax - 1);
  
   459                             directoryBlock->SetDirectoryEntry(index, entry);
  
   460                             directoryBlock->SetFlag(Block.Flags.dirty);
  
   461                             GetBlockManager()->WriteBlock(directoryBlock, null);
  
   462                             sourceParent->SetMTime();
  
   463                             sourceParent->SetFlag(INode.Flags.dirty);
  
   464                             return;
  
   465                         }
  
   466                         else
  
   467                         {
  
   468                             sourcePutter.Reset(putter.Release());
  
   469                             sourceDirectoryBlock = directoryBlock;
  
   470                             sourceIndex = index;
  
   471                             sourceEntryFound = true;
  
   472                             break;
  
   473                         }
  
   474                     }
  
   475                     sourceOffset = sourceOffset + directoryEntrySize;
  
   476                 }
  
   477             }
  
   478             DirectorySlot freeDirectorySlot;
  
   479             INode* targetParent = null;
  
   480             string name;
  
   481             INode* target = GetINodeManager()->PathToINode(process, targetPath, PathToINodeFlags.createEntry, targetParent, freeDirectorySlot, name);
  
   482             INodePutter targetParentPutter(targetParent);
  
   483             INodePutter targetPutter;
  
   484             if (target != null)
  
   485             {
  
   486                 targetPutter.ResetINode(target);
  
   487             }
  
   488             if (targetParent == null)
  
   489             {
  
   490                 throw SystemError(EINVAL, "directory path '" + targetParentPath + "' not found ");
  
   491             }
  
   492             try
  
   493             {
  
   494                 targetParent->CheckPermissions(process->uid, process->gid, Access.write);
  
   495             }
  
   496             catch (const Exception& ex)
  
   497             {
  
   498                 throw SystemError(EPERM, "cannot write to directory '" + targetParentPath + "' using uid " + ToString(process->uid) + ": " + ex.Message());
  
   499             }
  
   500             FileSystem* targetFS = GetMountTable().GetFileSystem(targetParent->Key().fsNumber);
  
   501             if (targetFS->IsReadOnly())
  
   502             {
  
   503                 throw SystemError(EINVAL, "cannot rename: target file system '" + targetFS->Name() + "' is read-only");
  
   504             }
  
   505             if (sourceParent->Key().fsNumber != targetParent->Key().fsNumber)
  
   506             {
  
   507                 throw SystemError(EINVAL, "cannot rename across file systems: source file system number: " + ToString(sourceParent->Key().fsNumber) + ", target file system number: " + ToString(targetParent->Key().fsNumber));
  
   508             }
  
   509             if (target == null)
  
   510             {
  
   511                 Block* targetBlock = null;
  
   512                 if (freeDirectorySlot.blockNumber != invalidBlockNumber)
  
   513                 {
  
   514                     targetBlock = GetBlockManager()->ReadBlock(BlockKey(freeDirectorySlot.blockNumber, targetParent->Key().fsNumber), null);
  
   515                 }
  
   516                 else
  
   517                 {
  
   518                     int blockNumber = 0;
  
   519                     int blockOffset = 0;
  
   520                     GetBlockManager()->GetBlockNumber(targetParent, freeDirectorySlot.offset, blockNumber, blockOffset, true);
  
   521                     targetBlock = GetBlockManager()->GetBlock(BlockKey(blockNumber, targetParent->Key().fsNumber), null);
  
   522                     targetBlock->Clear();
  
   523                 }
  
   524                 BlockPutter targetBlockPutter(targetBlock);
  
   525                 DirectoryBlock* targetDirectoryBlock = cast<DirectoryBlock*>(targetBlock);
  
   526                 bool created = false;
  
   527                 for (int i = 0; i < numDirectoryEntriesInBlock; ++i;)
  
   528                 {
  
   529                     DirectoryEntry targetEntry = targetDirectoryBlock->GetDirectoryEntry(i);
  
   530                     if (targetEntry.inodeNumber == 0)
  
   531                     {
  
   532                         DirectoryEntry sourceEntry = sourceDirectoryBlock->GetDirectoryEntry(sourceIndex);
  
   533                         targetEntry.inodeNumber = sourceEntry.inodeNumber;
  
   534                         targetEntry.name = targetEntryName.Substring(0, nameMax - 1);
  
   535                         targetDirectoryBlock->SetDirectoryEntry(i, targetEntry);
  
   536                         targetDirectoryBlock->SetFlag(Block.Flags.dirty);
  
   537                         if (freeDirectorySlot.blockNumber == invalidBlockNumber || freeDirectorySlot.offset >= targetParent->GetFileSize())
  
   538                         {
  
   539                             targetParent->SetFileSize(targetParent->GetFileSize() + directoryEntrySize);
  
   540                         }
  
   541                         created = true;
  
   542                         break;
  
   543                     }
  
   544                 }
  
   545                 if (created)
  
   546                 {
  
   547                     sourceDirectoryBlock->SetFlag(Block.Flags.dirty);
  
   548                     GetBlockManager()->WriteBlock(sourceDirectoryBlock, null);
  
   549                     targetDirectoryBlock->SetFlag(Block.Flags.dirty);
  
   550                     GetBlockManager()->WriteBlock(targetDirectoryBlock, null);
  
   551                     sourceParent->SetFlag(INode.Flags.dirty);
  
   552                     sourceParent->SetMTime();
  
   553                     targetParent->SetFlag(INode.Flags.dirty);
  
   554                     targetParent->SetMTime();
  
   555                 }
  
   556                 else
  
   557                 {
  
   558                     throw SystemError(EFAIL, "no room for target directory entry");
  
   559                 }
  
   560             }
  
   561             else
  
   562             {
  
   563                 long offset = 0;
  
   564                 bool entrySet = false;
  
   565                 DirectoryBlock* targetDirectoryBlock = null;
  
   566                 while (!entrySet && offset < targetParent->GetFileSize())
  
   567                 {
  
   568                     int blockNumber = 0;
  
   569                     int blockOffset = 0;
  
   570                     GetBlockManager()->GetBlockNumber(targetParent, offset, blockNumber, blockOffset, false);
  
   571                     if (blockNumber == 0)
  
   572                     {
  
   573                         throw SystemError(EFAIL, "directory block number not found from inode of '" + targetParentPath + "'");
  
   574                     }
  
   575                     Block* targetBlock = GetBlockManager()->ReadBlock(BlockKey(blockNumber, targetParent->Key().fsNumber), null);
  
   576                     BlockPutter putter(targetBlock);
  
   577                     #assert(targetBlock is DirectoryBlock*);
  
   578                     targetDirectoryBlock = cast<DirectoryBlock*>(targetBlock);
  
   579                     for (int index = blockOffset / directoryEntrySize; index < numDirectoryEntriesInBlock && offset < targetParent->GetFileSize(); ++index;)
  
   580                     {
  
   581                         DirectoryEntry targetEntry = targetDirectoryBlock->GetDirectoryEntry(index);
  
   582                         if (targetEntry.inodeNumber == target->Key().inodeNumber)
  
   583                         {
  
   584                             target->SetNumLinks(target->GetNumLinks() - 1);
  
   585                             DirectoryEntry sourceEntry = sourceDirectoryBlock->GetDirectoryEntry(sourceIndex);
  
   586                             targetEntry.inodeNumber = sourceEntry.inodeNumber;
  
   587                             targetDirectoryBlock->SetDirectoryEntry(index, targetEntry);
  
   588                             entrySet = true;
  
   589                             break;
  
   590                         }
  
   591                         offset = offset + directoryEntrySize;
  
   592                     }
  
   593                 }
  
   594                 if (entrySet)
  
   595                 {
  
   596                     sourceDirectoryBlock->SetFlag(Block.Flags.dirty);
  
   597                     GetBlockManager()->WriteBlock(sourceDirectoryBlock, null);
  
   598                     targetDirectoryBlock->SetFlag(Block.Flags.dirty);
  
   599                     GetBlockManager()->WriteBlock(targetDirectoryBlock, null);
  
   600                     sourceParent->SetMTime();
  
   601                     targetParent->SetMTime();
  
   602                 }
  
   603                 else
  
   604                 {
  
   605                     throw SystemError(EFAIL, "directory entry for '" + targetPath + "' not found");
  
   606                 }
  
   607             }
  
   608         }
  
   609         public void MakeDirectory(Process* process, const string& path, int mode)
  
   610         {
  
   611             if (Log())
  
   612             {
  
   613                 LogMessage("fs.makedirectory", path);
  
   614             }
  
   615             if (process != null && path == "/")
  
   616             {
  
   617                 throw SystemError(EEXIST, "directory '" + path + "' already exists");
  
   618             }
  
   619             DirectorySlot freeDirectorySlot;
  
   620             INode* parent = null;
  
   621             string name;
  
   622             INode* inode = GetINodeManager()->PathToINode(process, path, PathToINodeFlags.createEntry, parent, freeDirectorySlot, name);
  
   623             INodePutter parentPutter(parent);
  
   624             if (Log())
  
   625             {
  
   626                 if (parent != null)
  
   627                 {
  
   628                     LogMessage("fs.makedirectory", "parent=" + parent->ToString());
  
   629                 }
  
   630             }
  
   631             if (parent != null)
  
   632             {
  
   633                 FileSystem* fs = GetMountTable().GetFileSystem(parent->Key().fsNumber);
  
   634                 if (fs->IsReadOnly())
  
   635                 {
  
   636                     throw SystemError(EINVAL, "cannot make directory: file system '" + fs->Name() + "' is read-only");
  
   637                 }
  
   638             }
  
   639             INodePutter inodePutter;
  
   640             if (inode != parent)
  
   641             {
  
   642                 inodePutter.ResetINode(inode);
  
   643             }
  
   644             if (path != "/")
  
   645             {
  
   646                 if (name.IsEmpty())
  
   647                 {
  
   648                     throw SystemError(EINVAL, "invalid path name '" + path + "'");
  
   649                 }
  
   650                 if (inode != null)
  
   651                 {
  
   652                     throw SystemError(EEXIST, "directory '" + path + "' already exists");
  
   653                 }
  
   654             }
  
   655             if (process != null)
  
   656             {
  
   657                 try
  
   658                 {
  
   659                     parent->CheckPermissions(process->uid, process->gid, Access.write);
  
   660                 }
  
   661                 catch (const SystemError& ex)
  
   662                 {
  
   663                     throw SystemError(EPERM, "cannot write to directory '" + Path.GetDirectoryName(path) + "' using uid " + ToString(process->uid) + ": " + ex.message);
  
   664                 }
  
   665             }
  
   666             if (!name.IsEmpty())
  
   667             {
  
   668                 FileSystem* fs = GetMountTable().GetFileSystem(parent->Key().fsNumber);
  
   669                 int inodeNumber = fs->GetFreeINodeNumber();
  
   670                 INodeKey key(inodeNumber, parent->Key().fsNumber);
  
   671                 inode = GetINodeManager()->GetINode(key);
  
   672                 inodePutter.ResetINode(inode);
  
   673                 Block* block = null;
  
   674                 if (freeDirectorySlot.blockNumber != invalidBlockNumber)
  
   675                 {
  
   676                     block = GetBlockManager()->ReadBlock(BlockKey(freeDirectorySlot.blockNumber, parent->Key().fsNumber), null);
  
   677                 }
  
   678                 else
  
   679                 {
  
   680                     int blockNumber = 0;
  
   681                     int blockOffset = 0;
  
   682                     GetBlockManager()->GetBlockNumber(parent, freeDirectorySlot.offset, blockNumber, blockOffset, true);
  
   683                     block = GetBlockManager()->GetBlock(BlockKey(blockNumber, parent->Key().fsNumber), null);
  
   684                     block->Clear();
  
   685                 }
  
   686                 BlockPutter blockPutter(block);
  
   687                 DirectoryBlock* directoryBlock = cast<DirectoryBlock*>(block);
  
   688                 bool created = false;
  
   689                 for (int i = 0; i < numDirectoryEntriesInBlock; ++i;)
  
   690                 {
  
   691                     DirectoryEntry entry = directoryBlock->GetDirectoryEntry(i);
  
   692                     if (entry.inodeNumber == 0)
  
   693                     {
  
   694                         entry.inodeNumber = inode->Key().inodeNumber;
  
   695                         entry.name = name.Substring(0, nameMax - 1);
  
   696                         directoryBlock->SetDirectoryEntry(i, entry);
  
   697                         if (freeDirectorySlot.blockNumber == invalidBlockNumber || freeDirectorySlot.offset >= parent->GetFileSize())
  
   698                         {
  
   699                             parent->SetFileSize(parent->GetFileSize() + directoryEntrySize);
  
   700                         }
  
   701                         created = true;
  
   702                         break;
  
   703                     }
  
   704                 }
  
   705                 if (created)
  
   706                 {
  
   707                     directoryBlock->SetFlag(Block.Flags.dirty);
  
   708                     GetBlockManager()->WriteBlock(directoryBlock, null);
  
   709                     parent->SetMTime();
  
   710                     parent->SetFlag(INode.Flags.dirty);
  
   711                 }
  
   712                 else
  
   713                 {
  
   714                     throw SystemError(EFAIL, "no room for directory entry");
  
   715                 }
  
   716             }
  
   717             inode->SetType(FileType.directory);
  
   718             if (path == "/" && process == null)
  
   719             {
  
   720                 inode->SetUID(0);
  
   721                 inode->SetGID(0);
  
   722             }
  
   723             else
  
   724             {
  
   725                 if (process == null)
  
   726                 {
  
   727                     inode->SetUID(0);
  
   728                     inode->SetGID(0);
  
   729                 }
  
   730                 else
  
   731                 {
  
   732                     inode->SetUID(process->uid);
  
   733                     inode->SetGID(process->gid);
  
   734                 }
  
   735             }
  
   736             if (mode == 0)
  
   737             {
  
   738                 mode = EncodeMode(INode.Flags.none, FileType.directory, 
  
   739                     cast<Access>(Access.read | Access.write | Access.execute), 
  
   740                     cast<Access>(Access.read | Access.write | Access.execute), 
  
   741                     cast<Access>(Access.read | Access.write | Access.execute));
  
   742             }
  
   743             if (process != null)
  
   744             {
  
   745                 mode = mode & ~process->umask;
  
   746             }
  
   747             inode->SetAccessMode(mode);
  
   748             int blockNumber = 0;
  
   749             int blockOffset = 0;
  
   750             GetBlockManager()->GetBlockNumber(inode, 0, blockNumber, blockOffset, true);
  
   751             Block* blck = GetBlockManager()->GetBlock(BlockKey(blockNumber, inode->Key().fsNumber), null);
  
   752             blck->Clear();
  
   753             BlockPutter putter(blck);
  
   754             DirectoryBlock* dirBlock = cast<DirectoryBlock*>(blck);
  
   755             int n = 0;
  
   756             bool crtd = false;
  
   757             for (int i = 0; i < numDirectoryEntriesInBlock; ++i;)
  
   758             {
  
   759                 DirectoryEntry entry = dirBlock->GetDirectoryEntry(i);
  
   760                 if (entry.inodeNumber == 0 && n == 0)
  
   761                 {
  
   762                     entry.inodeNumber = parent->Key().inodeNumber;
  
   763                     entry.name = "..";
  
   764                     dirBlock->SetDirectoryEntry(i, entry);
  
   765                     ++n;
  
   766                 }
  
   767                 else if (entry.inodeNumber == 0 && n == 1)
  
   768                 {
  
   769                     entry.inodeNumber = inode->Key().inodeNumber;
  
   770                     entry.name = ".";
  
   771                     dirBlock->SetDirectoryEntry(i, entry);
  
   772                     ++n;
  
   773                     crtd = true;
  
   774                     break;
  
   775                 }
  
   776             }
  
   777             if (crtd)
  
   778             {
  
   779                 dirBlock->SetFlag(Block.Flags.dirty);
  
   780                 GetBlockManager()->WriteBlock(dirBlock, null);
  
   781                 inode->SetFileSize(inode->GetFileSize() + n * directoryEntrySize);
  
   782                 inode->SetMTime();
  
   783             }
  
   784             else
  
   785             {
  
   786                 throw SystemError(EFAIL, "no room for directory entries");
  
   787             }
  
   788         }
  
   789         public int OpenDirectory(Process* process, const string& path)
  
   790         {
  
   791             if (Log())
  
   792             {
  
   793                 LogMessage("fs.opendirectory", path);
  
   794             }
  
   795             INode* dirINode = GetINodeManager()->PathToINode(process, path);
  
   796             if (dirINode == null)
  
   797             {
  
   798                 throw SystemError(ENOENT, "path '" + path + "' does not exist");
  
   799             }
  
   800             INodePutter inodePutter(dirINode);
  
   801             if (dirINode->Type() != FileType.directory)
  
   802             {
  
   803                 throw SystemError(EINVAL, "cannot open directory '" + path + "': file type is " + FileTypeStr(dirINode->Type()));
  
   804             }
  
   805             FileSystem* fs = GetMountTable().GetFileSystem(dirINode->Key().fsNumber);
  
   806             return fs->OpenDirectory(process, path, dirINode, inodePutter);
  
   807         }
  
   808         public abstract int OpenDirectory(Process* process, const string& path, INode* dirINode, INodePutter& inodePutter);
  
   809         public abstract int ReadDirectory(Process* process, DirectoryFile* dirFile, ulong inodeNumberAddress, ulong entryNameAddress);
  
   810         public string GetCurrentWorkingDirectory(Process* process)
  
   811         {
  
   812             if (Log())
  
   813             {
  
   814                 LogMessage("fs.getcurrentworkingdirectory", "begin");
  
   815             }
  
   816             INode* rootINode = GetINodeManager()->GetINode(process->rootDirINodeKey);
  
   817             INodePutter rootPutter(rootINode);
  
   818             INode* dirINode = rootINode;
  
   819             INodePutter wdNodePutter;
  
   820             INodePutter parentPutter;
  
   821             if (process->workingDirINodeKey != process->rootDirINodeKey)
  
   822             {
  
   823                 dirINode = GetINodeManager()->GetINode(process->workingDirINodeKey);
  
   824                 wdNodePutter.ResetINode(dirINode);
  
   825             }
  
   826             INode* prevINode = null;
  
   827             bool first = true;
  
   828             string currentWorkindDirectoryPath;
  
   829             while (prevINode != rootINode)
  
   830             {
  
   831                 INode* parentINode = null;
  
   832                 bool found = false;
  
   833                 for (long offset = 0; !found && offset < dirINode->GetFileSize();;)
  
   834                 {
  
   835                     int blockNumber = 0;
  
   836                     int blockOffset = 0;
  
   837                     GetBlockManager()->GetBlockNumber(dirINode, offset, blockNumber, blockOffset, false);
  
   838                     if (blockNumber == 0)
  
   839                     {
  
   840                         throw SystemError(EFAIL, "could not get block number for directory inode");
  
   841                     }
  
   842                     Block* block = GetBlockManager()->ReadBlock(BlockKey(blockNumber, dirINode->Key().fsNumber), null);
  
   843                     BlockPutter putter(block);
  
   844                     DirectoryBlock* directoryBlock = cast<DirectoryBlock*>(block);
  
   845                     for (int index = blockOffset / directoryEntrySize; index < numDirectoryEntriesInBlock && offset < dirINode->GetFileSize(); ++index;)
  
   846                     {
  
   847                         DirectoryEntry directoryEntry = directoryBlock->GetDirectoryEntry(index);
  
   848                         if (!first && directoryEntry.inodeNumber == prevINode->Key().inodeNumber)
  
   849                         {
  
   850                             currentWorkindDirectoryPath = Path.Combine(directoryEntry.name, currentWorkindDirectoryPath);
  
   851                             found = true;
  
   852                         }
  
   853                         if (directoryEntry.name == "..")
  
   854                         {
  
   855                             INodeKey key(directoryEntry.inodeNumber, dirINode->Key().fsNumber);
  
   856                             if (key != dirINode->Key())
  
   857                             {
  
   858                                 if (key != rootINode->Key())
  
   859                                 {
  
   860                                     parentINode = GetINodeManager()->GetINode(key);
  
   861                                     parentPutter.ResetINode(parentINode);
  
   862                                 }
  
   863                                 else
  
   864                                 {
  
   865                                     parentINode = rootINode;
  
   866                                 }
  
   867                             }
  
   868                             else
  
   869                             {
  
   870                                 parentINode = dirINode;
  
   871                             }
  
   872                         }
  
   873                         offset = offset + directoryEntrySize;
  
   874                     }
  
   875                 }
  
   876                 if (parentINode == null)
  
   877                 {
  
   878                     throw SystemError(EFAIL, "parent inode entry not found");
  
   879                 }
  
   880                 if (found)
  
   881                 {
  
   882                     prevINode = dirINode;
  
   883                     dirINode = parentINode;
  
   884                 }
  
   885                 else if (first)
  
   886                 {
  
   887                     prevINode = dirINode;
  
   888                     dirINode = parentINode;
  
   889                     first = false;
  
   890                 }
  
   891                 else
  
   892                 {
  
   893                     throw SystemError(EFAIL, "could not find matching directory inode from parent inode");
  
   894                 }
  
   895             }
  
   896             currentWorkindDirectoryPath = Path.Combine("/", currentWorkindDirectoryPath);
  
   897             if (Log())
  
   898             {
  
   899                 LogMessage("fs.getcurrentworkingdirectory", "end.cwd=" + currentWorkindDirectoryPath);
  
   900             }
  
   901             return currentWorkindDirectoryPath;
  
   902         }
  
   903         public void ChDir(Process* process, const string& path)
  
   904         {
  
   905             INode* inode = GetINodeManager()->PathToINode(process, path);
  
   906             if (inode == null)
  
   907             {
  
   908                 throw SystemError(ENOENT, "path '" + path + "' does not exist");
  
   909             }
  
   910             INodePutter putter(inode);
  
   911             process->workingDirINodeKey = inode->Key();
  
   912         }
  
   913         public void Stat(Process* process, const string& path, ulong statBufAddress)
  
   914         {
  
   915             INode* inode = GetINodeManager()->PathToINode(process, path);
  
   916             if (inode == null)
  
   917             {
  
   918                 throw SystemError(ENOENT, "path '" + path + "' does not exist");
  
   919             }
  
   920             INodePutter putter(inode);
  
   921             FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
  
   922             fs->FillINode(inode);
  
   923             Machine& machine = GetMachine();
  
   924             byte[statBufSize] statBuf;
  
   925             MemoryWriter writer(&statBuf[0], statBuf.Length());
  
   926             writer.Write(inode->Key().inodeNumber);
  
   927             writer.Write(inode->Key().fsNumber);
  
   928             writer.Write(inode->Mode());
  
   929             writer.Write(inode->UID());
  
   930             writer.Write(inode->GID());
  
   931             writer.Write(inode->GetNumLinks());
  
   932             writer.Write(inode->GetFileSize());
  
   933             writer.Write(inode->CTime());
  
   934             writer.Write(inode->MTime());
  
   935             writer.Write(inode->ATime());
  
   936             WriteProcessMemory(machine, process, statBufAddress, &statBuf[0], cast<ulong>(statBuf.Length()), Protection.write);
  
   937         }
  
   938         public void ChangeMode(Process* process, const string& path, int mode)
  
   939         {
  
   940             INode* inode = GetINodeManager()->PathToINode(process, path);
  
   941             if (inode == null)
  
   942             {
  
   943                 throw SystemError(ENOENT, "path '" + path + "' does not exist");
  
   944             }
  
   945             INodePutter putter(inode);
  
   946             FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
  
   947             fs->ChangeMode(process, path, inode, mode);
  
   948         }
  
   949         public virtual void ChangeMode(Process* process, const string& path, INode* inode, int mode)
  
   950         {
  
   951             throw SystemError(EINVAL, "error changing mode of '" + path + "': file system '" + name + "' does not support chmod");
  
   952         }
  
   953         public void ChangeOwner(Process* process, const string& path, int uid, int gid)
  
   954         {
  
   955             INode* inode = GetINodeManager()->PathToINode(process, path);
  
   956             if (inode == null)
  
   957             {
  
   958                 throw SystemError(ENOENT, "path '" + path + "' does not exist");
  
   959             }
  
   960             INodePutter putter(inode);
  
   961             FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
  
   962             fs->ChangeOwner(process, path, inode, uid, gid);
  
   963         }
  
   964         public virtual void ChangeOwner(Process* process, const string& path, INode* inode, int uid, int gid)
  
   965         {
  
   966             throw SystemError(EINVAL, "error changing owner of '" + path + "': file system '" + name + "' does not support chown");
  
   967         }
  
   968         public void UpdateFileTimes(Process* process, const string& path, const DateTime& atime, const DateTime& mtime)
  
   969         {
  
   970             INode* inode = GetINodeManager()->PathToINode(process, path);
  
   971             if (inode == null)
  
   972             {
  
   973                 throw SystemError(ENOENT, "path '" + path + "' does not exist");
  
   974             }
  
   975             INodePutter putter(inode);
  
   976             FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
  
   977             fs->UpdateFileTimes(process, path, inode, atime, mtime);
  
   978         }
  
   979         public virtual void UpdateFileTimes(Process* process, const string& path, INode* inode, const DateTime& atime, const DateTime& mtime)
  
   980         {
  
   981             throw SystemError(EINVAL, "error updating file times of '" + path + "': file system '" + name + "' does not support utime");
  
   982         }
  
   983         public virtual void FillINode(INode* inode)
  
   984         {
  
   985         }
  
   986         public abstract HostFile* GetHostFile(int fsNumber) const;
  
   987         public abstract int AllocateBlockNumber();
  
   988         public abstract void SetBlockFree(int blockNumber);
  
   989         public abstract int GetFreeINodeNumber();
  
   990         public abstract void SetFreeINodeNumber(int inodeNumber);
  
   991         public abstract int GetFirstINodeBlockNumber() const;
  
   992         public abstract INodeKey GetRootDirINodeKey() const;
  
   993         public abstract int LastBlockNumber() const;
  
   994         public abstract void SetLastBlockNumber(int blockNumber, SuperBlock* sb);
  
   995         public abstract nothrow bool HasMountDirKey(const INodeKey& mountDirKey) const;
  
   996         public abstract nothrow BlockManager* GetBlockManager();
  
   997         public abstract nothrow INodeManager* GetINodeManager();
  
   998         public inline nothrow const string& Name() const
  
   999         {
  
  1000             return name;
  
  1001         }
  
  1002         public inline nothrow int Index() const
  
  1003         {
  
  1004             return index;
  
  1005         }
  
  1006         public inline nothrow bool IsReadOnly() const
  
  1007         {
  
  1008             return readOnly;
  
  1009         }
  
  1010         private string name;
  
  1011         private int index;
  
  1012         private bool readOnly;
  
  1013     }
  
  1014 
  
  1015     public abstract class BlockManager
  
  1016     {
  
  1017         public virtual default ~BlockManager();
  
  1018         public Block* GetBlock(const BlockKey& key, SuperBlock* sb)
  
  1019         {
  
  1020             return GetBlock(key, sb, true, true);
  
  1021         }
  
  1022         public abstract Block* GetBlock(const BlockKey& key, SuperBlock* sb, bool sleep, bool setOwner);
  
  1023         public abstract nothrow void PutBlock(Block* block);
  
  1024         public abstract nothrow Block* ReadBlock(const BlockKey& key, SuperBlock* sb);
  
  1025         public abstract void WriteBlock(Block* block, SuperBlock* superBlock);
  
  1026         public abstract void FreeBlocks(INode* inode);
  
  1027         public abstract void GetBlockNumber(INode* inode, long offset, int& blockNumber, int& blockOffset, bool allocate);
  
  1028         public abstract int GetBlockNumber(INode* inode, int logicalBlockNumber) const;
  
  1029         public abstract void Flush();
  
  1030         public abstract nothrow string Name() const;
  
  1031     }
  
  1032 
  
  1033     public abstract class INodeManager
  
  1034     {
  
  1035         public virtual default ~INodeManager();
  
  1036         public abstract INode* GetINode(const INodeKey& key);
  
  1037         public abstract void PutINode(INode* inode);
  
  1038         public INode* CallPathToINode(INodeManager* imgr, Process* process, const string& path)
  
  1039         {
  
  1040             return imgr->PathToINode(process, path);
  
  1041         }
  
  1042         public abstract INode* PathToINode(Process* process, const string& path);
  
  1043         public abstract INode* PathToINode(Process* process, const string& path, PathToINodeFlags flags, INode*& parent, DirectorySlot& freeDirectorySlot, string& name);
  
  1044         public abstract nothrow string Name() const;
  
  1045     }
  
  1046 }