1 using System;
2 using System.IO;
3 using System.Collections;
4 using cmsx.util;
5
6 namespace cmsx.kernel
7 {
8 public class INodeBitmap
9 {
10 public INodeBitmap(int fsNumber_) : fsNumber(fsNumber_)
11 {
12 FileSystem* fs = GetMountTable().GetFileSystem(fsNumber);
13 Block* sb = fs->GetBlockManager()->ReadBlock(BlockKey(0, fsNumber), null);
14 BlockPutter superBlockPutter(sb);
15 SuperBlock* superBlock = cast<SuperBlock*>(sb);
16 int firstNumber = superBlock->GetFirstINodeBitmapBlockNumber();
17 int bitmapBlockIndex = 0;
18 Block* block = null;
19 if (Log())
20 {
21 LogMessage("fs.ibm.init", ToString(firstNumber + bitmapBlockIndex));
22 }
23 if (superBlock->GetLastBlockNumber() < firstNumber + bitmapBlockIndex)
24 {
25 block = fs->GetBlockManager()->GetBlock(BlockKey(firstNumber + bitmapBlockIndex, superBlock->Key().fsNumber), superBlock);
26 block->Clear();
27 }
28 else
29 {
30 block = fs->GetBlockManager()->ReadBlock(BlockKey(firstNumber + bitmapBlockIndex, superBlock->Key().fsNumber), superBlock);
31 }
32 BlockPutter putter(block);
33 BitmapBlock* bitmapBlock = cast<BitmapBlock*>(block);
34 bitmapBlock->SetBit(0);
35 fs->GetBlockManager()->WriteBlock(block, superBlock);
36 }
37 public int GetFreeINodeNumber()
38 {
39 if (Log())
40 {
41 LogMessage("fs.ibm.getfreeinodenumber", "begin");
42 }
43 FileSystem* fs = GetMountTable().GetFileSystem(fsNumber);
44 Block* sb = fs->GetBlockManager()->ReadBlock(BlockKey(0, fsNumber), null);
45 BlockPutter superBlockPutter(sb);
46 SuperBlock* superBlock = cast<SuperBlock*>(sb);
47 int firstNumber = superBlock->GetFirstINodeBitmapBlockNumber();
48 int n = superBlock->GetNumINodeBitmapBlocks();
49 for (int i = 0; i < n; ++i;)
50 {
51 Block* block = null;
52 if (superBlock->GetLastBlockNumber() < firstNumber + i)
53 {
54 block = fs->GetBlockManager()->GetBlock(BlockKey(firstNumber + i, superBlock->Key().fsNumber), superBlock);
55 block->Clear();
56 }
57 else
58 {
59 block = fs->GetBlockManager()->ReadBlock(BlockKey(firstNumber + i, superBlock->Key().fsNumber), superBlock);
60 }
61 BlockPutter putter(block);
62 BitmapBlock* bitmapBlock = cast<BitmapBlock*>(block);
63 int index = bitmapBlock->GetFirstZeroBitIndex();
64 if (index != -1)
65 {
66 bitmapBlock->SetBit(index);
67 int inodeNumber = numBitsInBitmapBlock * i + index;
68 if (Log())
69 {
70 LogMessage("fs.ibm.getfreeinodenumber", "end.inode=" + ToString(inodeNumber));
71 }
72 fs->GetBlockManager()->WriteBlock(block, superBlock);
73 return inodeNumber;
74 }
75 }
76 throw SystemError(ERLIMITEXCEEDED, "no free inodes");
77 }
78 public void SetFreeINodeNumber(int inodeNumber)
79 {
80 if (Log())
81 {
82 LogMessage("fs.ibm.setfreeinodenumber", "begin.inode=" + ToString(inodeNumber));
83 }
84 FileSystem* fs = GetMountTable().GetFileSystem(fsNumber);
85 Block* sb = fs->GetBlockManager()->ReadBlock(BlockKey(0, fsNumber), null);
86 BlockPutter superBlockPutter(sb);
87 SuperBlock* superBlock = cast<SuperBlock*>(sb);
88 int blockNumber = inodeNumber / numBitsInBitmapBlock;
89 int index = inodeNumber % numBitsInBitmapBlock;
90 int firstNumber = superBlock->GetFirstINodeBitmapBlockNumber();
91 Block* block = null;
92 if (superBlock->GetLastBlockNumber() < firstNumber + blockNumber)
93 {
94 block = fs->GetBlockManager()->GetBlock(BlockKey(firstNumber + blockNumber, superBlock->Key().fsNumber), superBlock);
95 block->Clear();
96 }
97 else
98 {
99 block = fs->GetBlockManager()->ReadBlock(BlockKey(firstNumber + blockNumber, superBlock->Key().fsNumber), superBlock);
100 }
101 {
102 BlockPutter putter(block);
103 BitmapBlock* bitmapBlock = cast<BitmapBlock*>(block);
104 bitmapBlock->ResetBit(index);
105 fs->GetBlockManager()->WriteBlock(block, superBlock);
106 }
107 if (Log())
108 {
109 LogMessage("fs.ibm.setfreeinodenumber", "end.inode=" + ToString(inodeNumber));
110 }
111 }
112 private int fsNumber;
113 }
114
115 public class RootINodeManager : INodeManager
116 {
117 public RootINodeManager()
118 {
119 for (long i = 0; i < maxCachedINodes; ++i;)
120 {
121 INode* inode = new INode(this);
122 inodes.Add(UniquePtr<INode>(inode));
123 freeINodeList.Add(inode);
124 inode->SetIterator(LinkedList<INode*>.Iterator(&freeINodeList, freeINodeList.Tail()));
125 }
126 }
127 public override nothrow string Name() const
128 {
129 return "fs.root.imgr";
130 }
131 public override INode* GetINode(const INodeKey& key)
132 {
133 if (Log())
134 {
135 LogMessage("fs.root.imgr.getinode.begin", key.ToString());
136 }
137 FileSystem* fs = GetMountTable().GetFileSystem(0);
138 while (true)
139 {
140 HashMap<INodeKey, INode*, INodeKeyHash>.ConstIterator it = inodeMap.CFind(key);
141 if (it != inodeMap.CEnd())
142 {
143 INode* inode = it->second;
144 if (inode->GetFlag(INode.Flags.locked))
145 {
146 void* fiberData = OsGetFiberData();
147 Process* process = cast<Process*>(fiberData);
148 if (inode->Owner() != process)
149 {
150 inode->AddWaitingProcess(process);
151 SleepProcess(process, inodeUnlockedEvent, cast<ulong>(cast<void*>(inode)), 0u);
152 continue;
153 }
154 else
155 {
156 inode->SetUseCount(inode->GetUseCount() + 1);
157 if (Log())
158 {
159 LogMessage("fs.root.imgr.getinode.end", inode->ToString());
160 }
161 return inode;
162 }
163 }
164 else
165 {
166 inode->SetFlag(INode.Flags.locked);
167 void* fiberData = OsGetFiberData();
168 Process* process = cast<Process*>(fiberData);
169 inode->SetOwner(process);
170 RemoveINodeFromFreeList(inode);
171 inode->SetUseCount(inode->GetUseCount() + 1);
172 if (Log())
173 {
174 LogMessage("fs.root.imgr.getinode.end", inode->ToString());
175 }
176 return inode;
177 }
178 }
179 else
180 {
181 if (freeINodeList.IsEmpty())
182 {
183 throw SystemError(ERLIMITEXCEEDED, "no free inodes in inode cache");
184 }
185 INode* inode = freeINodeList.Front();
186 void* fiberData = OsGetFiberData();
187 Process* process = cast<Process*>(fiberData);
188 inode->SetOwner(process);
189 freeINodeList.RemoveFirst();
190 inode->SetIterator(freeINodeList.End());
191 inodeMap.Remove(inode->Key());
192 inode->SetKey(key);
193 inodeMap[key] = inode;
194 inode->SetFlag(INode.Flags.locked);
195 FileSystem* fileSystem = GetMountTable().GetFileSystem(key.fsNumber);
196 int inodeBlockNumber = GetINodeBlockNumber(key.inodeNumber, fileSystem->GetFirstINodeBlockNumber());
197 Block* block = null;
198 if (fileSystem->LastBlockNumber() < inodeBlockNumber)
199 {
200 block = fs->GetBlockManager()->GetBlock(BlockKey(inodeBlockNumber, key.fsNumber), null);
201 block->Clear();
202 }
203 else
204 {
205 block = fs->GetBlockManager()->ReadBlock(BlockKey(inodeBlockNumber, key.fsNumber), null);
206 }
207 BlockPutter putter(block);
208 INodeBlock* inodeBlock = cast<INodeBlock*>(block);
209 INode source = inodeBlock->GetINode(GetINodeIndex(key.inodeNumber));
210 UniquePtr<byte> mem(cast<byte*>(MemAlloc(inodeSize)));
211 MemoryWriter writer(mem.Get(), inodeSize);
212 source.Write(writer);
213 MemoryReader reader(mem.Get(), inodeSize);
214 inode->ResetFlags();
215 inode->Read(reader);
216 inode->SetFlag(INode.Flags.locked);
217 inode->SetUseCount(1);
218 if (inode->GetNumLinks() == 0)
219 {
220 inode->SetNumLinks(1);
221 }
222 if (Log())
223 {
224 LogMessage("fs.root.imgr.getinode.end", inode->ToString());
225 }
226 return inode;
227 }
228 }
229 }
230 public override void PutINode(INode* inode)
231 {
232 if (Log())
233 {
234 LogMessage("fs.root.imgr.putinode.begin", inode->ToString());
235 }
236 FileSystem* fs = GetMountTable().GetFileSystem(0);
237 inode->SetFlag(INode.Flags.locked);
238 inode->SetUseCount(inode->GetUseCount() - 1);
239 if (inode->GetUseCount() == 0)
240 {
241 if (Log())
242 {
243 LogMessage("fs.root.imgr.putinode.count=0", inode->ToString());
244 }
245 if (inode->GetNumLinks() == 0u)
246 {
247 if (Log())
248 {
249 LogMessage("fs.root.imgr.putinode.nlinks=0", inode->ToString());
250 }
251 fs->GetBlockManager()->FreeBlocks(inode);
252 inode->SetType(FileType.free);
253 inodeMap.Remove(inode->Key());
254 FileSystem* fsi = GetMountTable().GetFileSystem(inode->Key().fsNumber);
255 fsi->SetFreeINodeNumber(inode->Key().inodeNumber);
256 }
257 if (inode->GetFlag(INode.Flags.dirty))
258 {
259 FileSystem* fileSystem = GetMountTable().GetFileSystem(inode->Key().fsNumber);
260 int inodeBlockNumber = GetINodeBlockNumber(inode->Key().inodeNumber, fileSystem->GetFirstINodeBlockNumber());
261 Block* block = null;
262 if (fileSystem->LastBlockNumber() < inodeBlockNumber)
263 {
264 block = fileSystem->GetBlockManager()->GetBlock(BlockKey(inodeBlockNumber, inode->Key().fsNumber), null);
265 block->Clear();
266 }
267 else
268 {
269 block = fileSystem->GetBlockManager()->ReadBlock(BlockKey(inodeBlockNumber, inode->Key().fsNumber), null);
270 }
271 BlockPutter putter(block);
272 INodeBlock* inodeBlock = cast<INodeBlock*>(block);
273 inodeBlock->SetINode(GetINodeIndex(inode->Key().inodeNumber), *inode);
274 block->SetFlag(Block.Flags.dirty);
275 fileSystem->GetBlockManager()->WriteBlock(block, null);
276 inode->ResetFlag(INode.Flags.dirty);
277 }
278 freeINodeList.Add(inode);
279 inode->SetIterator(LinkedList<INode*>.Iterator(&freeINodeList, freeINodeList.Tail()));
280 inode->ResetOwner();
281 }
282 inode->ResetFlag(INode.Flags.locked);
283 Kernel& kernel = GetKernel();
284 ProcessTable& processTable = kernel.GetProcessTable();
285 List<Process*> waitingProcesses = inode->GetWaitingProcesses();
286 for (Process* process : waitingProcesses)
287 {
288 WakeUpProcess(processTable, process);
289 }
290 if (Log())
291 {
292 LogMessage("fs.root.imgr.putinode.end", inode->ToString());
293 }
294 }
295 public override INode* PathToINode(Process* process, const string& path)
296 {
297 DirectorySlot freeDirectorySlot;
298 INode* parent = null;
299 string name;
300 return PathToINode(process, path, PathToINodeFlags.none, parent, freeDirectorySlot, name);
301 }
302 public override INode* PathToINode(Process* process, const string& path, PathToINodeFlags flags, INode*& parent, DirectorySlot& freeDirectorySlot, string& name)
303 {
304 if (Log())
305 {
306 LogMessage("fs.root.imgr.pathtoinode.begin", path);
307 }
308 bool createEntry = (flags & PathToINodeFlags.createEntry) != PathToINodeFlags.none;
309 bool ignoreMountPoint = (flags & PathToINodeFlags.ignoreMountPoint) != PathToINodeFlags.none;
310 FileSystem* fs = GetMountTable().GetFileSystem(0);
311 if (createEntry)
312 {
313 DirectorySlot slot;
314 freeDirectorySlot = slot;
315 name.Clear();
316 }
317 INode* inode = null;
318 if (path.IsEmpty() || path[0] != '/')
319 {
320 if (process != null)
321 {
322 inode = GetINode(process->workingDirINodeKey);
323 }
324 else
325 {
326 inode = GetINode(fs->GetRootDirINodeKey());
327 }
328 }
329 else
330 {
331 if (process != null)
332 {
333 inode = GetINode(process->rootDirINodeKey);
334 }
335 else
336 {
337 inode = GetINode(fs->GetRootDirINodeKey());
338 }
339 }
340 if (!createEntry && path == "/")
341 {
342 return inode;
343 }
344 INodePutter inodePutter(inode);
345 List<string> pathComponents = path.Split('/');
346 long pn = pathComponents.Count();
347 if (pn < 2 && createEntry)
348 {
349 parent = inode;
350 inodePutter.ResetINode();
351 }
352 string pc;
353 for (long pi = 0; pi < pn; ++pi;)
354 {
355 const string& pathComponent = pathComponents[pi];
356 if (pathComponent.IsEmpty())
357 {
358 pc = "/";
359 if (!createEntry)
360 {
361 continue;
362 }
363 }
364 else
365 {
366 pc = Path.Combine(pc, pathComponent);
367 }
368 if (Log())
369 {
370 LogMessage("fs.root.imgr.pathtoinode.pc", pc);
371 }
372 if (createEntry)
373 {
374 if (pi == pn - 1)
375 {
376 name = pathComponent;
377 }
378 else if (pathComponent.IsEmpty())
379 {
380 if (pi == pn - 2)
381 {
382 parent = inode;
383 inodePutter.ResetINode();
384 }
385 continue;
386 }
387 }
388 if (inode->Type() == FileType.directory)
389 {
390 if (process != null)
391 {
392 try
393 {
394 inode->CheckPermissions(process->uid, process->gid, Access.execute);
395 }
396 catch (const Exception& ex)
397 {
398 throw SystemError(EPERM, "cannot browse directory '" + pc + "' using uid " + ToString(process->uid) + ": " + ex.Message());
399 }
400 if (inode->Key() == process->rootDirINodeKey && pathComponent == "..") continue;
401 if (pathComponent == ".") continue;
402 }
403 else
404 {
405 if (inode->Key() == fs->GetRootDirINodeKey() && pathComponent == "..") continue;
406 if (pathComponent == ".") continue;
407 }
408 long offset = 0;
409 long directorySize = inode->GetFileSize();
410 bool found = false;
411 while (offset < directorySize && !found)
412 {
413 int blockNumber = 0;
414 int blockOffset = 0;
415 fs->GetBlockManager()->GetBlockNumber(inode, offset, blockNumber, blockOffset, false);
416 if (blockNumber == 0)
417 {
418 return null;
419 }
420 Block* block = fs->GetBlockManager()->ReadBlock(BlockKey(blockNumber, inode->Key().fsNumber), null);
421 BlockPutter blockPutter(block);
422 DirectoryBlock* directoryBlock = cast<DirectoryBlock*>(block);
423 for (int i = 0; i < numDirectoryEntriesInBlock; ++i;)
424 {
425 DirectoryEntry entry = directoryBlock->GetDirectoryEntry(i);
426 if (entry.inodeNumber == 0)
427 {
428 if (createEntry && pi == pn - 1 && freeDirectorySlot.offset == -1)
429 {
430 DirectorySlot slot(blockNumber, inode->Key().fsNumber, offset);
431 freeDirectorySlot = slot;
432 }
433 offset = offset + directoryEntrySize;
434 continue;
435 }
436 if (pathComponent == entry.name)
437 {
438 inode = GetINode(INodeKey(entry.inodeNumber, inode->Key().fsNumber));
439 if (!ignoreMountPoint && inode->GetFlag(INode.Flags.mountPoint))
440 {
441 inodePutter.ResetINode(inode);
442 FileSystem* mountedFileSystem = GetMountTable().GetMountedFileSystem(pc, inode->Key());
443 if (createEntry && mountedFileSystem->IsReadOnly())
444 {
445 throw SystemError(EFAIL, "cannot create entry: mounted file system '" + mountedFileSystem->Name() + "' is read-only");
446 }
447 string subPath;
448 if (pi < pn - 1)
449 {
450 subPath = pathComponents[pi + 1];
451 for (long spi = pi + 2; spi < pn; ++spi;)
452 {
453 subPath = Path.Combine(subPath, pathComponents[spi]);
454 }
455 }
456 INodeManager* imgr = mountedFileSystem->GetINodeManager();
457 return CallPathToINode(imgr, process, subPath);
458 }
459 if (createEntry && pi == pn - 2)
460 {
461 parent = inode;
462 }
463 else
464 {
465 inodePutter.ResetINode(inode);
466 }
467 found = true;
468 break;
469 }
470 offset = offset + directoryEntrySize;
471 }
472 }
473 if (!found)
474 {
475 if (pi == pn - 1)
476 {
477 if (createEntry && freeDirectorySlot.offset == -1)
478 {
479 DirectorySlot slot(invalidBlockNumber, inode->Key().fsNumber, offset);
480 freeDirectorySlot = slot;
481 }
482 return null;
483 }
484 else if (pi < pn - 1 && process != null)
485 {
486 throw SystemError(ENOENT, "directory '" + pc + "' not found");
487 }
488 }
489 }
490 else
491 {
492 if (process != null)
493 {
494 throw SystemError(EINVAL, "path component '" + pathComponent + "' does not denote a directory");
495 }
496 }
497 }
498 inodePutter.ResetINode();
499 if (Log())
500 {
501 LogMessage("fs.root.imgr.pathtoinode.end", inode->ToString());
502 }
503 return inode;
504 }
505 private void RemoveINodeFromFreeList(INode* inode)
506 {
507 LinkedList<INode*>.Iterator iterator = inode->GetIterator();
508 if (iterator != freeINodeList.End())
509 {
510 freeINodeList.Remove(iterator);
511 inode->SetIterator(freeINodeList.End());
512 }
513 }
514 private HashMap<INodeKey, INode*, INodeKeyHash> inodeMap;
515 private LinkedList<INode*> freeINodeList;
516 private List<UniquePtr<INode>> inodes;
517 }
518 }