1 using System;
2 using System.IO;
3 using System.Collections;
4 using cmsx.util;
5 using cmsx.machine;
6
7 namespace cmsx.kernel
8 {
9 public const int rootFSMaxFiles = 1024;
10 public const int rootFSMaxBlocks = 256 * 1024;
11
12 public class RootFileSystem : FileSystem
13 {
14 public RootFileSystem(int index_) : base("root", index_, false), hostFile(null)
15 {
16 }
17 public override nothrow bool IsRootFileSystem() const
18 {
19 return true;
20 }
21 public override void Init()
22 {
23 if (Log())
24 {
25 LogMessage("fs.root", "init");
26 }
27 string cmajorRootDir = RtGetEnvironmentVariable("CMAJOR_ROOT");
28 if (cmajorRootDir.IsEmpty())
29 {
30 throw Exception("CMAJOR_ROOT environment variable not set. Please set it to contain /path/to/cmajor directory");
31 }
32 string fsDirPath = GetFullPath(Path.Combine(Path.Combine(Path.Combine(cmajorRootDir, "projects"), "cmsx"), "fs"));
33 Directory.CreateDirectories(fsDirPath);
34 hostFilePath = GetFullPath(Path.Combine(fsDirPath, "rootfs"));
35 bool create = false;
36 if (!System.IO.File.Exists(hostFilePath))
37 {
38 create = true;
39 }
40 DiskDriver& diskDriver = GetDiskDriver();
41 hostFile = diskDriver.GetOrInsertHostFile(hostFilePath, true);
42 if (create)
43 {
44 CreateRootFs(rootFSMaxFiles, rootFSMaxBlocks);
45 }
46 if (blockBitmap.IsNull())
47 {
48 blockBitmap.Reset(new BlockBitmap(0));
49 }
50 if (inodeBitmap.IsNull())
51 {
52 inodeBitmap.Reset(new INodeBitmap(0));
53 }
54 INode* mntINode = inodeManager.PathToINode(null, "/mnt");
55 if (mntINode == null)
56 {
57 MakeDirectory(null, "/mnt", cast<int>(ParseOctal("755")));
58 }
59 else
60 {
61 INodePutter putter(mntINode);
62 }
63 }
64 public override int OpenDirectory(Process* process, const string& path, INode* dirINode, INodePutter& inodePutter)
65 {
66 int fd = process->fileTable.GetEmptyFileSlot();
67 process->fileTable.SetFile(fd, new DirectoryFile(dirINode));
68 inodePutter.ResetINode();
69 dirINode->ResetFlag(INode.Flags.locked);
70 return fd;
71 }
72 public override int ReadDirectory(Process* process, DirectoryFile* dirFile, ulong inodeNumberAddress, ulong entryNameAddress)
73 {
74 INode* dirINode = dirFile->DirINode();
75 if (Log())
76 {
77 LogMessage("fs.root.readdirectory", dirINode->ToString());
78 }
79 while (dirINode->GetFlag(INode.Flags.locked))
80 {
81 dirINode->AddWaitingProcess(process);
82 SleepProcess(process, inodeUnlockedEvent, cast<ulong>(cast<void*>(dirINode)), 0u);
83 }
84 {
85 INodeLock lock(dirINode);
86 while (dirFile->Offset() < dirINode->GetFileSize())
87 {
88 int blockNumber = 0;
89 int blockOffset = 0;
90 blockManager.GetBlockNumber(dirINode, dirFile->Offset(), blockNumber, blockOffset, false);
91 if (blockNumber == 0)
92 {
93 return 0;
94 }
95 Block* block = blockManager.ReadBlock(BlockKey(blockNumber, dirINode->Key().fsNumber), null);
96 BlockPutter putter(block);
97 DirectoryBlock* directoryBlock = cast<DirectoryBlock*>(block);
98 for (int index = blockOffset / directoryEntrySize; index < numDirectoryEntriesInBlock && dirFile->Offset() < dirINode->GetFileSize(); ++index;)
99 {
100 DirectoryEntry directoryEntry = directoryBlock->GetDirectoryEntry(index);
101 if (directoryEntry.inodeNumber == 0)
102 {
103 dirFile->SetOffset(dirFile->Offset() + directoryEntrySize);
104 }
105 else
106 {
107 WriteProcessMemory(GetMachine(), process, inodeNumberAddress, cast<ulong>(directoryEntry.inodeNumber), 4u, Protection.write);
108 WriteProcessMemory(GetMachine(), process, entryNameAddress, cast<byte*>(cast<void*>(directoryEntry.name.Chars())), cast<ulong>(directoryEntry.name.Length() + 1), Protection.write);
109 dirFile->SetOffset(dirFile->Offset() + directoryEntrySize);
110 return 1;
111 }
112 }
113 }
114 }
115 return 0;
116 }
117 public override void ChangeMode(Process* process, const string& path, INode* inode, int mode)
118 {
119 if (process->uid != inode->UID())
120 {
121 throw SystemError(EPERM, "error changing mode of '" + path + "': user id of the process (" + ToString(process->uid) + ") does not match the owner (" + ToString(inode->UID()) + ") of the file");
122 }
123 inode->SetAccessMode(mode);
124 }
125 public override void ChangeOwner(Process* process, const string& path, INode* inode, int uid, int gid)
126 {
127 if (process->uid != 0)
128 {
129 throw SystemError(EPERM, "error changing owner of '" + path + "': not authorized");
130 }
131 inode->SetUID(uid);
132 inode->SetGID(gid);
133 }
134 public override void UpdateFileTimes(Process* process, const string& path, INode* inode, const DateTime& atime, const DateTime& mtime)
135 {
136 if (process->uid != inode->UID())
137 {
138 try
139 {
140 if (atime != DateTime() || mtime != DateTime())
141 {
142 throw SystemError(EPERM, "access time or modification time not default and not owner of the file");
143 }
144 inode->CheckPermissions(process->uid, process->gid, Access.write);
145 }
146 catch (const Exception& ex)
147 {
148 throw Exception("cannot change file '" + path + "' timestamps: " + ex.Message());
149 }
150 }
151 if (atime == DateTime())
152 {
153 inode->SetATime();
154 }
155 else
156 {
157 inode->SetATime(atime);
158 }
159 if (mtime == DateTime())
160 {
161 inode->SetMTime();
162 }
163 else
164 {
165 inode->SetMTime(mtime);
166 }
167 }
168 public override int AllocateBlockNumber()
169 {
170 return blockBitmap->AllocateBlockNumber();
171 }
172 public override void SetBlockFree(int blockNumber)
173 {
174 blockBitmap->SetBlockFree(blockNumber);
175 }
176 public override int GetFreeINodeNumber()
177 {
178 return inodeBitmap->GetFreeINodeNumber();
179 }
180 public override void SetFreeINodeNumber(int inodeNumber)
181 {
182 inodeBitmap->SetFreeINodeNumber(inodeNumber);
183 }
184 public override int GetFirstINodeBlockNumber() const
185 {
186 Block* block = blockManager.ReadBlock(BlockKey(0, 0), null);
187 BlockPutter putter(block);
188 SuperBlock* superBlock = cast<SuperBlock*>(block);
189 return superBlock->GetFirstINodeBlockNumber();
190 }
191 public override INodeKey GetRootDirINodeKey() const
192 {
193 Block* block = blockManager.ReadBlock(BlockKey(0, 0), null);
194 BlockPutter putter(block);
195 SuperBlock* superBlock = cast<SuperBlock*>(block);
196 return INodeKey(superBlock->GetRootDirINodeNumber(), 0);
197 }
198 public override HostFile* GetHostFile(int fsNumber) const
199 {
200 int hostFileIndex = GetHostFileIndex(fsNumber);
201 if (hostFileIndex != 0)
202 {
203 throw SystemError(EINVAL, "invalid file system number");
204 }
205 return hostFile;
206 }
207 public override int LastBlockNumber() const
208 {
209 Block* block = blockManager.ReadBlock(BlockKey(0, 0), null);
210 BlockPutter putter(block);
211 SuperBlock* superBlock = cast<SuperBlock*>(block);
212 return superBlock->GetLastBlockNumber();
213 }
214 public override void SetLastBlockNumber(int blockNumber, SuperBlock* sb)
215 {
216 if (sb != null && sb->Key().fsNumber == 0)
217 {
218 if (sb->GetLastBlockNumber() < blockNumber)
219 {
220 sb->SetLastBlockNumber(blockNumber);
221 }
222 }
223 else
224 {
225 Block* block = blockManager.ReadBlock(BlockKey(0, 0), null);
226 BlockPutter putter(block);
227 SuperBlock* superBlock = cast<SuperBlock*>(block);
228 if (superBlock->GetLastBlockNumber() < blockNumber)
229 {
230 superBlock->SetLastBlockNumber(blockNumber);
231 }
232 }
233 }
234 private void CreateRootFs(int maxFiles, int maxBlocks)
235 {
236 try
237 {
238 int numINodeBitmapBlocks = (maxFiles - 1) / (8 * blockSize) + 1;
239 int numBlockBitmapBlocks = (maxBlocks - 1) / (8 * blockSize) + 1;
240 int numINodeBlocks = (maxFiles - 1) / numINodesInBlock + 1;
241 {
242 Block* block = blockManager.GetBlock(BlockKey(0, 0), null);
243 block->Clear();
244 BlockPutter putter(block);
245 SuperBlock* superBlock = cast<SuperBlock*>(block);
246 superBlock->SetLastBlockNumber(-1);
247 superBlock->SetFirstINodeBitmapBlockNumber(1);
248 superBlock->SetNumINodeBitmapBlocks(numINodeBitmapBlocks);
249 superBlock->SetFirstBlockBitmapBlockNumber(superBlock->GetFirstINodeBitmapBlockNumber() + superBlock->GetNumINodeBitmapBlocks());
250 superBlock->SetNumBlockBitmapBlocks(numBlockBitmapBlocks);
251 superBlock->SetFirstINodeBlockNumber(superBlock->GetFirstBlockBitmapBlockNumber() + superBlock->GetNumBlockBitmapBlocks());
252 superBlock->SetNumINodeBlocks(numINodeBlocks);
253 superBlock->SetFirstDataBlockNumber(superBlock->GetFirstINodeBlockNumber() + superBlock->GetNumINodeBlocks());
254 blockManager.WriteBlock(block, superBlock);
255 superBlock->SetFlag(Block.Flags.valid);
256 }
257 blockBitmap.Reset(new BlockBitmap(0));
258 inodeBitmap.Reset(new INodeBitmap(0));
259 int rootDirINodeNumber = GetFreeINodeNumber();
260 {
261 Block* block = blockManager.ReadBlock(BlockKey(0, 0), null);
262 BlockPutter putter(block);
263 SuperBlock* superBlock = cast<SuperBlock*>(block);
264 superBlock->SetRootDirINodeNumber(rootDirINodeNumber);
265 blockManager.WriteBlock(block, superBlock);
266 }
267 blockManager.Flush();
268 FileSystem* fs = GetMountTable().GetFileSystem(0);
269 fs->MakeDirectory(null, "/", cast<int>(ParseOctal("755")));
270 blockManager.Flush();
271 }
272 catch (const Exception& ex)
273 {
274 GetDiskDriver().Stop();
275 GetConsoleDriver().Stop();
276 throw;
277 }
278 }
279 public override nothrow bool HasMountDirKey(const INodeKey& mountDirKey) const
280 {
281 return false;
282 }
283 public override nothrow BlockManager* GetBlockManager()
284 {
285 return &blockManager;
286 }
287 public override nothrow INodeManager* GetINodeManager()
288 {
289 return &inodeManager;
290 }
291 private UniquePtr<BlockBitmap> blockBitmap;
292 private UniquePtr<INodeBitmap> inodeBitmap;
293 private string hostFilePath;
294 private HostFile* hostFile;
295 private RootBlockManager blockManager;
296 private RootINodeManager inodeManager;
297 }
298 }