The Pedigree Project  0.1
Ext2Filesystem.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "Ext2Filesystem.h"
21 #include "Ext2Directory.h"
22 #include "Ext2File.h"
23 #include "Ext2Node.h"
24 #include "Ext2Symlink.h"
25 #include "ext2.h"
26 #include "modules/system/users/Group.h"
27 #include "modules/system/users/User.h"
28 #include "modules/system/vfs/File.h"
29 #include "modules/system/vfs/VFS.h"
30 #include "pedigree/kernel/Log.h"
31 #include "pedigree/kernel/compiler.h"
32 #include "pedigree/kernel/machine/Disk.h"
33 #include "pedigree/kernel/machine/Machine.h"
34 #include "pedigree/kernel/machine/Timer.h"
35 #include "pedigree/kernel/process/Process.h"
36 #include "pedigree/kernel/process/Thread.h"
37 #include "pedigree/kernel/processor/Processor.h"
38 #include "pedigree/kernel/processor/ProcessorInformation.h"
39 #include "pedigree/kernel/syscallError.h"
40 #include "pedigree/kernel/utilities/StaticString.h"
41 #include "pedigree/kernel/utilities/Vector.h"
42 #include "pedigree/kernel/utilities/assert.h"
43 #include "pedigree/kernel/utilities/utility.h"
44 
45 #ifndef EXT2_STANDALONE
46 #include "modules/Module.h"
47 #endif
48 
49 // The sparse block page. This is zeroed and made read-only. A handler is set
50 // and if written, it traps.
53 static uint8_t g_pSparseBlock[4096] ALIGN(4096) SECTION(".bss");
54 
55 #ifdef EXT2_STANDALONE
56 extern uint32_t getUnixTimestamp();
57 #else
58 static uint32_t getUnixTimestamp()
59 {
60  Timer *pTimer = Machine::instance().getTimer();
61  return pTimer->getUnixTimestamp();
62 }
63 #endif
64 
65 Ext2Filesystem::Ext2Filesystem()
66  : m_pSuperblock(0), m_pGroupDescriptors(0), m_pInodeTables(0),
67  m_pInodeBitmaps(0), m_pBlockBitmaps(0), m_BlockSize(0), m_InodeSize(0),
68  m_nGroupDescriptors(0),
69 #ifdef THREADS
70  m_WriteLock(false),
71 #endif
72  m_pRoot(0)
73 {
74 }
75 
76 Ext2Filesystem::~Ext2Filesystem()
77 {
78  delete[] m_pBlockBitmaps;
79  delete[] m_pInodeBitmaps;
80  delete[] m_pInodeTables;
81  delete[] m_pGroupDescriptors;
82  delete m_pRoot;
83 }
84 
86 {
87  String devName;
88  m_pDisk = pDisk;
89  pDisk->getName(devName);
90 
91  // Attempt to read the superblock.
92  // We need to pin the block, as we'll hold onto it.
93  uintptr_t block = m_pDisk->read(1024ULL);
94  if (!block || block == ~static_cast<uintptr_t>(0U))
95  {
96  return false;
97  }
98  m_pDisk->pin(1024ULL);
99  m_pSuperblock = reinterpret_cast<Superblock *>(block);
100 
101  // Read correctly?
102  if (LITTLE_TO_HOST16(m_pSuperblock->s_magic) != 0xEF53)
103  {
104  ERROR("Ext2: Superblock not found on device " << devName);
105  m_pDisk->unpin(1024ULL);
106  return false;
107  }
108 
109  // Clean?
110  if (LITTLE_TO_HOST16(m_pSuperblock->s_state) != EXT2_STATE_CLEAN)
111  {
112  WARNING("Ext2: filesystem on device " << devName << " is not clean.");
113  }
114 
115  // Compressed filesystem?
116  if (checkRequiredFeature(1))
117  {
118  WARNING(
119  "Ext2: filesystem on device "
120  << devName
121  << " requires compression, some files may fail to read.");
122 
123  // Compression type.
124  uint32_t algo_bitmap = LITTLE_TO_HOST32(m_pSuperblock->s_algo_bitmap);
125  switch (algo_bitmap)
126  {
127  case EXT2_LZV1_ALG:
128  NOTICE(
129  "Ext2: filesystem on device '"
130  << devName << "' uses compression algorithm LZV1.");
131  break;
132  case EXT2_LZRW3A_ALG:
133  NOTICE(
134  "Ext2: filesystem on device '"
135  << devName << "' uses compression algorithm LZRW3A.");
136  break;
137  case EXT2_GZIP_ALG:
138  NOTICE(
139  "Ext2: filesystem on device '"
140  << devName << "' uses compression algorithm gzip.");
141  break;
142  case EXT2_BZIP2_ALG:
143  NOTICE(
144  "Ext2: filesystem on device '"
145  << devName << "' uses compression algorithm bzip2.");
146  break;
147  case EXT2_LZO_ALG:
148  NOTICE(
149  "Ext2: filesystem on device '"
150  << devName << "' uses compression algorithm LZO.");
151  break;
152  default:
153  ERROR(
154  "Ext2: unknown compression algorithm "
155  << algo_bitmap << " on device '" << devName
156  << "' -- cannot mount!");
157  return false;
158  }
159  }
160 
163 
164  // If we can, check extended superblock fields.
165  if (LITTLE_TO_HOST32(m_pSuperblock->s_rev_level) >= 1)
166  {
167  // Non-standard inode sizes are permitted, handle that.
168  m_InodeSize = LITTLE_TO_HOST16(m_pSuperblock->s_inode_size);
169  }
170  else
171  {
172  m_InodeSize = sizeof(Inode);
173  }
174 
175  // Calculate the block size.
176  m_BlockSize = 1024 << LITTLE_TO_HOST32(m_pSuperblock->s_log_block_size);
177 
178  // More than 4096 bytes per block and we're a little screwed atm.
179  assert(m_BlockSize <= 4096);
180 
181  // Where is the group descriptor table?
182  uint32_t gdBlock = LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
183 
184  // How many group descriptors do we have? Round up the result.
185  uint32_t inodeCount = LITTLE_TO_HOST32(m_pSuperblock->s_inodes_count);
186  uint32_t inodesPerGroup =
187  LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
188  m_nGroupDescriptors =
189  (inodeCount / inodesPerGroup) + (inodeCount % inodesPerGroup);
190 
191  // Add an entry to the group descriptor tree for each GD.
192  m_pGroupDescriptors = new GroupDesc *[m_nGroupDescriptors];
193  for (size_t i = 0; i < m_nGroupDescriptors; i++)
194  {
195  uintptr_t idx = (i * sizeof(GroupDesc)) / m_BlockSize;
196  uintptr_t off = (i * sizeof(GroupDesc)) % m_BlockSize;
197 
198  uintptr_t groupBlock = readBlock(gdBlock + idx);
199  m_pGroupDescriptors[i] =
200  reinterpret_cast<GroupDesc *>(groupBlock + off);
201  }
202 
203  // Create our bitmap arrays and tables.
204  m_pInodeTables = new Vector<size_t>[m_nGroupDescriptors];
205  m_pInodeBitmaps = new Vector<size_t>[m_nGroupDescriptors];
206  m_pBlockBitmaps = new Vector<size_t>[m_nGroupDescriptors];
207 
209 
210  // load root directory
211  Inode *inode = getInode(EXT2_ROOT_INO);
212  m_pRoot = new Ext2Directory(String(""), EXT2_ROOT_INO, inode, this, 0);
213 
214  // cache volume label
215  bool hasVolumeLabel = LITTLE_TO_HOST32(m_pSuperblock->s_rev_level) >= 1;
216  if ((!hasVolumeLabel) || (m_pSuperblock->s_volume_name[0] == '\0'))
217  {
218  NormalStaticString str;
219  str += "no-volume-label@";
220  str.append(reinterpret_cast<uintptr_t>(this), 16);
221  m_VolumeLabel.assign(str, str.length(), true);
222  }
223  else
224  {
225  char buffer[17];
226  StringCopyN(buffer, m_pSuperblock->s_volume_name, 16);
227  buffer[16] = '\0';
228  m_VolumeLabel.assign(buffer);
229  }
230 
231  return true;
232 }
233 
234 Filesystem *Ext2Filesystem::probe(Disk *pDisk)
235 {
236  Ext2Filesystem *pFs = new Ext2Filesystem();
237  if (!pFs->initialise(pDisk))
238  {
239  // No ext2 filesystem found - don't leak the filesystem object.
240  delete pFs;
241  return 0;
242  }
243  else
244  return pFs;
245 }
246 
248 {
249  return m_pRoot;
250 }
251 
253 {
254  return m_VolumeLabel;
255 }
256 
258  File *parent, const String &filename, uint32_t mask, const String &value,
259  size_t type, uint32_t inodeOverride)
260 {
261  NOTICE("CREATE: " << filename);
262 
263  // Quick sanity check;
264  if (!parent->isDirectory())
265  {
266  SYSCALL_ERROR(NotADirectory);
267  return false;
268  }
269 
270  // The filename cannot be the special entries "." or "..".
271  if (filename.length() == 0 || !StringCompare(filename, ".") ||
272  !StringCompare(filename, ".."))
273  {
274  SYSCALL_ERROR(InvalidArgument);
275  return false;
276  }
277 
278  // Find a free inode.
279  uint32_t inode_num = inodeOverride;
280  if (!inode_num)
281  {
282  inode_num = findFreeInode();
283  if (inode_num == 0)
284  {
285  SYSCALL_ERROR(NoSpaceLeftOnDevice);
286  return false;
287  }
288  }
289 
290 #ifdef EXT2_STANDALONE
291  size_t uid = 0;
292  size_t gid = 0;
293 #else
294  size_t uid = Processor::information()
295  .getCurrentThread()
296  ->getParent()
297  ->getUser()
298  ->getId();
299  size_t gid = Processor::information()
300  .getCurrentThread()
301  ->getParent()
302  ->getGroup()
303  ->getId();
304 #endif
305 
306  uint32_t timestamp = getUnixTimestamp();
307 
308  // Populate the inode.
310  Inode *newInode = getInode(inode_num);
311  if (!inodeOverride)
312  {
313  ByteSet(reinterpret_cast<uint8_t *>(newInode), 0, m_InodeSize);
314  newInode->i_mode = HOST_TO_LITTLE16(mask | type);
315  newInode->i_uid = HOST_TO_LITTLE16(uid);
316  newInode->i_atime = newInode->i_ctime = newInode->i_mtime =
317  HOST_TO_LITTLE32(timestamp);
318  newInode->i_gid = HOST_TO_LITTLE16(gid);
319  }
320 
321  // If we have a value to store, and it's small enough, use the block
322  // indices.
323  if (value.length() && value.length() < 4 * 15)
324  {
325  MemoryCopy(
326  reinterpret_cast<void *>(newInode->i_block), value, value.length());
327  newInode->i_size = HOST_TO_LITTLE32(value.length());
328  }
329  // Else case comes later, after pFile is created.
330 
331  Ext2Directory *pE2Parent = reinterpret_cast<Ext2Directory *>(parent);
332  Ext2Node *pNewNode = 0;
333 
334  // Create the new File object.
335  File *pFile = 0;
336  switch (type)
337  {
338  case EXT2_S_IFREG:
339  {
340  Ext2File *pNewFile =
341  new Ext2File(filename, inode_num, newInode, this, parent);
342  pFile = pNewFile;
343  pNewNode = pNewFile;
344  break;
345  }
346  case EXT2_S_IFDIR:
347  {
348  Ext2Directory *pE2Dir =
349  new Ext2Directory(filename, inode_num, newInode, this, parent);
350  pFile = pE2Dir;
351  pNewNode = pE2Dir;
352 
353  // If we already have an inode, assume we already have dot/dotdot
354  // entries and so don't need to make them.
355  if (!inodeOverride)
356  {
357  Inode *parentInode = getInode(pE2Parent->getInodeNumber());
358 
359  // Create dot and dotdot entries.
360  Ext2Directory *pDot = new Ext2Directory(
361  String("."), inode_num, newInode, this, pE2Dir);
362  Ext2Directory *pDotDot = new Ext2Directory(
363  String(".."), pE2Parent->getInodeNumber(), parentInode,
364  this, pE2Dir);
365 
366  // Add created dot/dotdot entries to the new directory.
367  pE2Dir->addEntry(String("."), pDot, EXT2_S_IFDIR);
368  pE2Dir->addEntry(String(".."), pDotDot, EXT2_S_IFDIR);
369  }
370  break;
371  }
372  case EXT2_S_IFLNK:
373  {
374  Ext2Symlink *pNewSymlink =
375  new Ext2Symlink(filename, inode_num, newInode, this, parent);
376  pFile = pNewSymlink;
377  pNewNode = pNewSymlink;
378  break;
379  }
380  default:
381  FATAL("EXT2: Unrecognised file type: " << Hex << type);
382  break;
383  }
384 
385  // Else case from earlier.
386  if (value.length() && value.length() >= 4 * 15)
387  {
388  const char *pStr = value;
389  pFile->write(0ULL, value.length(), reinterpret_cast<uintptr_t>(pStr));
390  }
391 
392  // Add to the parent directory.
393  if (!pE2Parent->addEntry(filename, pFile, type))
394  {
395  ERROR("EXT2: Internal error adding directory entry.");
396  SYSCALL_ERROR(IoError);
397  return false;
398  }
399 
400  // Edit the atime and mtime of the parent directory.
401  parent->setAccessedTime(timestamp);
402  parent->setModifiedTime(timestamp);
403 
404  // Write updated inodes.
405  writeInode(inode_num);
406  writeInode(pE2Parent->getInodeNumber());
407 
408  // Update directory count in the group descriptor.
409  if (type == EXT2_S_IFDIR)
410  {
411  uint32_t group = (inode_num - 1) /
412  LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
413  GroupDesc *pDesc = m_pGroupDescriptors[group];
414 
415  pDesc->bg_used_dirs_count++;
416 
417  // Update group descriptor on disk.
419  uint32_t gdBlock =
420  LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
421  uint32_t groupBlock = (group * sizeof(GroupDesc)) / m_BlockSize;
422  writeBlock(gdBlock + groupBlock);
423  }
424 
425  // OK, now we can preallocate blocks if desired.
426  // Note: don't preallocate symlinks, which can store data in i_blocks.
427  if (m_pSuperblock->s_prealloc_blocks &&
428  !(pFile->isDirectory() || pFile->isSymlink()))
429  {
430  pNewNode->ensureLargeEnough(
431  m_pSuperblock->s_prealloc_blocks * m_BlockSize, 0, 0, true);
432  }
433  else if (m_pSuperblock->s_prealloc_dir_blocks && pFile->isDirectory())
434  {
435  pNewNode->ensureLargeEnough(
436  m_pSuperblock->s_prealloc_dir_blocks * m_BlockSize, 0, 0, true);
437  }
438 
439  return true;
440 }
441 
443  File *parent, const String &filename, uint32_t mask)
444 {
445  return createNode(parent, filename, mask, String(""), EXT2_S_IFREG);
446 }
447 
449  File *parent, const String &filename, uint32_t mask)
450 {
451  if (!createNode(parent, filename, mask, String(""), EXT2_S_IFDIR))
452  {
453  return false;
454  }
455  return true;
456 }
457 
459  File *parent, const String &filename, const String &value)
460 {
461  return createNode(parent, filename, 0777, value, EXT2_S_IFLNK);
462 }
463 
465  File *parent, const String &filename, File *target)
466 {
467  Ext2Directory *pE2Parent = reinterpret_cast<Ext2Directory *>(parent);
468 
469  Ext2Node *pNode = 0;
470  if (target->isDirectory())
471  {
472  Ext2Directory *pDirectory = static_cast<Ext2Directory *>(target);
473  pNode = pDirectory;
474  }
475  else if (target->isSymlink())
476  {
477  Ext2Symlink *pSymlink = static_cast<Ext2Symlink *>(target);
478  pNode = pSymlink;
479  }
480  else
481  {
482  Ext2File *pFile = static_cast<Ext2File *>(target);
483  pNode = pFile;
484  }
485 
486  if (!pNode)
487  {
488  return false;
489  }
490 
491  // Extract permissions and entry type.
492  Inode *inode = pNode->getInode();
493  uint32_t mask = LITTLE_TO_HOST16(inode->i_mode) & 0x0FFF;
494  size_t type = LITTLE_TO_HOST16(inode->i_mode) & 0xF000;
495 
496  return createNode(
497  parent, filename, mask, String(""), type, pNode->getInodeNumber());
498 }
499 
500 bool Ext2Filesystem::remove(File *parent, File *file)
501 {
502  // Quick sanity check.
503  if (!parent->isDirectory())
504  {
505  SYSCALL_ERROR(IoError);
506  return false;
507  }
508 
509  Ext2Node *pNode = 0;
510  String filename;
511  if (file->isDirectory())
512  {
513  Ext2Directory *pDirectory = static_cast<Ext2Directory *>(file);
514  pNode = pDirectory;
515  filename = pDirectory->getName();
516  }
517  else if (file->isSymlink())
518  {
519  Ext2Symlink *pSymlink = static_cast<Ext2Symlink *>(file);
520  pNode = pSymlink;
521  filename = pSymlink->getName();
522  }
523  else
524  {
525  Ext2File *pFile = static_cast<Ext2File *>(file);
526  pNode = pFile;
527  filename = pFile->getName();
528  }
529 
530  NOTICE("REMOVE: " << filename);
531 
532  Ext2Directory *pE2Parent = reinterpret_cast<Ext2Directory *>(parent);
533  bool result = pE2Parent->removeEntry(filename, pNode);
534 
535  // Update the group descriptor directory count to reflect the deletion.
536  if (result && file->isDirectory() && (filename != "." && filename != ".."))
537  {
538  uint32_t inode_num = pNode->getInodeNumber();
539 
540  uint32_t group = (inode_num - 1) /
541  LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
542  GroupDesc *pDesc = m_pGroupDescriptors[group];
543 
544  pDesc->bg_used_dirs_count--;
545 
546  // Update group descriptor on disk.
548  uint32_t gdBlock =
549  LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
550  uint32_t groupBlock = (group * sizeof(GroupDesc)) / m_BlockSize;
551  writeBlock(gdBlock + groupBlock);
552  }
553 
554  return result;
555 }
556 
557 uintptr_t Ext2Filesystem::readBlock(uint32_t block)
558 {
559  if (block == 0)
560  return reinterpret_cast<uintptr_t>(g_pSparseBlock);
561 
562  return m_pDisk->read(
563  static_cast<uint64_t>(m_BlockSize) * static_cast<uint64_t>(block));
564 }
565 
566 void Ext2Filesystem::writeBlock(uint32_t block)
567 {
568  if (block == 0)
569  return;
570 
571  m_pDisk->write(
572  static_cast<uint64_t>(m_BlockSize) * static_cast<uint64_t>(block));
573 }
574 
575 void Ext2Filesystem::pinBlock(uint64_t location)
576 {
577  m_pDisk->pin(static_cast<uint64_t>(m_BlockSize) * location);
578 }
579 
580 void Ext2Filesystem::unpinBlock(uint64_t location)
581 {
582  m_pDisk->unpin(static_cast<uint64_t>(m_BlockSize) * location);
583 }
584 
585 void Ext2Filesystem::sync(size_t offset, bool async)
586 {
587  if (async)
588  m_pDisk->write(static_cast<uint64_t>(m_BlockSize) * offset);
589  else
590  m_pDisk->flush(static_cast<uint64_t>(m_BlockSize) * offset);
591 }
592 
593 uint32_t Ext2Filesystem::findFreeBlock(uint32_t inode)
594 {
595  Vector<uint32_t> blocks;
596  if (findFreeBlocks(inode, 1, blocks))
597  {
598  return blocks[0];
599  }
600 
601  return 0;
602 }
603 
605  uint32_t inode, size_t count, Vector<uint32_t> &blocks)
606 {
607  // Inode zero is invalid, so make sure we are getting local blocks.
608  --inode;
609 
610  // Try to allocate near the inode's group (but we can fall back to a
611  // different group if needed).
612  uint32_t group =
613  inode / LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
614  uint32_t startGroup = group;
615 
616  for (; count && group < m_nGroupDescriptors; ++group)
617  {
618  count -= findFreeBlocksInGroup(group, count, blocks);
619  }
620 
621  // Try again from the start of the disk if we couldn't find a group (if
622  // we started e.g. halfway through the disk due to the inode closeness
623  // thing above, we need to check the rest of the groups).
624  if (count)
625  ERROR("FALLING BACK TO STARTING FROM ZERO");
626  for (group = 0; count && group < startGroup; ++group)
627  {
628  count -= findFreeBlocksInGroup(group, count, blocks);
629  }
630 
632  return count == 0;
633 }
634 
636  uint32_t group, size_t maxCount, Vector<uint32_t> &blocks)
637 {
638  if (!maxCount)
639  {
640  return 0;
641  }
642 
643  const uint32_t blocksPerGroup =
644  LITTLE_TO_HOST32(m_pSuperblock->s_blocks_per_group);
645  const size_t bitmapBlockBytes = m_BlockSize;
646  size_t currentCount = 0;
647 
648  // Any free blocks here?
649  GroupDesc *pDesc = m_pGroupDescriptors[group];
650  if (!pDesc->bg_free_blocks_count)
651  {
652  // No blocks free in this group.
653  return currentCount;
654  }
655 
656  ensureFreeBlockBitmapLoaded(group);
657 
658  // 8 blocks per byte - i == bitmap offset in bytes.
659  Vector<size_t> &list = m_pBlockBitmaps[group];
660  const uint32_t bytesToSearch = blocksPerGroup >> 3;
661  size_t idx = 0;
662 
663  // Block bitmap pointer.
664  typedef uint64_t searchType;
665  size_t base = list[idx];
666  searchType *ptr = reinterpret_cast<searchType *>(base);
667  searchType *ptr_end = adjust_pointer(ptr, bitmapBlockBytes);
668 
669  // Find a free block in this group.
670  bool changedBitmap = false;
671  while (true)
672  {
673  // Grab the specific block for the bitmap.
676  searchType tmp = *ptr;
677 
678  // Bitmap full of bits? Skip it.
679  if (tmp != static_cast<searchType>(-1))
680  {
681  // Check each bit in this field.
682  for (size_t j = 0; j < (sizeof(searchType) * 8);
683  j++, tmp >>= static_cast<searchType>(1))
684  {
685  // Free?
686  if ((tmp & 1) == 0)
687  {
688  // This block is free! Mark used.
689  *ptr |= (static_cast<searchType>(1) << j);
690  pDesc->bg_free_blocks_count--;
691 
692  // Yes, we changed the bitmap.
693  changedBitmap = true;
694 
695  // Update superblock.
696  m_pSuperblock->s_free_blocks_count--;
697 
698  // First block of this group...
699  uint32_t result =
700  group *
701  LITTLE_TO_HOST32(m_pSuperblock->s_blocks_per_group);
702  // Add the data block offset for this filesystem.
703  result +=
704  LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block);
705  // Blocks skipped so far (i == offset in bytes)...
706  result += ((idx * bitmapBlockBytes) +
707  (reinterpret_cast<uintptr_t>(ptr) - base))
708  << 3;
709  // Blocks skipped so far (j == bits ie blocks)...
710  result += j;
711  // Return block.
712  blocks.pushBack(result);
713 
714  // Check if we're done - we have nothing left to do if
715  // there's no more blocks free in this bitmap.
716  if ((++currentCount >= maxCount) ||
717  (!pDesc->bg_free_blocks_count))
718  {
719  break;
720  }
721  }
722  }
723  }
724 
725  // Did we make changes to the bitmap? Write back now if so - we don't
726  // want to keep writing over and over if e.g. we're setting more than
727  // one block above.
728  if (changedBitmap)
729  {
730  // Update bitmap on disk.
731  uint32_t desc_block =
732  LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_block_bitmap) +
733  idx;
734  writeBlock(desc_block);
735 
736  changedBitmap = false;
737  }
738 
739  // Are we finished with this loop?
740  if (currentCount >= maxCount)
741  {
742  break;
743  }
744 
745  // Haven't found anything yet - need to take care here.
746  if (++ptr >= ptr_end)
747  {
748  if ((++idx * bitmapBlockBytes) >= bytesToSearch)
749  break;
750 
751  base = list[idx];
752  ptr = reinterpret_cast<searchType *>(base);
753  ptr_end = adjust_pointer(ptr, bitmapBlockBytes);
754  }
755  }
756 
757  if (currentCount >= maxCount)
758  {
759  // Write back the superblock/group descriptor updates now.
760  m_pDisk->write(1024ULL);
761 
762  // Update group descriptor on disk.
764  uint32_t gdBlock =
765  LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
766  uint32_t groupBlock = (group * sizeof(GroupDesc)) / m_BlockSize;
767  writeBlock(gdBlock + groupBlock);
768  }
769 
770  return currentCount;
771 }
772 
774 {
775  for (uint32_t group = 0; group < m_nGroupDescriptors; group++)
776  {
777  // Any free inodes here?
778  GroupDesc *pDesc = m_pGroupDescriptors[group];
779  if (!pDesc->bg_free_inodes_count)
780  {
781  // No inodes free in this group.
782  continue;
783  }
784 
785  // Make sure this block group's inode bitmap has been loaded.
786  ensureFreeInodeBitmapLoaded(group);
787 
788  // 8 inodes per byte - i == bitmap offset in bytes.
789  Vector<size_t> &list = m_pInodeBitmaps[group];
790  for (size_t i = 0;
791  i < LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group) / 8;
792  i += sizeof(uint32_t))
793  {
794  // Calculate block index into the bitmap.
795  size_t idx = i / (m_BlockSize * 8);
796  size_t off = i % (m_BlockSize * 8);
797 
798  // Grab the specific block for the bitmap.
801  uintptr_t block = list[idx];
802  uint32_t *ptr = reinterpret_cast<uint32_t *>(block + off);
803  uint32_t tmp = *ptr;
804 
805  // If all bits set, avoid searching the bitmap.
806  if (tmp == ~0U)
807  continue;
808 
809  // Check each bit for free inode.
810  for (size_t j = 0; j < 32; j++, tmp >>= 1)
811  {
812  // Free?
813  if ((tmp & 1) == 0)
814  {
815  // This inode is free! Mark used.
816  *ptr |= (1 << j);
817  pDesc->bg_free_inodes_count--;
818 
819  // Update superblock.
820  m_pSuperblock->s_free_inodes_count--;
821  m_pDisk->write(1024ULL);
822 
823  // Update bitmap on disk.
824  uint32_t desc_block =
825  LITTLE_TO_HOST32(
826  m_pGroupDescriptors[group]->bg_inode_bitmap) +
827  idx;
828  writeBlock(desc_block);
829 
830  // Update group descriptor count on disk.
832  uint32_t gdBlock =
833  LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
834  uint32_t result = (group * sizeof(GroupDesc)) / m_BlockSize;
835  writeBlock(gdBlock + result);
836 
837  // First inode of this group...
838  uint32_t inode =
839  group *
840  LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
841  // Inodes skipped so far (i == offset in bytes)...
842  inode += i * 8;
843  // Inodes skipped so far (j == bits ie inodes)...
844  // Note: inodes start counting at one, not zero.
845  inode += j + 1;
846  // Return inode.
847  return inode;
848  }
849  }
850 
851  // Shouldn't get here - if there were no available blocks here it
852  // should have hit the "continue" above!
853  assert(false);
854  }
855  }
856 
857  return 0;
858 }
859 
860 void Ext2Filesystem::releaseBlock(uint32_t block)
861 {
862  // In some ext2 filesystems, this is zero so we don't need to do this. But
863  // for those that do, not doing this messes up the bit offsets below.
864  block -= LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block);
865 
866  uint32_t blocksPerGroup =
867  LITTLE_TO_HOST32(m_pSuperblock->s_blocks_per_group);
868  uint32_t group = block / blocksPerGroup;
869  uint32_t index = block % blocksPerGroup;
870 
871  if (!block)
872  {
873  // Error out, zero is used as a sentinel in a few places - and we almost
874  // certainly never actually mean to free block zero.
875  FATAL("Releasing block zero!");
876  }
877 
878  ensureFreeBlockBitmapLoaded(group);
879 
880  // Free block.
881  GroupDesc *pDesc = m_pGroupDescriptors[group];
882 
883  // Index = block offset from the start of this block.
884  size_t bitmapField = (index / 8) / m_BlockSize;
885  size_t bitmapOffset = (index / 8) % m_BlockSize;
886 
887  Vector<size_t> &list = m_pBlockBitmaps[group];
888  uintptr_t diskBlock = list[bitmapField];
889  uint8_t *ptr = reinterpret_cast<uint8_t *>(diskBlock + bitmapOffset);
890  uint8_t bit = (index % 8);
891  if ((*ptr & (1 << bit)) == 0)
892  ERROR("bit already freed for block " << Dec << block << Hex);
893  *ptr &= ~(1 << bit);
894 
895  // Update hints.
896  pDesc->bg_free_blocks_count++;
897  m_pSuperblock->s_free_blocks_count++;
898 
899  // Update superblock.
900  m_pDisk->write(1024ULL);
901 
902  // Update bitmap on disk.
903  uint32_t desc_block =
904  LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_block_bitmap) +
905  bitmapField;
906  writeBlock(desc_block);
907 
908  // Update group descriptor on disk.
910  uint32_t gdBlock = LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
911  uint32_t groupBlock = (group * sizeof(GroupDesc)) / m_BlockSize;
912  writeBlock(gdBlock + groupBlock);
913 }
914 
915 bool Ext2Filesystem::releaseInode(uint32_t inode)
916 {
917  Inode *pInode = getInode(inode);
918  --inode; // Inode zero is undefined, so it's not used.
919 
920  uint32_t inodesPerGroup =
921  LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
922  uint32_t group = inode / inodesPerGroup;
923  uint32_t index = inode % inodesPerGroup;
924 
925  bool bRemove = decreaseInodeRefcount(inode + 1);
926 
927  // Do we need to free this inode?
928  if (bRemove)
929  {
930  // Set dtime on inode.
931  pInode->i_dtime = HOST_TO_LITTLE32(getUnixTimestamp());
932 
933  ensureFreeInodeBitmapLoaded(group);
934 
935  // Free inode.
936  GroupDesc *pDesc = m_pGroupDescriptors[group];
937  pDesc->bg_free_inodes_count++;
938  m_pSuperblock->s_free_inodes_count++;
939 
940  // Index = inode offset from the start of this block.
941  size_t bitmapField = (index / 8) / m_BlockSize;
942  size_t bitmapOffset = (index / 8) % m_BlockSize;
943 
944  Vector<size_t> &list = m_pInodeBitmaps[group];
945  uintptr_t block = list[bitmapField];
946  uint8_t *ptr = reinterpret_cast<uint8_t *>(block + bitmapOffset);
947  *ptr &= ~(1 << (index % 8));
948 
949  // Update superblock.
950  m_pDisk->write(1024ULL);
951 
952  // Update on disk.
953  uint32_t desc_block =
954  LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_inode_bitmap) +
955  bitmapField;
956  writeBlock(desc_block);
957 
958  // Update group descriptor on disk.
960  uint32_t gdBlock =
961  LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
962  uint32_t groupBlock = (group * sizeof(GroupDesc)) / m_BlockSize;
963  writeBlock(gdBlock + groupBlock);
964  }
965 
966  writeInode(inode);
967  return bRemove;
968 }
969 
970 Inode *Ext2Filesystem::getInode(uint32_t inode)
971 {
972  assert(inode > 0);
973 
974  inode--; // Inode zero is undefined, so it's not used.
975 
976  uint32_t inodesPerGroup =
977  LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
978  uint32_t group = inode / inodesPerGroup;
979  uint32_t index = inode % inodesPerGroup;
980 
981  ensureInodeTableLoaded(group);
982  Vector<size_t> &list = m_pInodeTables[group];
983 
984  size_t blockNum = (index * m_InodeSize) / m_BlockSize;
985  size_t blockOff = (index * m_InodeSize) % m_BlockSize;
986 
987  uintptr_t block = list[blockNum];
988 
989  Inode *pInode = reinterpret_cast<Inode *>(block + blockOff);
990  if (pInode->i_flags & EXT2_COMPRBLK_FL)
991  {
992  WARNING(
993  "Ext2: inode " << inode
994  << " has compressed blocks - not yet supported!");
995  }
996  return pInode;
997 }
998 
999 void Ext2Filesystem::writeInode(uint32_t inode)
1000 {
1001  inode--; // Inode zero is undefined, so it's not used.
1002 
1003  uint32_t inodesPerGroup =
1004  LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
1005  uint32_t group = inode / inodesPerGroup;
1006  uint32_t index = inode % inodesPerGroup;
1007 
1008  ensureInodeTableLoaded(group);
1009 
1010  size_t blockNum = (index * m_InodeSize) / m_BlockSize;
1011  uint64_t diskBlock =
1012  LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_inode_table) + blockNum;
1013  writeBlock(diskBlock);
1014 }
1015 
1016 bool Ext2Filesystem::checkOptionalFeature(size_t feature)
1017 {
1018  if (LITTLE_TO_HOST32(m_pSuperblock->s_rev_level) < 1)
1019  return false;
1020  return m_pSuperblock->s_feature_compat & feature;
1021 }
1022 
1023 bool Ext2Filesystem::checkRequiredFeature(size_t feature)
1024 {
1025  if (LITTLE_TO_HOST32(m_pSuperblock->s_rev_level) < 1)
1026  return false;
1027  return m_pSuperblock->s_feature_incompat & feature;
1028 }
1029 
1030 bool Ext2Filesystem::checkReadOnlyFeature(size_t feature)
1031 {
1032  if (LITTLE_TO_HOST32(m_pSuperblock->s_rev_level) < 1)
1033  return false;
1034  return m_pSuperblock->s_feature_ro_compat & feature;
1035 }
1036 
1037 void Ext2Filesystem::ensureFreeBlockBitmapLoaded(size_t group)
1038 {
1039  assert(group < m_nGroupDescriptors);
1040  Vector<size_t> &list = m_pBlockBitmaps[group];
1041 
1042  if (list.size() > 0)
1043  // Descriptors already loaded.
1044  return;
1045 
1046  // Determine how many blocks to load to bring in the full block bitmap.
1047  // The bitmap works so that 8 blocks fit into one byte.
1048  uint32_t blocksPerGroup =
1049  LITTLE_TO_HOST32(m_pSuperblock->s_blocks_per_group);
1050  size_t nBlocks = blocksPerGroup / (m_BlockSize * 8);
1051  if (blocksPerGroup % (m_BlockSize * 8))
1052  nBlocks++;
1053 
1054  for (size_t i = 0; i < nBlocks; i++)
1055  {
1056  uint32_t blockNumber =
1057  LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_block_bitmap) + i;
1058  list.pushBack(readBlock(blockNumber));
1059  }
1060 }
1061 
1062 void Ext2Filesystem::ensureFreeInodeBitmapLoaded(size_t group)
1063 {
1064  assert(group < m_nGroupDescriptors);
1065  Vector<size_t> &list = m_pInodeBitmaps[group];
1066 
1067  if (list.size() > 0)
1068  // Descriptors already loaded.
1069  return;
1070 
1071  // Determine how many blocks to load to bring in the full inode bitmap.
1072  // The bitmap works so that 8 inodes fit into one byte.
1073  uint32_t inodesPerGroup =
1074  LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
1075  size_t nBlocks = inodesPerGroup / (m_BlockSize * 8);
1076  if (inodesPerGroup % (m_BlockSize * 8))
1077  nBlocks++;
1078 
1079  for (size_t i = 0; i < nBlocks; i++)
1080  {
1081  uint32_t blockNumber =
1082  LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_inode_bitmap) + i;
1083  list.pushBack(readBlock(blockNumber));
1084  }
1085 }
1086 
1087 void Ext2Filesystem::ensureInodeTableLoaded(size_t group)
1088 {
1089  assert(group < m_nGroupDescriptors);
1090  Vector<size_t> &list = m_pInodeTables[group];
1091 
1092  if (list.size() > 0)
1093  // Descriptors already loaded.
1094  return;
1095 
1096  // Determine how many blocks to load to bring in the full inode table.
1097  uint32_t inodesPerGroup =
1098  LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
1099  size_t nBlocks = (inodesPerGroup * m_InodeSize) / m_BlockSize;
1100  if ((inodesPerGroup * m_InodeSize) / m_BlockSize)
1101  nBlocks++;
1102 
1103  // Load each block in the inode table.
1104  for (size_t i = 0; i < nBlocks; i++)
1105  {
1106  uint32_t blockNumber =
1107  LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_inode_table) + i;
1108  uintptr_t buffer = readBlock(blockNumber);
1109  // Avoid callbacks allowing the wipeout of our inode.
1110  pinBlock(blockNumber);
1111  list.pushBack(buffer);
1112  }
1113 }
1114 
1115 void Ext2Filesystem::increaseInodeRefcount(uint32_t inode)
1116 {
1117  Inode *pInode = getInode(inode);
1118  if (!pInode)
1119  return;
1120 
1121  uint32_t current_count = LITTLE_TO_HOST32(pInode->i_links_count);
1122  pInode->i_links_count = HOST_TO_LITTLE32(current_count + 1);
1123 
1124  writeInode(inode);
1125 }
1126 
1127 bool Ext2Filesystem::decreaseInodeRefcount(uint32_t inode)
1128 {
1129  Inode *pInode = getInode(inode);
1130  if (!pInode)
1131  return true; // No inode found - but didn't decrement to zero.
1132 
1133  uint32_t current_count = LITTLE_TO_HOST32(pInode->i_links_count);
1134  bool bRemove = current_count <= 1;
1135  if (current_count)
1136  pInode->i_links_count = HOST_TO_LITTLE32(current_count - 1);
1137 
1138  writeInode(inode);
1139  return bRemove;
1140 }
1141 
1142 #ifndef EXT2_STANDALONE
1143 static bool initExt2()
1144 {
1145  VFS::instance().addProbeCallback(&Ext2Filesystem::probe);
1146  return true;
1147 }
1148 
1149 static void destroyExt2()
1150 {
1151 }
1152 
1153 MODULE_INFO("ext2", &initExt2, &destroyExt2, "vfs");
1154 #endif
virtual bool createFile(File *parent, const String &filename, uint32_t mask)
void pushBack(const T &value)
Definition: Vector.h:270
virtual bool addEntry(String filename, File *pFile, size_t type)
virtual void pinBlock(uint64_t location)
Definition: Ext2File.cc:91
virtual bool createNode(File *parent, const String &filename, uint32_t mask, const String &value, size_t type, uint32_t inodeOverride=0)
Ext2File(const Ext2File &file)
virtual Timer * getTimer()=0
virtual void writeBlock(uint64_t location, uintptr_t addr)
Definition: Ext2File.cc:86
size_t findFreeBlocksInGroup(uint32_t group, size_t maxCount, Vector< uint32_t > &blocks)
Definition: String.h:49
virtual String getVolumeLabel() const
void addProbeCallback(Filesystem::ProbeCallback callback)
Definition: VFS.cc:298
static ProcessorInformation & information()
Definition: Processor.cc:45
static VFS & instance()
Definition: VFS.cc:56
virtual Time::Timestamp getUnixTimestamp()
Definition: Timer.cc:23
Definition: Disk.h:32
Definition: ext2.h:152
virtual bool isSymlink()
Definition: File.cc:431
virtual File * getRoot() const
#define WARNING(text)
Definition: Log.h:78
virtual bool createDirectory(File *parent, const String &filename, uint32_t mask)
void releaseBlock(uint32_t block)
#define NOTICE(text)
Definition: Log.h:74
virtual bool initialise(Disk *pDisk)
bool ensureLargeEnough(size_t size, uint64_t location, uint64_t opsize, bool onlyBlocks=false, bool nozeroblocks=false)
Definition: Ext2Node.cc:144
virtual bool createLink(File *parent, const String &filename, File *target)
Definition: Log.h:136
#define assert(x)
Definition: assert.h:37
bool releaseInode(uint32_t inode)
void setModifiedTime(Time::Timestamp t)
Definition: File.cc:405
size_t size() const
Definition: Vector.h:258
bool findFreeBlocks(uint32_t inode, size_t count, Vector< uint32_t > &blocks)
String getName() const
Definition: File.cc:411
virtual void getName(String &str)
Definition: Disk.cc:46
virtual uintptr_t readBlock(uint64_t location)
Definition: Ext2File.cc:81
uintptr_t readBlock(uint32_t block)
#define ERROR(text)
Definition: Log.h:82
uint32_t findFreeInode()
virtual bool isDirectory()
Definition: File.cc:436
Definition: Log.h:138
#define FATAL(text)
Definition: Log.h:89
Definition: File.h:66
void writeBlock(uint32_t block)
virtual bool removeEntry(const String &filename, Ext2Node *pFile)
virtual bool remove(File *parent, File *file)
void setAccessedTime(Time::Timestamp t)
Definition: File.cc:394
virtual bool createSymlink(File *parent, const String &filename, const String &value)
virtual uint64_t write(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
Definition: File.cc:183