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 seekSet, seekCur, seekEnd
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& machine, cmsx.kernel.Process* process, ulong pathAddress, int mode)
39 {
40 if (pathAddress == 0u)
41 {
42 throw SystemError(EINVAL, "path address is null");
43 }
44 string path;
45 ReadStringFromProcessMemory(machine, process, pathAddress, path);
46 if (path.IsEmpty())
47 {
48 throw SystemError(EINVAL, "path address is empty");
49 }
50 FileSystem* fs = GetMountTable().GetFileSystem(0);
51 return fs->Create(process, path, mode, true);
52 }
53
54 public int Open(Machine& machine, cmsx.kernel.Process* process, ulong pathAddress, OpenFlags flags, int mode)
55 {
56 if (pathAddress == 0u)
57 {
58 throw SystemError(EINVAL, "path address is null");
59 }
60 string path;
61 ReadStringFromProcessMemory(machine, process, pathAddress, path);
62 if (path.IsEmpty())
63 {
64 throw SystemError(EINVAL, "path is empty");
65 }
66 FileSystem* fs = GetMountTable().GetFileSystem(0);
67 return fs->Open(process, path, flags, mode);
68 }
69
70 public void Close(Machine& machine, cmsx.kernel.Process* process, int fd)
71 {
72 File* file = process->fileTable.GetFile(fd);
73 if (file != null)
74 {
75 file->Release(fd);
76 process->fileTable.SetFile(fd, null);
77 }
78 else
79 {
80 throw SystemError(EBADF, "file '" + ToString(fd) + "' not open");
81 }
82 }
83
84 public long Read(Machine& machine, cmsx.kernel.Process* process, int fd, ulong bufferAddress, long count)
85 {
86 File* file = process->fileTable.GetFile(fd);
87 if (count > 0)
88 {
89 return file->Read(machine, process, bufferAddress, count);
90 }
91 return 0;
92 }
93
94 public long Write(Machine& machine, cmsx.kernel.Process* process, int fd, ulong bufferAddress, long count)
95 {
96 File* file = process->fileTable.GetFile(fd);
97 if (count > 0)
98 {
99 return file->Write(machine, process, bufferAddress, count);
100 }
101 return 0;
102 }
103
104 public long Seek(Machine& machine, cmsx.kernel.Process* process, int fd, long pos, Origin origin)
105 {
106 File* file = process->fileTable.GetFile(fd);
107 return file->Seek(pos, origin);
108 }
109
110 public long Tell(Machine& machine, cmsx.kernel.Process* process, int fd)
111 {
112 File* file = process->fileTable.GetFile(fd);
113 return file->Tell();
114 }
115
116 public int Dup(cmsx.kernel.Process* process, int fd)
117 {
118 File* file = process->fileTable.GetFile(fd);
119 File* shared = file->Share();
120 int duplicate = process->fileTable.GetEmptyFileSlot();
121 process->fileTable.SetFile(duplicate, shared);
122 return duplicate;
123 }
124
125 public void Pipe(Machine& machine, cmsx.kernel.Process* process, ulong readFDAddress, ulong 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(inodeNumber, 0);
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(readFD, pipeInput);
146 int writeFD = process->fileTable.GetEmptyFileSlot();
147 PipeOutputFile* pipeOutput = new PipeOutputFile(fifo);
148 process->fileTable.SetFile(writeFD, pipeOutput);
149 WriteProcessMemory(machine, process, readFDAddress, cast<ulong>(readFD), 4u, Protection.write);
150 WriteProcessMemory(machine, process, writeFDAddress, cast<ulong>(writeFD), 4u, Protection.write);
151 inodePutter.ResetINode();
152 }
153
154 public void Link(Machine& machine, cmsx.kernel.Process* process, ulong sourcePathAddress, ulong 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(machine, process, sourcePathAddress, sourcePath);
166 if (sourcePath.IsEmpty())
167 {
168 throw SystemError(EINVAL, "source path is empty");
169 }
170 string targetPath;
171 ReadStringFromProcessMemory(machine, process, targetPathAddress, targetPath);
172 if (targetPath.IsEmpty())
173 {
174 throw SystemError(EINVAL, "target path is empty");
175 }
176 FileSystem* fs = GetMountTable().GetFileSystem(0);
177 fs->Link(process, sourcePath, targetPath);
178 }
179
180 public void Unlink(Machine& machine, cmsx.kernel.Process* process, ulong pathAddress)
181 {
182 if (pathAddress == 0u)
183 {
184 throw SystemError(EINVAL, "path address is null");
185 }
186 string path;
187 ReadStringFromProcessMemory(machine, process, pathAddress, path);
188 if (path.IsEmpty())
189 {
190 throw SystemError(EINVAL, "path is empty");
191 }
192 FileSystem* fs = GetMountTable().GetFileSystem(0);
193 fs->Unlink(process, path);
194 }
195
196 public void Rename(Machine& machine, cmsx.kernel.Process* process, ulong sourcePathAddress, ulong 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(machine, process, sourcePathAddress, sourcePath);
208 if (sourcePath.IsEmpty())
209 {
210 throw SystemError(EINVAL, "source path is empty");
211 }
212 string targetPath;
213 ReadStringFromProcessMemory(machine, process, targetPathAddress, targetPath);
214 if (targetPath.IsEmpty())
215 {
216 throw SystemError(EINVAL, "target path is empty");
217 }
218 FileSystem* fs = GetMountTable().GetFileSystem(0);
219 fs->Rename(process, sourcePath, targetPath);
220 }
221
222 public void Mkdir(Machine& machine, cmsx.kernel.Process* process, ulong pathAddress, int mode)
223 {
224 if (pathAddress == 0u)
225 {
226 throw SystemError(EINVAL, "path address is null");
227 }
228 string path;
229 ReadStringFromProcessMemory(machine, process, pathAddress, path);
230 if (path.IsEmpty())
231 {
232 throw SystemError(EINVAL, "path is empty");
233 }
234 FileSystem* fs = GetMountTable().GetFileSystem(0);
235 fs->MakeDirectory(process, path, mode);
236 }
237
238 public int OpenDir(Machine& machine, cmsx.kernel.Process* process, ulong pathAddress)
239 {
240 if (pathAddress == 0u)
241 {
242 throw SystemError(EINVAL, "path address is null");
243 }
244 string path;
245 ReadStringFromProcessMemory(machine, process, pathAddress, path);
246 if (path.IsEmpty())
247 {
248 throw SystemError(EINVAL, "path is empty");
249 }
250 FileSystem* fs = GetMountTable().GetFileSystem(0);
251 return fs->OpenDirectory(process, path);
252 }
253
254 public int ReadDir(Machine& machine, cmsx.kernel.Process* process, int fd, ulong inodeNumberAddress, ulong 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(process, inodeNumberAddress, entryNameAddress);
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& machine, cmsx.kernel.Process* process, int 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(fd, null);
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& machine, cmsx.kernel.Process* process, ulong pathBufferAddress, long 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(machine, process, pathBufferAddress, cast<byte*>(cast<void*>(currentWorkindDirectory.Chars())), cast<ulong>(currentWorkindDirectory.Length() + 1), Protection.write);
321 }
322
323 public void ChDir(Machine& machine, cmsx.kernel.Process* process, ulong pathAddress)
324 {
325 if (pathAddress == 0u)
326 {
327 throw SystemError(EINVAL, "path address is null");
328 }
329 string path;
330 ReadStringFromProcessMemory(machine, process, pathAddress, path);
331 if (path.IsEmpty())
332 {
333 throw SystemError(EINVAL, "path is empty");
334 }
335 FileSystem* fs = GetMountTable().GetFileSystem(0);
336 fs->ChDir(process, path);
337 }
338
339 public void Stat(Machine& machine, cmsx.kernel.Process* process, ulong pathAddress, ulong statBufAddress)
340 {
341 if (pathAddress == 0u)
342 {
343 throw SystemError(EINVAL, "path address is null");
344 }
345 string path;
346 ReadStringFromProcessMemory(machine, process, pathAddress, path);
347 if (path.IsEmpty())
348 {
349 throw SystemError(EINVAL, "path is empty");
350 }
351 FileSystem* fs = GetMountTable().GetFileSystem(0);
352 fs->Stat(process, path, statBufAddress);
353 }
354
355 public int UMask(Machine& machine, cmsx.kernel.Process* process, int 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& machine, cmsx.kernel.Process* process, int fd, int item, ulong bufferAddress, long 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(machine, process, item, bufferAddress, count);
374 }
375
376 public int GetFGPID(cmsx.kernel.Process* process, int fd)
377 {
378 File* file = process->fileTable.GetFile(fd);
379 return file->GetFGPID();
380 }
381
382 public void SetFGPID(cmsx.kernel.Process* process, int fd, int pid)
383 {
384 File* file = process->fileTable.GetFile(fd);
385 file->SetFGPID(pid);
386 }
387
388 public void Chmod(Machine& machine, cmsx.kernel.Process* process, ulong pathAddress, int mode)
389 {
390 if (pathAddress == 0u)
391 {
392 throw SystemError(EINVAL, "path address is null");
393 }
394 string path;
395 ReadStringFromProcessMemory(machine, process, pathAddress, path);
396 if (path.IsEmpty())
397 {
398 throw SystemError(EINVAL, "path is empty");
399 }
400 FileSystem* fs = GetMountTable().GetFileSystem(0);
401 fs->ChangeMode(process, path, mode);
402 }
403
404 public void Chown(Machine& machine, cmsx.kernel.Process* process, ulong pathAddress, int uid, int gid)
405 {
406 if (pathAddress == 0u)
407 {
408 throw SystemError(EINVAL, "path address is null");
409 }
410 string path;
411 ReadStringFromProcessMemory(machine, process, pathAddress, path);
412 if (path.IsEmpty())
413 {
414 throw SystemError(EINVAL, "path is empty");
415 }
416 FileSystem* fs = GetMountTable().GetFileSystem(0);
417 fs->ChangeOwner(process, path, uid, gid);
418 }
419
420 public void UTime(Machine& machine, cmsx.kernel.Process* process, ulong pathAddress, ulong 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(machine, process, pathAddress, path);
432 if (path.IsEmpty())
433 {
434 throw SystemError(EINVAL, "path is empty");
435 }
436 byte[16] buffer;
437 ReadProcessMemory(machine, process, timeBufAddress, &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(process, path, atime, mtime);
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& machine, cmsx.kernel.Process* writer, ulong bufferAddress, long count)
451 {
452 throw SystemError(EBADF, name + " not open for writing");
453 }
454 public virtual long Read(Machine& machine, cmsx.kernel.Process* reader, ulong bufferAddress, long count)
455 {
456 throw SystemError(EBADF, name + " not open for reading");
457 }
458 public virtual long Seek(long pos, Origin origin)
459 {
460 throw SystemError(ENOTSUPPORTED, name + " does not support seek");
461 }
462 public virtual long Tell()
463 {
464 throw SystemError(ENOTSUPPORTED, name + " 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(ENOTSUPPORTED, name + " does not support ioctl(echo)");
481 }
482 public virtual void SetEcho(bool echo)
483 {
484 throw SystemError(ENOTSUPPORTED, name + " does not support ioctl(setecho)");
485 }
486 public virtual void IOCtl(Machine& machine, cmsx.kernel.Process* process, int item, ulong bufferAddress, long count)
487 {
488 bool itemFound = true;
489 switch (item)
490 {
491 case IOCTL_IS_CONSOLE_ITEM:
492 {
493 GetConsoleStatus(machine, process, bufferAddress, count);
494 break;
495 }
496 case IOCTL_IS_HOST_TEXT_FILE_ITEM:
497 {
498 GetHostTextFileStatus(machine, process, bufferAddress, count);
499 break;
500 }
501 case IOCTL_HAS_COLORS_ITEM:
502 {
503 GetColorStatus(machine, process, bufferAddress, count);
504 break;
505 }
506 case IOCTL_GET_WINDOW_SIZE_ITEM:
507 {
508 GetWindowDimensions(machine, process, bufferAddress, count);
509 break;
510 }
511 case IOCTL_GET_COLORS_ITEM:
512 {
513 GetConsoleColors(machine, process, bufferAddress, count);
514 break;
515 }
516 case IOCTL_SET_COLORS_ITEM:
517 {
518 SetConsoleColors(machine, process, bufferAddress, count);
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(machine, process, bufferAddress, count);
534 break;
535 }
536 case IOCTL_SET_ECHO_ITEM:
537 {
538 SetEcho(machine, process, bufferAddress, count);
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(ENOTSUPPORTED, name + " does not support getfgpid()");
555 }
556 public virtual void SetFGPID(int pid)
557 {
558 throw SystemError(ENOTSUPPORTED, name + " 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& machine, cmsx.kernel.Process* process, ulong bufferAddress, long 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(machine, process, bufferAddress, &buffer[0], cast<ulong>(buffer.Length()), Protection.write);
581 }
582 private void GetHostTextFileStatus(Machine& machine, cmsx.kernel.Process* process, ulong bufferAddress, long 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(machine, process, bufferAddress, &buffer[0], cast<ulong>(buffer.Length()), Protection.write);
592 }
593 private void GetColorStatus(Machine& machine, cmsx.kernel.Process* process, ulong bufferAddress, long 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(machine, process, bufferAddress, &buffer[0], cast<ulong>(buffer.Length()), Protection.write);
603 }
604 private void GetEcho(Machine& machine, cmsx.kernel.Process* process, ulong bufferAddress, long 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(machine, process, bufferAddress, &buffer[0], cast<ulong>(buffer.Length()), Protection.write);
615 }
616 private void SetEcho(Machine& machine, cmsx.kernel.Process* process, ulong bufferAddress, long 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(machine, process, bufferAddress, &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& machine, cmsx.kernel.Process* process, ulong bufferAddress, long 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(rows, cols);
639 }
640 byte[8] buffer;
641 MemoryWriter writer(&buffer[0], buffer.Length());
642 writer.Write(rows);
643 writer.Write(cols);
644 WriteProcessMemory(machine, process, bufferAddress, &buffer[0], cast<ulong>(buffer.Length()), Protection.write);
645 }
646 private void GetConsoleColors(Machine& machine, cmsx.kernel.Process* process, ulong bufferAddress, long 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(foregroundColor, backgroundColor);
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(machine, process, bufferAddress, &buffer[0], cast<ulong>(buffer.Length()), Protection.write);
668 }
669 private void SetConsoleColors(Machine& machine, cmsx.kernel.Process* process, ulong bufferAddress, long 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(machine, process, bufferAddress, &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(foregroundColor, backgroundColor);
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& machine, cmsx.kernel.Process* reader, ulong bufferAddress, long 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(count, bufferedInput.Length());
726 WriteProcessMemory(machine, reader, bufferAddress, cast<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(EFAIL, ex.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& machine, cmsx.kernel.Process* writer, ulong bufferAddress, long 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(machine, writer, bufferAddress, buffer.Get(), cast<ulong>(count));
775 debugger->WriteOutput(buffer.Get(), count);
776 return count;
777 }
778 catch (const Exception& ex)
779 {
780 throw SystemError(EFAIL, ex.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& machine, cmsx.kernel.Process* reader, ulong bufferAddress, long 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(reader, bufferAddress, count);
822 }
823 }
824 long n = Min(count, bufferedInput.Length());
825 WriteProcessMemory(machine, reader, bufferAddress, cast<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(EFAIL, ex.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* reader, ulong bufferAddress, long count)
853 {
854 SleepProcess(reader, consoleInputEvent, bufferAddress, cast<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& machine, cmsx.kernel.Process* writer, ulong bufferAddress, long count)
889 {
890 try
891 {
892 UniquePtr<byte> buffer(cast<byte*>(RtMemAlloc(count)));
893 ReadProcessMemory(machine, writer, bufferAddress, buffer.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(EFAIL, ex.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& machine, cmsx.kernel.Process* writer, ulong bufferAddress, long count)
921 {
922 if ((filePtr->Flags() & OpenFlags.write) == OpenFlags.none)
923 {
924 throw SystemError(EBADF, name + " not open for writing");
925 }
926 return filePtr->Write(machine, writer, bufferAddress, count);
927 }
928 public override long Read(Machine& machine, cmsx.kernel.Process* reader, ulong bufferAddress, long count)
929 {
930 if ((filePtr->Flags() & OpenFlags.read) == OpenFlags.none)
931 {
932 throw SystemError(EBADF, name + " not open for reading");
933 }
934 return filePtr->Read(machine, reader, bufferAddress, count);
935 }
936 public override long Seek(long pos, Origin origin)
937 {
938 return filePtr->Seek(pos, origin);
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(name, filePtr->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* process, ulong inodeNumberAddress, ulong 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(process, this, inodeNumberAddress, entryNameAddress);
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* process, ulong inodeNumberAddress, ulong entryNameAddress)
1020 {
1021 FileSystem* fs = GetMountTable().GetFileSystem(DirINode()->Key().fsNumber);
1022 return fs->ReadDirectory(process, this, inodeNumberAddress, entryNameAddress);
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& machine, cmsx.kernel.Process* reader, ulong bufferAddress, long count)
1059 {
1060 return fifo->Read(machine, reader, bufferAddress, count);
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& machine, cmsx.kernel.Process* writer, ulong bufferAddress, long count)
1090 {
1091 return fifo->Write(machine, writer, bufferAddress, count);
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& machine, cmsx.kernel.Process* writer, ulong bufferAddress, long 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(writer, inodeUnlockedEvent, cast<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(inode, pos, blockNumber, blockOffset, true);
1145 long bytesToWrite = Min(count, blockSize - 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(blockNumber, inode->Key().fsNumber), null);
1151 }
1152 else
1153 {
1154 block = fs->GetBlockManager()->GetBlock(BlockKey(blockNumber, inode->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(machine, writer, bufferAddress + offset, buffer.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 + i, x);
1166 }
1167 fileBlock->SetFlag(Block.Flags.dirty);
1168 fs->GetBlockManager()->WriteBlock(block, null);
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& machine, cmsx.kernel.Process* reader, ulong bufferAddress, long count)
1187 {
1188 while (inode->GetFlag(INode.Flags.locked))
1189 {
1190 inode->AddWaitingProcess(reader);
1191 SleepProcess(reader, inodeUnlockedEvent, cast<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(inode, pos, blockNumber, blockOffset, false);
1203 long bytesToRead = Min(count, blockSize - blockOffset);
1204 long bytesLeft = Max(inode->GetFileSize() - pos, 0);
1205 bytesToRead = Min(bytesToRead, bytesLeft);
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(blockNumber, inode->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(machine, reader, bufferAddress + offset, buffer.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 p, Origin 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& machine, cmsx.kernel.Process* writer, ulong bufferAddress, long 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(writer, pipeNotFullEvent, 0u, 0u);
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(inode, writePos, blockNumber, blockOffset, true);
1399 long bytesToWrite = Min(room, Min(count, pipeBlockSize - blockOffset));
1400 Block* block = fs->GetBlockManager()->GetBlock(BlockKey(blockNumber, inode->Key().fsNumber), null);
1401 BlockPutter blockPutter(block);
1402 FileBlock* fileBlock = cast<FileBlock*>(block);
1403 UniquePtr<byte> buffer(cast<byte*>(RtMemAlloc(bytesToWrite)));
1404 ReadProcessMemory(machine, writer, bufferAddress + offset, buffer.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 + i, x);
1410 }
1411 fileBlock->SetFlag(Block.Flags.dirty);
1412 fileBlock->SetFlag(Block.Flags.valid);
1413 fs->GetBlockManager()->WriteBlock(block, null);
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& machine, cmsx.kernel.Process* reader, ulong bufferAddress, long 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(reader, pipeNotEmptyEvent, 0u, 0u);
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(inode, readPos, blockNumber, blockOffset, false);
1505 long bytesToRead = Min(bytesAvailable, Min(count, pipeBlockSize - 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(blockNumber, inode->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(machine, reader, bufferAddress + offset, buffer.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(processTable, writer);
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(processTable, reader);
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 fd, File* 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& machine, ProcessFileTable& fileTable)
1711 {
1712 Kernel& kernel = Kernel.Instance();
1713 GlobalFileTable& globalFileTable = kernel.FileTable();
1714 if (kernel.HasUserDebugger())
1715 {
1716 fileTable.SetFile(0, globalFileTable.GetDebuggerInputFile());
1717 fileTable.SetFile(1, globalFileTable.GetDebuggerOutputFile());
1718 fileTable.SetFile(2, globalFileTable.GetDebuggerOutputFile());
1719 }
1720 else
1721 {
1722 fileTable.SetFile(0, globalFileTable.GetConsoleInputFile());
1723 fileTable.SetFile(1, globalFileTable.GetConsoleOutputFile());
1724 fileTable.SetFile(2, globalFileTable.GetConsoleOutputFile());
1725 }
1726 }
1727 }