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* processconst string& pathint modebool 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(processpathPathToINodeFlags.createEntryparentfreeDirectorySlotname);
  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->uidprocess->gidAccess.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(inodeNumberparent->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.noneFileType.regularcast<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.blockNumberparent->Key().fsNumber)null);
 104                 }
 105                 else
 106                 {
 107                     int blockNumber = 0;
 108                     int blockOffset = 0;
 109                     GetBlockManager()->GetBlockNumber(parentfreeDirectorySlot.offsetblockNumberblockOffsettrue);
 110                     block = GetBlockManager()->GetBlock(BlockKey(blockNumberparent->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(0nameMax - 1);
 123                         directoryBlock->SetDirectoryEntry(ientry);
 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(directoryBlocknull);
 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(inodecast<OpenFlags>(OpenFlags.read | OpenFlags.write | OpenFlags.create | OpenFlags.truncate));
 146             RegularFile* regularFile = new RegularFile(namefilePtr);
 147             process->fileTable.SetFile(fdregularFile);
 148             inodePutter.ResetINode();
 149             return fd;
 150         }
 151         public int Open(Process* processconst string& pathOpenFlags flagsint mode)
 152         {
 153             if (Log())
 154             {
 155                 LogMessage("fs.open"path);
 156             }
 157             if ((flags & OpenFlags.create) != OpenFlags.none)
 158             {
 159                 return Create(processpathmode(flags & OpenFlags.truncate) != OpenFlags.none);
 160             }
 161             INode* inode = GetINodeManager()->PathToINode(processpath);
 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(processinodeflagspathinodePutter);
 178         }
 179         public virtual int Open(Process* processINode* inodeOpenFlags flagsconst string& pathINodePutter& inodePutter)
 180         {
 181             int fd = process->fileTable.GetEmptyFileSlot();
 182             FilePtr* filePtr = new FilePtr(inodeflags);
 183             RegularFile* regularFile = new RegularFile(Path.GetFileName(path)filePtr);
 184             process->fileTable.SetFile(fdregularFile);
 185             inodePutter.ResetINode();
 186             return fd;
 187         }
 188         public void Link(Process* processconst string& sourcePathconst string& targetPath)
 189         {
 190             if (Log())
 191             {
 192                 LogMessage("fs.link"sourcePath + "->" + targetPath);
 193             }
 194             INode* source = GetINodeManager()->PathToINode(processsourcePath);
 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(processtargetPathPathToINodeFlags.createEntryparentfreeDirectorySlotname);
 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->uidprocess->gidAccess.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.blockNumberparent->Key().fsNumber)null);
 246             }
 247             else
 248             {
 249                 int blockNumber = 0;
 250                 int blockOffset = 0;
 251                 GetBlockManager()->GetBlockNumber(parentfreeDirectorySlot.offsetblockNumberblockOffsettrue);
 252                 block = GetBlockManager()->GetBlock(BlockKey(blockNumberparent->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(0nameMax - 1);
 265                     directoryBlock->SetDirectoryEntry(ientry);
 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(directoryBlocknull);
 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* processconst 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(processparentPath);
 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(processpath);
 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->uidprocess->gidAccess.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(parentoffsetblockNumberblockOffsetfalse);
 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(blockNumberparent->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(indexentry);
 359                         block->SetFlag(Block.Flags.dirty);
 360                         GetBlockManager()->WriteBlock(blocknull);
 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* processconst string& sourcePathconst 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(processsourceParentPath);
 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->uidprocess->gidAccess.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(sourceParentsourceOffsetblockNumberblockOffsetfalse);
 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(blockNumbersourceParent->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(0nameMax - 1);
 459                             directoryBlock->SetDirectoryEntry(indexentry);
 460                             directoryBlock->SetFlag(Block.Flags.dirty);
 461                             GetBlockManager()->WriteBlock(directoryBlocknull);
 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(processtargetPathPathToINodeFlags.createEntrytargetParentfreeDirectorySlotname);
 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->uidprocess->gidAccess.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.blockNumbertargetParent->Key().fsNumber)null);
 515                 }
 516                 else
 517                 {
 518                     int blockNumber = 0;
 519                     int blockOffset = 0;
 520                     GetBlockManager()->GetBlockNumber(targetParentfreeDirectorySlot.offsetblockNumberblockOffsettrue);
 521                     targetBlock = GetBlockManager()->GetBlock(BlockKey(blockNumbertargetParent->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(0nameMax - 1);
 535                         targetDirectoryBlock->SetDirectoryEntry(itargetEntry);
 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(sourceDirectoryBlocknull);
 549                     targetDirectoryBlock->SetFlag(Block.Flags.dirty);
 550                     GetBlockManager()->WriteBlock(targetDirectoryBlocknull);
 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(targetParentoffsetblockNumberblockOffsetfalse);
 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(blockNumbertargetParent->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(indextargetEntry);
 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(sourceDirectoryBlocknull);
 598                     targetDirectoryBlock->SetFlag(Block.Flags.dirty);
 599                     GetBlockManager()->WriteBlock(targetDirectoryBlocknull);
 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* processconst string& pathint 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(processpathPathToINodeFlags.createEntryparentfreeDirectorySlotname);
 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->uidprocess->gidAccess.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(inodeNumberparent->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.blockNumberparent->Key().fsNumber)null);
 677                 }
 678                 else
 679                 {
 680                     int blockNumber = 0;
 681                     int blockOffset = 0;
 682                     GetBlockManager()->GetBlockNumber(parentfreeDirectorySlot.offsetblockNumberblockOffsettrue);
 683                     block = GetBlockManager()->GetBlock(BlockKey(blockNumberparent->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(0nameMax - 1);
 696                         directoryBlock->SetDirectoryEntry(ientry);
 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(directoryBlocknull);
 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.noneFileType.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(inode0blockNumberblockOffsettrue);
 751             Block* blck = GetBlockManager()->GetBlock(BlockKey(blockNumberinode->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(ientry);
 765                     ++n;
 766                 }
 767                 else if (entry.inodeNumber == 0 && n == 1)
 768                 {
 769                     entry.inodeNumber = inode->Key().inodeNumber;
 770                     entry.name = ".";
 771                     dirBlock->SetDirectoryEntry(ientry);
 772                     ++n;
 773                     crtd = true;
 774                     break;
 775                 }
 776             }
 777             if (crtd)
 778             {
 779                 dirBlock->SetFlag(Block.Flags.dirty);
 780                 GetBlockManager()->WriteBlock(dirBlocknull);
 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* processconst string& path)
 790         {
 791             if (Log())
 792             {
 793                 LogMessage("fs.opendirectory"path);
 794             }
 795             INode* dirINode = GetINodeManager()->PathToINode(processpath);
 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(processpathdirINodeinodePutter);
 807         }
 808         public abstract int OpenDirectory(Process* processconst string& pathINode* dirINodeINodePutter& inodePutter);
 809         public abstract int ReadDirectory(Process* processDirectoryFile* dirFileulong inodeNumberAddressulong 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(dirINodeoffsetblockNumberblockOffsetfalse);
 838                     if (blockNumber == 0)
 839                     {
 840                         throw SystemError(EFAIL"could not get block number for directory inode");
 841                     }
 842                     Block* block = GetBlockManager()->ReadBlock(BlockKey(blockNumberdirINode->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.namecurrentWorkindDirectoryPath);
 851                             found = true;
 852                         }
 853                         if (directoryEntry.name == "..")
 854                         {
 855                             INodeKey key(directoryEntry.inodeNumberdirINode->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* processconst string& path)
 904         {
 905             INode* inode = GetINodeManager()->PathToINode(processpath);
 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* processconst string& pathulong statBufAddress)
 914         {
 915             INode* inode = GetINodeManager()->PathToINode(processpath);
 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(machineprocessstatBufAddress&statBuf[0]cast<ulong>(statBuf.Length())Protection.write);
 937         }
 938         public void ChangeMode(Process* processconst string& pathint mode)
 939         {
 940             INode* inode = GetINodeManager()->PathToINode(processpath);
 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(processpathinodemode);
 948         }
 949         public virtual void ChangeMode(Process* processconst string& pathINode* inodeint mode)
 950         {
 951             throw SystemError(EINVAL"error changing mode of '" + path + "': file system '" + name + "' does not support chmod");
 952         }
 953         public void ChangeOwner(Process* processconst string& pathint uidint gid)
 954         {
 955             INode* inode = GetINodeManager()->PathToINode(processpath);
 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(processpathinodeuidgid);
 963         }
 964         public virtual void ChangeOwner(Process* processconst string& pathINode* inodeint uidint gid)
 965         {
 966             throw SystemError(EINVAL"error changing owner of '" + path + "': file system '" + name + "' does not support chown");
 967         }
 968         public void UpdateFileTimes(Process* processconst string& pathconst DateTime& atimeconst DateTime& mtime)
 969         {
 970             INode* inode = GetINodeManager()->PathToINode(processpath);
 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(processpathinodeatimemtime);
 978         }
 979         public virtual void UpdateFileTimes(Process* processconst string& pathINode* inodeconst DateTime& atimeconst 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 blockNumberSuperBlock* 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& keySuperBlock* sb)
1019         {
1020             return GetBlock(keysbtruetrue);
1021         }
1022         public abstract Block* GetBlock(const BlockKey& keySuperBlock* sbbool sleepbool setOwner);
1023         public abstract nothrow void PutBlock(Block* block);
1024         public abstract nothrow Block* ReadBlock(const BlockKey& keySuperBlock* sb);
1025         public abstract void WriteBlock(Block* blockSuperBlock* superBlock);
1026         public abstract void FreeBlocks(INode* inode);
1027         public abstract void GetBlockNumber(INode* inodelong offsetint& blockNumberint& blockOffsetbool allocate);
1028         public abstract int GetBlockNumber(INode* inodeint 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* imgrProcess* processconst string& path)
1039         {
1040             return imgr->PathToINode(processpath);
1041         }
1042         public abstract INode* PathToINode(Process* processconst string& path);
1043         public abstract INode* PathToINode(Process* processconst string& pathPathToINodeFlags flagsINode*& parentDirectorySlot& freeDirectorySlotstring& name);
1044         public abstract nothrow string Name() const;
1045     }
1046 }