1 using System;
   2 using System.IO;
   3 using System.Collections;
   4 using cmsx.machine;
   5 using cmsx.util;
   6 
   7 namespace cmsx.kernel
   8 {
   9     public const long openFileMax = 32;
  10 
  11     public enum Origin : byte
  12     {
  13         seekSetseekCurseekEnd
  14     }
  15 
  16     public const int IOCTL_IS_CONSOLE_ITEM = 1;
  17     public const int IOCTL_IS_HOST_TEXT_FILE_ITEM = 2;
  18     public const int IOCTL_HAS_COLORS_ITEM = 3;
  19     public const int IOCTL_GET_WINDOW_SIZE_ITEM = 4;
  20     public const int IOCTL_GET_COLORS_ITEM = 5;
  21     public const int IOCTL_SET_COLORS_ITEM = 6;
  22     public const int IOCTL_BEGIN_OBEY_COLOR_STRINGS_ITEM = 7;
  23     public const int IOCTL_END_OBEY_COLOR_STRINGS_ITEM = 8;
  24     public const int IOCTL_GET_ECHO_ITEM = 9;
  25     public const int IOCTL_SET_ECHO_ITEM = 10;
  26 
  27     public enum OpenFlags : int
  28     {
  29         none = 0u
  30         read = 1u << 0u
  31         write = 1u << 1u
  32         create = 1 << 2u
  33         append = 1u << 3u
  34         truncate = 1u << 4u
  35         text = 1u << 5u
  36     }
  37 
  38     public int Create(Machine& machinecmsx.kernel.Process* processulong pathAddressint mode)
  39     {
  40         if (pathAddress == 0u)
  41         {
  42             throw SystemError(EINVAL"path address is null");
  43         }
  44         string path;
  45         ReadStringFromProcessMemory(machineprocesspathAddresspath);
  46         if (path.IsEmpty())
  47         {
  48             throw SystemError(EINVAL"path address is empty");
  49         }
  50         FileSystem* fs = GetMountTable().GetFileSystem(0);
  51         return fs->Create(processpathmodetrue);
  52     }
  53 
  54     public int Open(Machine& machinecmsx.kernel.Process* processulong pathAddressOpenFlags flagsint mode)
  55     {
  56         if (pathAddress == 0u)
  57         {
  58             throw SystemError(EINVAL"path address is null");
  59         }
  60         string path;
  61         ReadStringFromProcessMemory(machineprocesspathAddresspath);
  62         if (path.IsEmpty())
  63         {
  64             throw SystemError(EINVAL"path is empty");
  65         }
  66         FileSystem* fs = GetMountTable().GetFileSystem(0);
  67         return fs->Open(processpathflagsmode);
  68     }
  69 
  70     public void Close(Machine& machinecmsx.kernel.Process* processint fd)
  71     {
  72         File* file = process->fileTable.GetFile(fd);
  73         if (file != null)
  74         {
  75             file->Release(fd);
  76             process->fileTable.SetFile(fdnull);
  77         }
  78         else
  79         {
  80             throw SystemError(EBADF"file '" + ToString(fd) + "' not open");
  81         }
  82     }
  83 
  84     public long Read(Machine& machinecmsx.kernel.Process* processint fdulong bufferAddresslong count)
  85     {
  86         File* file = process->fileTable.GetFile(fd);
  87         if (count > 0)
  88         {
  89             return file->Read(machineprocessbufferAddresscount);
  90         }
  91         return 0;
  92     }
  93 
  94     public long Write(Machine& machinecmsx.kernel.Process* processint fdulong bufferAddresslong count)
  95     {
  96         File* file = process->fileTable.GetFile(fd);
  97         if (count > 0)
  98         {
  99             return file->Write(machineprocessbufferAddresscount);
 100         }
 101         return 0;
 102     }
 103 
 104     public long Seek(Machine& machinecmsx.kernel.Process* processint fdlong posOrigin origin)
 105     {
 106         File* file = process->fileTable.GetFile(fd);
 107         return file->Seek(posorigin);
 108     }
 109 
 110     public long Tell(Machine& machinecmsx.kernel.Process* processint fd)
 111     {
 112         File* file = process->fileTable.GetFile(fd);
 113         return file->Tell();
 114     }
 115 
 116     public int Dup(cmsx.kernel.Process* processint fd)
 117     {
 118         File* file = process->fileTable.GetFile(fd);
 119         File* shared = file->Share();
 120         int duplicate = process->fileTable.GetEmptyFileSlot();
 121         process->fileTable.SetFile(duplicateshared);
 122         return duplicate;
 123     }
 124 
 125     public void Pipe(Machine& machinecmsx.kernel.Process* processulong readFDAddressulong writeFDAddress)
 126     {
 127         if (readFDAddress == 0u)
 128         {
 129             throw SystemError(EINVAL"read file descriptor address is null");
 130         }
 131         if (writeFDAddress == 0u)
 132         {
 133             throw SystemError(EINVAL"write file descriptor address is null");
 134         }
 135         FileSystem* fs = GetMountTable().GetFileSystem(0);
 136         int inodeNumber = fs->GetFreeINodeNumber();
 137         INodeKey key(inodeNumber0);
 138         INode* inode = fs->GetINodeManager()->GetINode(key);
 139         INodePutter inodePutter(inode);
 140         inode->SetType(FileType.fifo);
 141         inode->SetFlag(INode.Flags.pipeEmpty);
 142         Fifo* fifo = new Fifo(inode);
 143         int readFD = process->fileTable.GetEmptyFileSlot();
 144         PipeInputFile* pipeInput = new PipeInputFile(fifo);
 145         process->fileTable.SetFile(readFDpipeInput);
 146         int writeFD = process->fileTable.GetEmptyFileSlot();
 147         PipeOutputFile* pipeOutput = new PipeOutputFile(fifo);
 148         process->fileTable.SetFile(writeFDpipeOutput);
 149         WriteProcessMemory(machineprocessreadFDAddresscast<ulong>(readFD)4uProtection.write);
 150         WriteProcessMemory(machineprocesswriteFDAddresscast<ulong>(writeFD)4uProtection.write);
 151         inodePutter.ResetINode();
 152     }
 153 
 154     public void Link(Machine& machinecmsx.kernel.Process* processulong sourcePathAddressulong targetPathAddress)
 155     {
 156         if (sourcePathAddress == 0u)
 157         {
 158             throw SystemError(EINVAL"source path address is null");
 159         }
 160         if (targetPathAddress == 0u)
 161         {
 162             throw SystemError(EINVAL"target path address is null");
 163         }
 164         string sourcePath;
 165         ReadStringFromProcessMemory(machineprocesssourcePathAddresssourcePath);
 166         if (sourcePath.IsEmpty())
 167         {
 168             throw SystemError(EINVAL"source path is empty");
 169         }
 170         string targetPath;
 171         ReadStringFromProcessMemory(machineprocesstargetPathAddresstargetPath);
 172         if (targetPath.IsEmpty())
 173         {
 174             throw SystemError(EINVAL"target path is empty");
 175         }
 176         FileSystem* fs = GetMountTable().GetFileSystem(0);
 177         fs->Link(processsourcePathtargetPath);
 178     }
 179 
 180     public void Unlink(Machine& machinecmsx.kernel.Process* processulong pathAddress)
 181     {
 182         if (pathAddress == 0u)
 183         {
 184             throw SystemError(EINVAL"path address is null");
 185         }
 186         string path;
 187         ReadStringFromProcessMemory(machineprocesspathAddresspath);
 188         if (path.IsEmpty())
 189         {
 190             throw SystemError(EINVAL"path is empty");
 191         }
 192         FileSystem* fs = GetMountTable().GetFileSystem(0);
 193         fs->Unlink(processpath);
 194     }
 195 
 196     public void Rename(Machine& machinecmsx.kernel.Process* processulong sourcePathAddressulong targetPathAddress)
 197     {
 198         if (sourcePathAddress == 0u)
 199         {
 200             throw SystemError(EINVAL"source path address is null");
 201         }
 202         if (targetPathAddress == 0u)
 203         {
 204             throw SystemError(EINVAL"target path address is null");
 205         }
 206         string sourcePath;
 207         ReadStringFromProcessMemory(machineprocesssourcePathAddresssourcePath);
 208         if (sourcePath.IsEmpty())
 209         {
 210             throw SystemError(EINVAL"source path is empty");
 211         }
 212         string targetPath;
 213         ReadStringFromProcessMemory(machineprocesstargetPathAddresstargetPath);
 214         if (targetPath.IsEmpty())
 215         {
 216             throw SystemError(EINVAL"target path is empty");
 217         }
 218         FileSystem* fs = GetMountTable().GetFileSystem(0);
 219         fs->Rename(processsourcePathtargetPath);
 220     }
 221 
 222     public void Mkdir(Machine& machinecmsx.kernel.Process* processulong pathAddressint mode)
 223     {
 224         if (pathAddress == 0u)
 225         {
 226             throw SystemError(EINVAL"path address is null");
 227         }
 228         string path;
 229         ReadStringFromProcessMemory(machineprocesspathAddresspath);
 230         if (path.IsEmpty())
 231         {
 232             throw SystemError(EINVAL"path is empty");
 233         }
 234         FileSystem* fs = GetMountTable().GetFileSystem(0);
 235         fs->MakeDirectory(processpathmode);
 236     }
 237 
 238     public int OpenDir(Machine& machinecmsx.kernel.Process* processulong pathAddress)
 239     {
 240         if (pathAddress == 0u)
 241         {
 242             throw SystemError(EINVAL"path address is null");
 243         }
 244         string path;
 245         ReadStringFromProcessMemory(machineprocesspathAddresspath);
 246         if (path.IsEmpty())
 247         {
 248             throw SystemError(EINVAL"path is empty");
 249         }
 250         FileSystem* fs = GetMountTable().GetFileSystem(0);
 251         return fs->OpenDirectory(processpath);
 252     }
 253 
 254     public int ReadDir(Machine& machinecmsx.kernel.Process* processint fdulong inodeNumberAddressulong entryNameAddress)
 255     {
 256         if (inodeNumberAddress == 0u)
 257         {
 258             throw SystemError(EINVAL"inode number address is null");
 259         }
 260         if (entryNameAddress == 0u)
 261         {
 262             throw SystemError(EINVAL"entry name address is null");
 263         }
 264         File* file = process->fileTable.GetFile(fd);
 265         if (file != null)
 266         {
 267             if (file is DirectoryFile*)
 268             {
 269                 DirectoryFile* dir = cast<DirectoryFile*>(file);
 270                 return dir->Read(processinodeNumberAddressentryNameAddress);
 271             }
 272             else
 273             {
 274                 throw SystemError(EBADF"file descriptor '" + ToString(fd) + "' does not denote a directory");
 275             }
 276         }
 277         else
 278         {
 279             throw SystemError(EBADF"directory '" + ToString(fd) + "' not open");
 280         }
 281     }
 282 
 283     public void CloseDir(Machine& machinecmsx.kernel.Process* processint fd)
 284     {
 285         File* file = process->fileTable.GetFile(fd);
 286         if (file != null)
 287         {
 288             if (file is DirectoryFile*)
 289             {
 290                 file->Release(fd);
 291                 process->fileTable.SetFile(fdnull);
 292             }
 293             else
 294             {
 295                 throw SystemError(EBADF"file descriptor '" + ToString(fd) + "' does not denote a directory");
 296             }
 297         }
 298         else
 299         {
 300             throw SystemError(EBADF"directory '" + ToString(fd) + "' not open");
 301         }
 302     }
 303 
 304     public void GetCWD(Machine& machinecmsx.kernel.Process* processulong pathBufferAddresslong count)
 305     {
 306         if (pathBufferAddress == 0u)
 307         {
 308             throw SystemError(EINVAL"path buffer address is null");
 309         }
 310         if (count < 0)
 311         {
 312             throw SystemError(EINVAL"invalid count");
 313         }
 314         FileSystem* fs = GetMountTable().GetFileSystem(process->workingDirINodeKey.fsNumber);
 315         string currentWorkindDirectory = fs->GetCurrentWorkingDirectory(process);
 316         if (currentWorkindDirectory.Length() >= count)
 317         {
 318             throw SystemError(ERANGE"path buffer not big enough");
 319         }
 320         WriteProcessMemory(machineprocesspathBufferAddresscast<byte*>(cast<void*>(currentWorkindDirectory.Chars()))cast<ulong>(currentWorkindDirectory.Length() + 1)Protection.write);
 321     }
 322 
 323     public void ChDir(Machine& machinecmsx.kernel.Process* processulong pathAddress)
 324     {
 325         if (pathAddress == 0u)
 326         {
 327             throw SystemError(EINVAL"path address is null");
 328         }
 329         string path;
 330         ReadStringFromProcessMemory(machineprocesspathAddresspath);
 331         if (path.IsEmpty())
 332         {
 333             throw SystemError(EINVAL"path is empty");
 334         }
 335         FileSystem* fs = GetMountTable().GetFileSystem(0);
 336         fs->ChDir(processpath);
 337     }
 338 
 339     public void Stat(Machine& machinecmsx.kernel.Process* processulong pathAddressulong statBufAddress)
 340     {
 341         if (pathAddress == 0u)
 342         {
 343             throw SystemError(EINVAL"path address is null");
 344         }
 345         string path;
 346         ReadStringFromProcessMemory(machineprocesspathAddresspath);
 347         if (path.IsEmpty())
 348         {
 349             throw SystemError(EINVAL"path is empty");
 350         }
 351         FileSystem* fs = GetMountTable().GetFileSystem(0);
 352         fs->Stat(processpathstatBufAddress);
 353     }
 354 
 355     public int UMask(Machine& machinecmsx.kernel.Process* processint mask)
 356     {
 357         int prevUMask = process->umask;
 358         process->umask = mask & cast<int>(ParseOctal("777"));
 359         return prevUMask;
 360     }
 361 
 362     public void IOCtl(Machine& machinecmsx.kernel.Process* processint fdint itemulong bufferAddresslong count)
 363     {
 364         if (bufferAddress == 0u)
 365         {
 366             throw SystemError(EINVAL"buffer address is null");
 367         }
 368         if (count < 0)
 369         {
 370             throw SystemError(EINVAL"invalid count");
 371         }
 372         File* file = process->fileTable.GetFile(fd);
 373         file->IOCtl(machineprocessitembufferAddresscount);
 374     }
 375 
 376     public int GetFGPID(cmsx.kernel.Process* processint fd)
 377     {
 378         File* file = process->fileTable.GetFile(fd);
 379         return file->GetFGPID();
 380     }
 381 
 382     public void SetFGPID(cmsx.kernel.Process* processint fdint pid)
 383     {
 384         File* file = process->fileTable.GetFile(fd);
 385         file->SetFGPID(pid);
 386     }
 387 
 388     public void Chmod(Machine& machinecmsx.kernel.Process* processulong pathAddressint mode)
 389     {
 390         if (pathAddress == 0u)
 391         {
 392             throw SystemError(EINVAL"path address is null");
 393         }
 394         string path;
 395         ReadStringFromProcessMemory(machineprocesspathAddresspath);
 396         if (path.IsEmpty())
 397         {
 398             throw SystemError(EINVAL"path is empty");
 399         }
 400         FileSystem* fs = GetMountTable().GetFileSystem(0);
 401         fs->ChangeMode(processpathmode);
 402     }
 403 
 404     public void Chown(Machine& machinecmsx.kernel.Process* processulong pathAddress int uidint gid)
 405     {
 406         if (pathAddress == 0u)
 407         {
 408             throw SystemError(EINVAL"path address is null");
 409         }
 410         string path;
 411         ReadStringFromProcessMemory(machineprocesspathAddresspath);
 412         if (path.IsEmpty())
 413         {
 414             throw SystemError(EINVAL"path is empty");
 415         }
 416         FileSystem* fs = GetMountTable().GetFileSystem(0);
 417         fs->ChangeOwner(processpathuidgid);
 418     }
 419 
 420     public void UTime(Machine& machinecmsx.kernel.Process* processulong pathAddressulong timeBufAddress)
 421     {
 422         if (pathAddress == 0u)
 423         {
 424             throw SystemError(EINVAL"path address is null");
 425         }
 426         if (timeBufAddress == 0u)
 427         {
 428             throw SystemError(EINVAL"time buffer address is null");
 429         }
 430         string path;
 431         ReadStringFromProcessMemory(machineprocesspathAddresspath);
 432         if (path.IsEmpty())
 433         {
 434             throw SystemError(EINVAL"path is empty");
 435         }
 436         byte[16] buffer;
 437         ReadProcessMemory(machineprocesstimeBufAddress&buffer[0]cast<ulong>(buffer.Length()));
 438         MemoryReader reader(&buffer[0]buffer.Length());
 439         DateTime atime = reader.ReadDateTime();
 440         DateTime mtime = reader.ReadDateTime();
 441         FileSystem* fs = GetMountTable().GetFileSystem(0);
 442         fs->UpdateFileTimes(processpathatimemtime);
 443     }
 444 
 445     public abstract class File
 446     {
 447         public nothrow File(const string& name_) : name(name_)
 448         {
 449         }
 450         public virtual long Write(Machine& machinecmsx.kernel.Process* writerulong bufferAddresslong count)
 451         {
 452             throw SystemError(EBADFname + " not open for writing");
 453         }
 454         public virtual long Read(Machine& machinecmsx.kernel.Process* readerulong bufferAddresslong count)
 455         {
 456             throw SystemError(EBADFname + " not open for reading");
 457         }
 458         public virtual long Seek(long posOrigin origin)
 459         {
 460             throw SystemError(ENOTSUPPORTEDname + " does not support seek");
 461         }
 462         public virtual long Tell()
 463         {
 464             throw SystemError(ENOTSUPPORTEDname + " does not support tell");
 465         }
 466         public virtual nothrow bool IsConsole() const
 467         {
 468             return false;
 469         }
 470         public virtual nothrow bool IsHostTextFile() const
 471         {
 472             return false;
 473         }
 474         public virtual nothrow bool HasColors() const
 475         {
 476             return false;
 477         }
 478         public virtual bool Echo() const
 479         {
 480             throw SystemError(ENOTSUPPORTEDname + " does not support ioctl(echo)");
 481         }
 482         public virtual void SetEcho(bool echo)
 483         {
 484             throw SystemError(ENOTSUPPORTEDname + " does not support ioctl(setecho)");
 485         }
 486         public virtual void IOCtl(Machine& machinecmsx.kernel.Process* processint itemulong bufferAddresslong count)
 487         {
 488             bool itemFound = true;
 489             switch (item)
 490             {
 491                 case IOCTL_IS_CONSOLE_ITEM:
 492                 {
 493                     GetConsoleStatus(machineprocessbufferAddresscount);
 494                     break;
 495                 }
 496                 case IOCTL_IS_HOST_TEXT_FILE_ITEM:
 497                 {
 498                     GetHostTextFileStatus(machineprocessbufferAddresscount);
 499                     break;
 500                 }
 501                 case IOCTL_HAS_COLORS_ITEM:
 502                 {
 503                     GetColorStatus(machineprocessbufferAddresscount);
 504                     break;
 505                 }
 506                 case IOCTL_GET_WINDOW_SIZE_ITEM:
 507                 {
 508                     GetWindowDimensions(machineprocessbufferAddresscount);
 509                     break;
 510                 }
 511                 case IOCTL_GET_COLORS_ITEM:
 512                 {
 513                     GetConsoleColors(machineprocessbufferAddresscount);
 514                     break;
 515                 }
 516                 case IOCTL_SET_COLORS_ITEM:
 517                 {
 518                     SetConsoleColors(machineprocessbufferAddresscount);
 519                     break;
 520                 }
 521                 case IOCTL_BEGIN_OBEY_COLOR_STRINGS_ITEM:
 522                 {
 523                     BeginObeyColorStrings();
 524                     break;
 525                 }
 526                 case IOCTL_END_OBEY_COLOR_STRINGS_ITEM:
 527                 {
 528                     EndObeyColorStrings();
 529                     break;
 530                 }
 531                 case IOCTL_GET_ECHO_ITEM:
 532                 {
 533                     GetEcho(machineprocessbufferAddresscount);
 534                     break;
 535                 }
 536                 case IOCTL_SET_ECHO_ITEM:
 537                 {
 538                     SetEcho(machineprocessbufferAddresscount);
 539                     break;
 540                 }
 541                 default:
 542                 {
 543                     itemFound = false;
 544                     break;
 545                 }
 546             }
 547             if (!itemFound)
 548             {
 549                 throw SystemError(EINVAL"unknown ioctl item " + ToString(item) + " for " + name);
 550             }
 551         }
 552         public virtual int GetFGPID()
 553         {
 554             throw SystemError(ENOTSUPPORTEDname + " does not support getfgpid()");
 555         }
 556         public virtual void SetFGPID(int pid)
 557         {
 558             throw SystemError(ENOTSUPPORTEDname + " does not support setfgpid()");
 559         }
 560         public virtual File* Share()
 561         {
 562             return this;
 563         }
 564         public virtual void Release(int fd)
 565         {
 566             if (Log())
 567             {
 568                 LogMessage("fs.file""release.fd=" + ToString(fd));
 569             }
 570         }
 571         private void GetConsoleStatus(Machine& machinecmsx.kernel.Process* processulong bufferAddresslong count)
 572         {
 573             if (count < 1)
 574             {
 575                 throw SystemError(ERANGE"buffer size for IOCTL_IS_CONSOLE_ITEM must be at least 1 byte");
 576             }
 577             byte[1] buffer;
 578             MemoryWriter writer(&buffer[0]buffer.Length());
 579             writer.Write(cast<byte>(IsConsole()));
 580             WriteProcessMemory(machineprocessbufferAddress&buffer[0]cast<ulong>(buffer.Length())Protection.write);
 581         }
 582         private void GetHostTextFileStatus(Machine& machinecmsx.kernel.Process* processulong bufferAddresslong count)
 583         {
 584             if (count < 1)
 585             {
 586                 throw SystemError(ERANGE"buffer size for IOCTL_IS_HOST_TEXT_FILE_ITEM must be at least 1 byte");
 587             }
 588             byte[1] buffer;
 589             MemoryWriter writer(&buffer[0]buffer.Length());
 590             writer.Write(cast<byte>(IsHostTextFile()));
 591             WriteProcessMemory(machineprocessbufferAddress&buffer[0]cast<ulong>(buffer.Length())Protection.write);
 592         }
 593         private void GetColorStatus(Machine& machinecmsx.kernel.Process* processulong bufferAddresslong count)
 594         {
 595             if (count < 1)
 596             {
 597                 throw SystemError(ERANGE"buffer size for IOCTL_HAS_COLORS_ITEM must be at least 1 byte");
 598             }
 599             byte[1] buffer;
 600             MemoryWriter writer(&buffer[0]buffer.Length());
 601             writer.Write(cast<byte>(HasColors()));
 602             WriteProcessMemory(machineprocessbufferAddress&buffer[0]cast<ulong>(buffer.Length())Protection.write);
 603         }
 604         private void GetEcho(Machine& machinecmsx.kernel.Process* processulong bufferAddresslong count)
 605         {
 606             if (count < 1)
 607             {
 608                 throw SystemError(ERANGE"buffer size for IOCTL_GET_ECHO_ITEM must be at least 1 byte");
 609             }
 610             byte[1] buffer;
 611             MemoryWriter writer(&buffer[0]buffer.Length());
 612             bool echo = Echo();
 613             writer.Write(cast<byte>(echo));
 614             WriteProcessMemory(machineprocessbufferAddress&buffer[0]cast<ulong>(buffer.Length())Protection.write);
 615         }
 616         private void SetEcho(Machine& machinecmsx.kernel.Process* processulong bufferAddresslong count)
 617         {
 618             if (count < 1)
 619             {
 620                 throw SystemError(ERANGE"buffer size for IOCTL_SET_ECHO_ITEM must be at least 1 byte");
 621             }
 622             byte[1] buffer;
 623             ReadProcessMemory(machineprocessbufferAddress&buffer[0]cast<ulong>(buffer.Length()));
 624             MemoryReader reader(&buffer[0]buffer.Length());
 625             bool echo = cast<bool>(reader.ReadByte());
 626             SetEcho(echo);
 627         }
 628         private void GetWindowDimensions(Machine& machinecmsx.kernel.Process* processulong bufferAddresslong count)
 629         {
 630             if (count < 8)
 631             {
 632                 throw SystemError(ERANGE"buffer size for IOCTL_GET_WINDOW_SIZE_ITEM must be at least 8 bytes");
 633             }
 634             int rows = 0;
 635             int cols = 0;
 636             if (IsConsole())
 637             {
 638                 GetConsoleDriver().GetDimensions(rowscols);
 639             }
 640             byte[8] buffer;
 641             MemoryWriter writer(&buffer[0]buffer.Length());
 642             writer.Write(rows);
 643             writer.Write(cols);
 644             WriteProcessMemory(machineprocessbufferAddress&buffer[0]cast<ulong>(buffer.Length())Protection.write);
 645         }
 646         private void GetConsoleColors(Machine& machinecmsx.kernel.Process* processulong bufferAddresslong count)
 647         {
 648             if (count < 2)
 649             {
 650                 throw SystemError(ERANGE"buffer size for IOCTL_GET_COLORS_ITEM must be at least 2 bytes");
 651             }
 652             byte[2] buffer;
 653             MemoryWriter writer(&buffer[0]buffer.Length());
 654             if (IsConsole())
 655             {
 656                 ConsoleColor foregroundColor;
 657                 ConsoleColor backgroundColor;
 658                 GetConsoleDriver().GetConsoleColors(foregroundColorbackgroundColor);
 659                 writer.Write(cast<byte>(foregroundColor));
 660                 writer.Write(cast<byte>(backgroundColor));
 661             }
 662             else
 663             {
 664                 writer.Write(cast<byte>(0u));
 665                 writer.Write(cast<byte>(0u));
 666             }
 667             WriteProcessMemory(machineprocessbufferAddress&buffer[0]cast<ulong>(buffer.Length())Protection.write);
 668         }
 669         private void SetConsoleColors(Machine& machinecmsx.kernel.Process* processulong bufferAddresslong count)
 670         {
 671             if (count < 2)
 672             {
 673                 throw SystemError(ERANGE"buffer size for IOCTL_SET_COLORS_ITEM must be at least 2 bytes");
 674             }
 675             if (IsConsole())
 676             {
 677                 byte[2] buffer;
 678                 ReadProcessMemory(machineprocessbufferAddress&buffer[0]cast<ulong>(buffer.Length()));
 679                 MemoryReader reader(&buffer[0]buffer.Length());
 680                 ConsoleColor foregroundColor = cast<ConsoleColor>(reader.ReadByte());
 681                 ConsoleColor backgroundColor = cast<ConsoleColor>(reader.ReadByte());
 682                 GetConsoleDriver().SetConsoleColors(foregroundColorbackgroundColor);
 683             }
 684         }
 685         private void BeginObeyColorStrings()
 686         {
 687             if (IsConsole())
 688             {
 689                 GetConsoleDriver().BeginObeyColorStrings();
 690             }
 691         }
 692         private void EndObeyColorStrings()
 693         {
 694             if (IsConsole())
 695             {
 696                 GetConsoleDriver().EndObeyColorStrings();
 697             }
 698         }
 699         public string name;
 700     }
 701 
 702     public class DebuggerInputFile : File
 703     {
 704         public nothrow DebuggerInputFile() : base("CONSOLE INPUT (DEBUGGER)")
 705         {
 706         }
 707         public override long Read(Machine& machinecmsx.kernel.Process* readerulong bufferAddresslong count)
 708         {
 709             try
 710             {
 711                 if (bufferedInput.IsEmpty())
 712                 {
 713                     Kernel& kernel = Kernel.Instance();
 714                     Debugger* debugger = kernel.GetDebugger();
 715                     bufferedInput = debugger->ReadInputLine();
 716                     if (bufferedInput.IsEmpty())
 717                     {
 718                         if (debugger->Eof())
 719                         {
 720                             debugger->ResetEof();
 721                             return 0;
 722                         }
 723                     }
 724                 }
 725                 long n = Min(countbufferedInput.Length());
 726                 WriteProcessMemory(machinereaderbufferAddresscast<byte*>(cast<void*>(bufferedInput.Chars()))cast<ulong>(n)Protection.write);
 727                 bufferedInput = bufferedInput.Substring(n);
 728                 return n;
 729             }
 730             catch (const Exception& ex)
 731             {
 732                 throw SystemError(EFAILex.Message());
 733             }
 734             return -1;
 735         }
 736         public override nothrow bool IsConsole() const
 737         {
 738             return true;
 739         }
 740         public override bool Echo() const
 741         {
 742             return true;
 743         }
 744         public override void SetEcho(bool echo)
 745         {
 746         }
 747         public override int GetFGPID()
 748         {
 749             Kernel& kernel = GetKernel();
 750             Session* currentSession = kernel.CurrentSession();
 751             return currentSession->fgpid;
 752         }
 753         public override void SetFGPID(int pid)
 754         {
 755             Kernel& kernel = GetKernel();
 756             Session* currentSession = kernel.CurrentSession();
 757             currentSession->fgpid = pid;
 758         }
 759         private string bufferedInput;
 760     }
 761 
 762     public class DebuggerOutputFile : File
 763     {
 764         public nothrow DebuggerOutputFile() : base("CONSOLE OUTPUT (DEBUGGER)")
 765         {
 766         }
 767         public override long Write(Machine& machinecmsx.kernel.Process* writerulong bufferAddresslong count)
 768         {
 769             try
 770             {
 771                 Kernel& kernel = Kernel.Instance();
 772                 Debugger* debugger = kernel.GetDebugger();
 773                 UniquePtr<byte> buffer(cast<byte*>(RtMemAlloc(count)));
 774                 ReadProcessMemory(machinewriterbufferAddressbuffer.Get()cast<ulong>(count));
 775                 debugger->WriteOutput(buffer.Get()count);
 776                 return count;
 777             }
 778             catch (const Exception& ex)
 779             {
 780                 throw SystemError(EFAILex.Message());
 781             }
 782             return -1;
 783         }
 784         public override nothrow bool IsConsole() const
 785         {
 786             return true;
 787         }
 788     }
 789 
 790     public class ConsoleInputFile : File
 791     {
 792         public nothrow ConsoleInputFile() : base("CONSOLE INPUT")
 793         {
 794         }
 795         public override long Read(Machine& machinecmsx.kernel.Process* readerulong bufferAddresslong count)
 796         {
 797             try
 798             {
 799                 while (true)
 800                 {
 801                     if (Log())
 802                     {
 803                         LogMessage("fs.file""console.input.begin.read.count=" + ToString(count));
 804                     }
 805                     Kernel& kernel = Kernel.Instance();
 806                     Session* session = kernel.CurrentSession();
 807                     if (bufferedInput.IsEmpty())
 808                     {
 809                         ConsoleDriver* consoleDriver = kernel.GetConsoleDriver();
 810                         if (consoleDriver->HasLine())
 811                         {
 812                             bufferedInput = consoleDriver->GetLine();
 813                         }
 814                         else if (consoleDriver->Eof())
 815                         {
 816                             consoleDriver->ResetEof();
 817                             return 0;
 818                         }
 819                         else
 820                         {
 821                             EnqueueReadRequest(readerbufferAddresscount);
 822                         }
 823                     }
 824                     long n = Min(countbufferedInput.Length());
 825                     WriteProcessMemory(machinereaderbufferAddresscast<byte*>(cast<void*>(bufferedInput.Chars()))cast<ulong>(n)Protection.write);
 826                     bufferedInput = bufferedInput.Substring(n);
 827                     if (Log())
 828                     {
 829                         LogMessage("fs.file""console.input.end.read.count=" + ToString(n));
 830                     }
 831                     return n;
 832                 }
 833             }
 834             catch (const Exception& ex)
 835             {
 836                 throw SystemError(EFAILex.Message());
 837             }
 838             return -1;
 839         }
 840         public override nothrow bool IsConsole() const
 841         {
 842             return true;
 843         }
 844         public inline nothrow bool HasBufferedInput() const
 845         {
 846             return !bufferedInput.IsEmpty();
 847         }
 848         public nothrow void SetBufferedInput(const string& line)
 849         {
 850             bufferedInput = line;
 851         }
 852         private void EnqueueReadRequest(cmsx.kernel.Process* readerulong bufferAddresslong count)
 853         {
 854             SleepProcess(readerconsoleInputEventbufferAddresscast<ulong>(count));
 855         }
 856         public override bool Echo() const
 857         {
 858             return GetConsoleDriver().Echo();
 859         }
 860         public override void SetEcho(bool echo)
 861         {
 862             GetConsoleDriver().SetEcho(echo);
 863         }
 864         public override int GetFGPID()
 865         {
 866             Kernel& kernel = GetKernel();
 867             Session* currentSession = kernel.CurrentSession();
 868             return currentSession->fgpid;
 869         }
 870         public override void SetFGPID(int pid)
 871         {
 872             if (Log())
 873             {
 874                 LogMessage("fs.file""console.input.setfgpid=" + ToString(pid));
 875             }
 876             Kernel& kernel = GetKernel();
 877             Session* currentSession = kernel.CurrentSession();
 878             currentSession->fgpid = pid;
 879         }
 880         private string bufferedInput;
 881     }
 882 
 883     public class ConsoleOutputFile : File
 884     {
 885         public nothrow ConsoleOutputFile() : base("CONSOLE OUTPUT")
 886         {
 887         }
 888         public override long Write(Machine& machinecmsx.kernel.Process* writerulong bufferAddresslong count)
 889         {
 890             try
 891             {
 892                 UniquePtr<byte> buffer(cast<byte*>(RtMemAlloc(count)));
 893                 ReadProcessMemory(machinewriterbufferAddressbuffer.Get()cast<ulong>(count));
 894                 Kernel& kernel = Kernel.Instance();
 895                 ConsoleDriver* consoleDriver = kernel.GetConsoleDriver();
 896                 consoleDriver->WriteToConsole(buffer.Get()count);
 897                 return count;
 898             }
 899             catch (const Exception& ex)
 900             {
 901                 throw SystemError(EFAILex.Message());
 902             }
 903             return -1;
 904         }
 905         public override nothrow bool IsConsole() const
 906         {
 907             return true;
 908         }
 909         public override nothrow bool HasColors() const
 910         {
 911             return true;
 912         }
 913     }
 914 
 915     public class RegularFile : File
 916     {
 917         public nothrow RegularFile(const string& name_FilePtr* filePtr_) : base(name_)filePtr(filePtr_)
 918         {
 919         }
 920         public override long Write(Machine& machinecmsx.kernel.Process* writerulong bufferAddresslong count)
 921         {
 922             if ((filePtr->Flags() & OpenFlags.write) == OpenFlags.none)
 923             {
 924                 throw SystemError(EBADFname + " not open for writing");
 925             }
 926             return filePtr->Write(machinewriterbufferAddresscount);
 927         }
 928         public override long Read(Machine& machinecmsx.kernel.Process* readerulong bufferAddresslong count)
 929         {
 930             if ((filePtr->Flags() & OpenFlags.read) == OpenFlags.none)
 931             {
 932                 throw SystemError(EBADFname + " not open for reading");
 933             }
 934             return filePtr->Read(machinereaderbufferAddresscount);
 935         }
 936         public override long Seek(long posOrigin origin)
 937         {
 938             return filePtr->Seek(posorigin);
 939         }
 940         public override long Tell()
 941         {
 942             return filePtr->Tell();
 943         }
 944         public override nothrow bool IsHostTextFile() const
 945         {
 946             return filePtr->IsHostTextFile();
 947         }
 948         public override File* Share()
 949         {
 950             return new RegularFile(namefilePtr->Share());
 951         }
 952         public override void Release(int fd)
 953         {
 954             if (Log())
 955             {
 956                 LogMessage("fs.file""regular.release.begin.fd=" + ToString(fd));
 957             }
 958             filePtr->Release();
 959             delete this;
 960             if (Log())
 961             {
 962                 LogMessage("fs.file""regular.release.end");
 963             }
 964         }
 965         private FilePtr* filePtr;
 966     }
 967 
 968     public class DirectoryFile : File
 969     {
 970         public nothrow DirectoryFile(INode* dirINode_) : base("dir")dirINode(dirINode_)offset(0)
 971         {
 972         }
 973         public virtual int Read(cmsx.kernel.Process* processulong inodeNumberAddressulong entryNameAddress)
 974         {
 975             if (dirINode == null)
 976             {
 977                 throw SystemError(EINVAL"directory inode is null");
 978             }
 979             FileSystem* fs = GetMountTable().GetFileSystem(dirINode->Key().fsNumber);
 980             return fs->ReadDirectory(processthisinodeNumberAddressentryNameAddress);
 981         }
 982         public inline nothrow INode* DirINode() const
 983         {
 984             return dirINode;
 985         }
 986         public inline nothrow long Offset() const
 987         {
 988             return offset;
 989         }
 990         public inline nothrow void SetOffset(long offset_)
 991         {
 992             offset = offset_;
 993         }
 994         public override File* Share()
 995         {
 996             if (dirINode != null)
 997             {
 998                 dirINode->SetUseCount(dirINode->GetUseCount() + 1);
 999             }
1000             return new DirectoryFile(dirINode);
1001         }
1002         public override void Release(int fd)
1003         {
1004             if (dirINode != null)
1005             {
1006                 dirINode->Manager()->PutINode(dirINode);
1007             }
1008             delete this;
1009         }
1010         private INode* dirINode;
1011         private long offset;
1012     }
1013 
1014     public class HostDirectoryFile : DirectoryFile
1015     {
1016         public nothrow HostDirectoryFile(INode* dirINode_) : base(dirINode_)findHandle(null)
1017         {
1018         }
1019         public override int Read(cmsx.kernel.Process* processulong inodeNumberAddressulong entryNameAddress)
1020         {
1021             FileSystem* fs = GetMountTable().GetFileSystem(DirINode()->Key().fsNumber);
1022             return fs->ReadDirectory(processthisinodeNumberAddressentryNameAddress);
1023         }
1024         public void* GetFindHandle() const
1025         {
1026             return findHandle;
1027         }
1028         public void SetFindHandle(void* findHandle_)
1029         {
1030             findHandle = findHandle_;
1031         }
1032         public override File* Share()
1033         {
1034             if (DirINode() != null)
1035             {
1036                 DirINode()->SetUseCount(DirINode()->GetUseCount() + 1);
1037             }
1038             return new HostDirectoryFile(DirINode());
1039         }
1040         public override void Release(int fd)
1041         {
1042             if (findHandle != null)
1043             {
1044                 OsFindClose(findHandle);
1045                 findHandle = null;
1046             }
1047             base->Release(fd);
1048         }
1049         private void* findHandle;
1050     }
1051 
1052     public class PipeInputFile : File
1053     {
1054         public nothrow PipeInputFile(Fifo* fifo_) : base("PIPE INPUT")fifo(fifo_)
1055         {
1056             fifo->Share();
1057         }
1058         public override long Read(Machine& machinecmsx.kernel.Process* readerulong bufferAddresslong count)
1059         {
1060             return fifo->Read(machinereaderbufferAddresscount);
1061         }
1062         public override File* Share()
1063         {
1064             return new PipeInputFile(fifo);
1065         }
1066         public override void Release(int fd)
1067         {
1068             if (Log())
1069             {
1070                 LogMessage("fs.file""pipe.input.release.begin.fd=" + ToString(fd));
1071             }
1072             fifo->Release();
1073             delete this;
1074             if (Log())
1075             {
1076                 LogMessage("fs.file""pipe.input.release.end");
1077             }
1078         }
1079         private Fifo* fifo;
1080     }
1081 
1082     public class PipeOutputFile : File
1083     {
1084         public nothrow PipeOutputFile(Fifo* fifo_) : base("PIPE OUTPUT")fifo(fifo_)
1085         {
1086             fifo->Share();
1087             fifo->AddWriter();
1088         }
1089         public override long Write(Machine& machinecmsx.kernel.Process* writerulong bufferAddresslong count)
1090         {
1091             return fifo->Write(machinewriterbufferAddresscount);
1092         }
1093         public override File* Share()
1094         {
1095             return new PipeOutputFile(fifo);
1096         }
1097         public override void Release(int fd)
1098         {
1099             if (Log())
1100             {
1101                 LogMessage("fs.file""pipe.output.release.begin.fd=" + ToString(fd));
1102             }
1103             fifo->RemoveWriter();
1104             fifo->Release();
1105             delete this;
1106             if (Log())
1107             {
1108                 LogMessage("fs.file""pipe.output.release.end");
1109             }
1110         }
1111         private Fifo* fifo;
1112     }
1113 
1114     public class FilePtr
1115     {
1116         public nothrow FilePtr(INode* inode_OpenFlags flags_) : inode(inode_)pos(0)flags(flags_)useCount(1)
1117         {
1118             if ((flags & OpenFlags.append) != OpenFlags.none)
1119             {
1120                 pos = inode->GetFileSize();
1121             }
1122         }
1123         public long Write(Machine& machinecmsx.kernel.Process* writerulong bufferAddresslong count)
1124         {
1125             if (Log())
1126             {
1127                 LogMessage("fs.file""fileptr.write.begin");
1128             }
1129             while (inode->GetFlag(INode.Flags.locked))
1130             {
1131                 inode->AddWaitingProcess(writer);
1132                 SleepProcess(writerinodeUnlockedEventcast<ulong>(cast<void*>(inode))0u);
1133             }
1134             long bytesWritten = 0;
1135             {
1136                 FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
1137                 INodeLock lock(inode);
1138                 ulong offset = 0u;
1139                 while (count > 0)
1140                 {
1141                     int logicalBlockNumber = cast<int>(pos / blockSize);
1142                     int blockNumber = 0;
1143                     int blockOffset = 0;
1144                     fs->GetBlockManager()->GetBlockNumber(inodeposblockNumberblockOffsettrue);
1145                     long bytesToWrite = Min(countblockSize - blockOffset);
1146                     Block* block = null;
1147                     bool clearBlock = false;
1148                     if (inode->GetNumBlocks() > logicalBlockNumber && (blockOffset != 0 || bytesToWrite != blockSize))
1149                     {
1150                         block = fs->GetBlockManager()->ReadBlock(BlockKey(blockNumberinode->Key().fsNumber)null);
1151                     }
1152                     else
1153                     {
1154                         block = fs->GetBlockManager()->GetBlock(BlockKey(blockNumberinode->Key().fsNumber)null);
1155                         block->Clear();
1156                     }
1157                     BlockPutter blockPutter(block);
1158                     FileBlock* fileBlock = cast<FileBlock*>(block);
1159                     UniquePtr<byte> buffer(cast<byte*>(RtMemAlloc(bytesToWrite)));
1160                     ReadProcessMemory(machinewriterbufferAddress + offsetbuffer.Get()cast<ulong>(bytesToWrite));
1161                     MemoryReader reader(buffer.Get()bytesToWrite);
1162                     for (long i = 0; i < bytesToWrite; ++i;)
1163                     {
1164                         byte x = reader.ReadByte();
1165                         fileBlock->SetByte(blockOffset + ix);
1166                     }
1167                     fileBlock->SetFlag(Block.Flags.dirty);
1168                     fs->GetBlockManager()->WriteBlock(blocknull);
1169                     count = count - bytesToWrite;
1170                     pos = pos + bytesToWrite;
1171                     offset = offset + cast<ulong>(bytesToWrite);
1172                     bytesWritten = bytesWritten + bytesToWrite;
1173                 }
1174                 if (pos > inode->GetFileSize())
1175                 {
1176                     inode->SetFileSize(pos);
1177                 }
1178             }
1179             inode->SetMTime();
1180             if (Log())
1181             {
1182                 LogMessage("fs.file""fileptr.write.end.written=" + ToString(bytesWritten));
1183             }
1184             return bytesWritten;
1185         }
1186         public long Read(Machine& machinecmsx.kernel.Process* readerulong bufferAddresslong count)
1187         {
1188             while (inode->GetFlag(INode.Flags.locked))
1189             {
1190                 inode->AddWaitingProcess(reader);
1191                 SleepProcess(readerinodeUnlockedEventcast<ulong>(cast<void*>(inode))0u);
1192             }
1193             FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
1194             long bytesRead = 0;
1195             {
1196                 INodeLock lock(inode);
1197                 ulong offset = 0u;
1198                 while (count > 0)
1199                 {
1200                     int blockNumber = 0;
1201                     int blockOffset = 0;
1202                     fs->GetBlockManager()->GetBlockNumber(inodeposblockNumberblockOffsetfalse);
1203                     long bytesToRead = Min(countblockSize - blockOffset);
1204                     long bytesLeft = Max(inode->GetFileSize() - pos0);
1205                     bytesToRead = Min(bytesToReadbytesLeft);
1206                     if (bytesToRead == 0)
1207                     {
1208                         break;
1209                     }
1210                     Block* block = null;
1211                     UniquePtr<byte> buffer(cast<byte*>(RtMemAlloc(bytesToRead)));
1212                     MemoryWriter writer(buffer.Get()bytesToRead);
1213                     if (!fs->IsRootFileSystem() || blockNumber != 0)
1214                     {
1215                         block = fs->GetBlockManager()->ReadBlock(BlockKey(blockNumberinode->Key().fsNumber)null);
1216                         BlockPutter blockPutter(block);
1217                         FileBlock* fileBlock = cast<FileBlock*>(block);
1218                         for (long i = 0; i < bytesToRead; ++i;)
1219                         {
1220                             byte x = fileBlock->GetByte(blockOffset + i);
1221                             writer.Write(x);
1222                         }
1223                     }
1224                     else
1225                     {
1226                         for (long i = 0; i < bytesToRead; ++i;)
1227                         {
1228                             byte x = 0u;
1229                             writer.Write(x);
1230                         }
1231                     }
1232                     WriteProcessMemory(machinereaderbufferAddress + offsetbuffer.Get()cast<ulong>(bytesToRead)Protection.write);
1233                     count = count - bytesToRead;
1234                     pos = pos + bytesToRead;
1235                     offset = offset + cast<ulong>(bytesToRead);
1236                     bytesRead = bytesRead + bytesToRead;
1237                 }
1238             }
1239             inode->SetATime();
1240             return bytesRead;
1241         }
1242         public long Seek(long pOrigin origin)
1243         {
1244             switch (origin)
1245             {
1246                 case Origin.seekSet: pos = p; return pos;
1247                 case Origin.seekCur: pos = pos + p; return pos;
1248                 case Origin.seekEnd: pos = inode->GetFileSize() + p; return pos;
1249             }
1250             return -1;
1251         }
1252         public long Tell()
1253         {
1254             return pos;
1255         }
1256         public nothrow bool IsHostTextFile() const
1257         {
1258             if ((flags & OpenFlags.text) != OpenFlags.none)
1259             {
1260                 FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
1261                 return fs->IsHostFileSystem();
1262             }
1263             else
1264             {
1265                 return false;
1266             }
1267         }
1268         public FilePtr* Share()
1269         {
1270             ++useCount;
1271             if (Log())
1272             {
1273                 LogMessage("fs.file""fileptr.share.useCount=" + ToString(useCount));
1274             }
1275             return this;
1276         }
1277         public void Release()
1278         {
1279             --useCount;
1280             if (Log())
1281             {
1282                 LogMessage("fs.file""fileptr.release.begin.useCount=" + ToString(useCount));
1283             }
1284             if (useCount == 0)
1285             {
1286                 if (inode != null)
1287                 {
1288                     inode->Manager()->PutINode(inode);
1289                 }
1290                 delete this;
1291             }
1292             if (Log())
1293             {
1294                 LogMessage("fs.file""fileptr.release.end");
1295             }
1296         }
1297         public inline nothrow OpenFlags Flags() const
1298         {
1299             return flags;
1300         }
1301         private INode* inode;
1302         private long pos;
1303         private OpenFlags flags;
1304         private int useCount;
1305     }
1306 
1307     public class Fifo
1308     {
1309         public nothrow Fifo(INode* inode_) : inode(inode_)useCount(0)writerCount(0)
1310         {
1311         }
1312         public inline nothrow void AddWriter()
1313         {
1314             ++writerCount;
1315             if (Log())
1316             {
1317                 LogMessage("fs.file""fifo.addwriter.writers=" + ToString(writerCount));
1318             }
1319         }
1320         public inline nothrow void RemoveWriter()
1321         {
1322             --writerCount;
1323             if (Log())
1324             {
1325                 LogMessage("fs.file""fifo.removewriter.writers=" + ToString(writerCount));
1326             }
1327         }
1328         public long Write(Machine& machinecmsx.kernel.Process* writerulong bufferAddresslong count)
1329         {
1330             if (Log())
1331             {
1332                 LogMessage("fs.file""fifo.write.begin");
1333             }
1334             long bytesWritten = 0;
1335             {
1336                 FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
1337                 long pipeBlockSize = maxPipeSize % blockSize;
1338                 if (pipeBlockSize == 0)
1339                 {
1340                     pipeBlockSize = blockSize;
1341                 }
1342                 ulong offset = 0u;
1343                 long writePos = inode->WritePos();
1344                 long readPos = inode->ReadPos();
1345                 long room = 0;
1346                 if (writePos > readPos)
1347                 {
1348                     room = maxPipeSize - (writePos - readPos);
1349                 }
1350                 else if (inode->GetFlag(INode.Flags.pipeFull))
1351                 {
1352                     room = 0;
1353                 }
1354                 else if (inode->GetFlag(INode.Flags.pipeEmpty))
1355                 {
1356                     room = maxPipeSize;
1357                 }
1358                 else
1359                 {
1360                     room = readPos - writePos;
1361                 }
1362                 while (count > 0)
1363                 {
1364                     while (room == 0)
1365                     {
1366                         if (Log())
1367                         {
1368                             LogMessage("fs.file""fifo.write.sleep");
1369                         }
1370                         inode->AddWaitingPipeWriter(writer);
1371                         SleepProcess(writerpipeNotFullEvent0u0u);
1372                         if (Log())
1373                         {
1374                             LogMessage("fs.file""fifo.write.wakeup");
1375                         }
1376                         writePos = inode->WritePos();
1377                         readPos = inode->ReadPos();
1378                         if (writePos > readPos)
1379                         {
1380                             room = maxPipeSize - (writePos - readPos);
1381                         }
1382                         else if (inode->GetFlag(INode.Flags.pipeFull))
1383                         {
1384                             room = 0;
1385                         }
1386                         else if (inode->GetFlag(INode.Flags.pipeEmpty))
1387                         {
1388                             room = maxPipeSize;
1389                         }
1390                         else
1391                         {
1392                             room = readPos - writePos;
1393                         }
1394                     }
1395                     int logicalBlockNumber = cast<int>(writePos / blockSize);
1396                     int blockNumber = 0;
1397                     int blockOffset = 0;
1398                     fs->GetBlockManager()->GetBlockNumber(inodewritePosblockNumberblockOffsettrue);
1399                     long bytesToWrite = Min(roomMin(countpipeBlockSize - blockOffset));
1400                     Block* block = fs->GetBlockManager()->GetBlock(BlockKey(blockNumberinode->Key().fsNumber)null);
1401                     BlockPutter blockPutter(block);
1402                     FileBlock* fileBlock = cast<FileBlock*>(block);
1403                     UniquePtr<byte> buffer(cast<byte*>(RtMemAlloc(bytesToWrite)));
1404                     ReadProcessMemory(machinewriterbufferAddress + offsetbuffer.Get()cast<ulong>(bytesToWrite));
1405                     MemoryReader reader(buffer.Get()bytesToWrite);
1406                     for (long i = 0; i < bytesToWrite; ++i;)
1407                     {
1408                         byte x = reader.ReadByte();
1409                         fileBlock->SetByte(blockOffset + ix);
1410                     }
1411                     fileBlock->SetFlag(Block.Flags.dirty);
1412                     fileBlock->SetFlag(Block.Flags.valid);
1413                     fs->GetBlockManager()->WriteBlock(blocknull);
1414                     count = count - bytesToWrite;
1415                     writePos = (writePos + bytesToWrite) % maxPipeSize;
1416                     inode->SetWritePos(writePos);
1417                     offset = offset + cast<ulong>(bytesToWrite);
1418                     room = room - bytesToWrite;
1419                     inode->ResetFlag(INode.Flags.pipeEmpty);
1420                     if (room == 0)
1421                     {
1422                         inode->SetFlag(INode.Flags.pipeFull);
1423                     }
1424                     WakeUpPipeReaders();
1425                     bytesWritten = bytesWritten + bytesToWrite;
1426                 }
1427             }
1428             inode->SetMTime();
1429             if (Log())
1430             {
1431                 LogMessage("fs.file""fifo.write.end.written=" + ToString(bytesWritten));
1432             }
1433             return bytesWritten;
1434         }
1435         public long Read(Machine& machinecmsx.kernel.Process* readerulong bufferAddresslong count)
1436         {
1437             if (Log())
1438             {
1439                 LogMessage("fs.file""fifo.read.begin");
1440             }
1441             FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
1442             long bytesRead = 0;
1443             {
1444                 long pipeBlockSize = maxPipeSize % blockSize;
1445                 if (pipeBlockSize == 0)
1446                 {
1447                     pipeBlockSize = blockSize;
1448                 }
1449                 ulong offset = 0u;
1450                 long writePos = inode->WritePos();
1451                 long readPos = inode->ReadPos();
1452                 long bytesAvailable = 0;
1453                 if (writePos > readPos)
1454                 {
1455                     bytesAvailable = writePos - readPos;
1456                 }
1457                 else if (inode->GetFlag(INode.Flags.pipeFull))
1458                 {
1459                     bytesAvailable = maxPipeSize;
1460                 }
1461                 else if (inode->GetFlag(INode.Flags.pipeEmpty))
1462                 {
1463                     bytesAvailable = 0;
1464                 }
1465                 else
1466                 {
1467                     bytesAvailable = maxPipeSize - (readPos - writePos);
1468                 }
1469                 while (count > 0)
1470                 {
1471                     while (bytesAvailable == 0 && writerCount > 0)
1472                     {
1473                         if (Log())
1474                         {
1475                             LogMessage("fs.file""fifo.read.sleep");
1476                         }
1477                         inode->AddWaitingPipeReader(reader);
1478                         SleepProcess(readerpipeNotEmptyEvent0u0u);
1479                         if (Log())
1480                         {
1481                             LogMessage("fs.file""fifo.read.wakeup");
1482                         }
1483                         writePos = inode->WritePos();
1484                         readPos = inode->ReadPos();
1485                         if (writePos > readPos)
1486                         {
1487                             bytesAvailable = writePos - readPos;
1488                         }
1489                         else if (inode->GetFlag(INode.Flags.pipeFull))
1490                         {
1491                             bytesAvailable = maxPipeSize;
1492                         }
1493                         else if (inode->GetFlag(INode.Flags.pipeEmpty))
1494                         {
1495                             bytesAvailable = 0;
1496                         }
1497                         else
1498                         {
1499                             bytesAvailable = maxPipeSize - (readPos - writePos);
1500                         }
1501                     }
1502                     int blockNumber = 0;
1503                     int blockOffset = 0;
1504                     fs->GetBlockManager()->GetBlockNumber(inodereadPosblockNumberblockOffsetfalse);
1505                     long bytesToRead = Min(bytesAvailableMin(countpipeBlockSize - blockOffset));
1506                     if (bytesToRead == 0 && writerCount == 0)
1507                     {
1508                         break;
1509                     }
1510                     UniquePtr<byte> buffer(cast<byte*>(RtMemAlloc(bytesToRead)));
1511                     MemoryWriter writer(buffer.Get()bytesToRead);
1512                     Block* block = fs->GetBlockManager()->ReadBlock(BlockKey(blockNumberinode->Key().fsNumber)null);
1513                     BlockPutter blockPutter(block);
1514                     FileBlock* fileBlock = cast<FileBlock*>(block);
1515                     for (long i = 0; i < bytesToRead; ++i;)
1516                     {
1517                         byte x = fileBlock->GetByte(blockOffset + i);
1518                         writer.Write(x);
1519                     }
1520                     WriteProcessMemory(machinereaderbufferAddress + offsetbuffer.Get()cast<ulong>(bytesToRead)Protection.write);
1521                     count = count - bytesToRead;
1522                     readPos = (readPos + bytesToRead) % maxPipeSize;
1523                     inode->SetReadPos(readPos);
1524                     offset = offset + cast<ulong>(bytesToRead);
1525                     bytesAvailable = bytesAvailable - bytesToRead;
1526                     inode->ResetFlag(INode.Flags.pipeFull);
1527                     if (bytesAvailable == 0)
1528                     {
1529                         inode->SetFlag(INode.Flags.pipeEmpty);
1530                     }
1531                     WakeUpPipeWriters();
1532                     bytesRead = bytesRead + bytesToRead;
1533                 }
1534             }
1535             inode->SetATime();
1536             if (Log())
1537             {
1538                 LogMessage("fs.file""fifo.read.end.read=" + ToString(bytesRead));
1539             }
1540             return bytesRead;
1541         }
1542         public Fifo* Share()
1543         {
1544             ++useCount;
1545             if (Log())
1546             {
1547                 LogMessage("fs.file""fifo.share.useCount=" + ToString(useCount));
1548             }
1549             return this;
1550         }
1551         public void Release()
1552         {
1553             --useCount;
1554             if (Log())
1555             {
1556                 LogMessage("fs.file""fifo.release.useCount=" + ToString(useCount));
1557             }
1558             if (useCount == 0)
1559             {
1560                 if (inode != null)
1561                 {
1562                     inode->SetNumLinks(inode->GetNumLinks() - 1);
1563                     inode->Manager()->PutINode(inode);
1564                 }
1565                 delete this;
1566             }
1567             if (Log())
1568             {
1569                 LogMessage("fs.file""fifo.release.end");
1570             }
1571         }
1572         private nothrow void WakeUpPipeWriters()
1573         {
1574             Kernel& kernel = GetKernel();
1575             ProcessTable& processTable = kernel.GetProcessTable();
1576             List<cmsx.kernel.Process*> waitingPipeWriters = inode->GetWaitingPipeWriters();
1577             for (cmsx.kernel.Process* writer : waitingPipeWriters)
1578             {
1579                 WakeUpProcess(processTablewriter);
1580             }
1581         }
1582         private nothrow void WakeUpPipeReaders()
1583         {
1584             Kernel& kernel = GetKernel();
1585             ProcessTable& processTable = kernel.GetProcessTable();
1586             List<cmsx.kernel.Process*> waitingPipeReaders = inode->GetWaitingPipeReaders();
1587             for (cmsx.kernel.Process* reader : waitingPipeReaders)
1588             {
1589                 WakeUpProcess(processTablereader);
1590             }
1591         }
1592         private INode* inode;
1593         private int useCount;
1594         private int writerCount;
1595     }
1596 
1597     public class ProcessFileTable
1598     {
1599         public void SetFile(int fdFile* file)
1600         {
1601             if (fd < 0)
1602             {
1603                 throw SystemError(EBADF"value negative");
1604             }
1605             else if (fd >= openFileMax)
1606             {
1607                 throw SystemError(EBADF"value exceeds max number of open files");
1608             }
1609             files[fd] = file;
1610         }
1611         public File* GetFile(int fd)
1612         {
1613             if (fd < 0)
1614             {
1615                 throw SystemError(EBADF"descriptor value is negative");
1616             }
1617             else if (fd >= openFileMax)
1618             {
1619                 throw SystemError(EBADF"descriptor value (" + ToString(fd) + ") exceeds maximum number of open files");
1620             }
1621             File* file = files[fd];
1622             if (file == null)
1623             {
1624                 throw SystemError(EBADF"file " + ToString(fd) + " not open");
1625             }
1626             return file;
1627         }
1628         public int GetEmptyFileSlot()
1629         {
1630             for (long i = 0; i < openFileMax; ++i;)
1631             {
1632                 File* file = files[i];
1633                 if (file == null)
1634                 {
1635                     return cast<int>(i);
1636                 }
1637             }
1638             throw SystemError(ERLIMITEXCEEDED"maximum number of open files (" + ToString(openFileMax) + ") exceeded");
1639         }
1640         public void ShareFilesTo(ProcessFileTable& that)
1641         {
1642             if (Log())
1643             {
1644                 LogMessage("fs.file""share.files.begin");
1645             }
1646             for (long i = 0; i < openFileMax; ++i;)
1647             {
1648                 File* file = files[i];
1649                 if (file != null)
1650                 {
1651                     that.files[i] = file->Share();
1652                 }
1653                 else
1654                 {
1655                     that.files[i] = null;
1656                 }
1657             }
1658             if (Log())
1659             {
1660                 LogMessage("fs.file""share.files.end");
1661             }
1662         }
1663         public void ReleaseFiles()
1664         {
1665             if (Log())
1666             {
1667                 LogMessage("fs.file""release.files.begin");
1668             }
1669             for (long i = 0; i < openFileMax; ++i;)
1670             {
1671                 File* file = files[i];
1672                 if (file != null)
1673                 {
1674                     file->Release(cast<int>(i));
1675                     files[i] = null;
1676                 }
1677             }
1678             if (Log())
1679             {
1680                 LogMessage("fs.file""release.files.end");
1681             }
1682         }
1683         private File*[openFileMax] files;
1684     }
1685 
1686     public class GlobalFileTable
1687     {
1688         public inline nothrow DebuggerInputFile* GetDebuggerInputFile()
1689         {
1690             return &debuggerInputFile;
1691         }
1692         public inline nothrow DebuggerOutputFile* GetDebuggerOutputFile()
1693         {
1694             return &debuggerOutputFile;
1695         }
1696         public inline nothrow ConsoleInputFile* GetConsoleInputFile()
1697         {
1698             return &consoleInputFile;
1699         }
1700         public inline nothrow ConsoleOutputFile* GetConsoleOutputFile()
1701         {
1702             return &consoleOutputFile;
1703         }
1704         private DebuggerInputFile debuggerInputFile;
1705         private DebuggerOutputFile debuggerOutputFile;
1706         private ConsoleInputFile consoleInputFile;
1707         private ConsoleOutputFile consoleOutputFile;
1708     }
1709 
1710     public void InitializeFileTable(Machine& machineProcessFileTable& fileTable)
1711     {
1712         Kernel& kernel = Kernel.Instance();
1713         GlobalFileTable& globalFileTable = kernel.FileTable();
1714         if (kernel.HasUserDebugger())
1715         {
1716             fileTable.SetFile(0globalFileTable.GetDebuggerInputFile());
1717             fileTable.SetFile(1globalFileTable.GetDebuggerOutputFile());
1718             fileTable.SetFile(2globalFileTable.GetDebuggerOutputFile());
1719         }
1720         else
1721         {
1722             fileTable.SetFile(0globalFileTable.GetConsoleInputFile());
1723             fileTable.SetFile(1globalFileTable.GetConsoleOutputFile());
1724             fileTable.SetFile(2globalFileTable.GetConsoleOutputFile());
1725         }
1726     }
1727 }