The Pedigree Project  0.1
Ext2Node.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 "Ext2Node.h"
21 #include "Ext2Filesystem.h"
22 #include "ext2.h"
23 #include "modules/system/vfs/File.h"
24 #include "pedigree/kernel/Log.h"
25 #include "pedigree/kernel/compiler.h"
26 #include "pedigree/kernel/syscallError.h"
27 #include "pedigree/kernel/utilities/assert.h"
28 #include "pedigree/kernel/utilities/utility.h"
29 
30 Ext2Node::Ext2Node(uintptr_t inode_num, Inode *pInode, Ext2Filesystem *pFs)
31  : m_pInode(pInode), m_InodeNumber(inode_num), m_pExt2Fs(pFs), m_Blocks(),
32  m_nMetadataBlocks(0), m_nSize(LITTLE_TO_HOST32(pInode->i_size))
33 {
34  // i_blocks == # of 512-byte blocks. Convert to FS block count.
35  uint32_t blockCount = LITTLE_TO_HOST32(pInode->i_blocks);
36  uint32_t totalBlocks = (blockCount * 512) / m_pExt2Fs->m_BlockSize;
37 
38  size_t dataBlockCount = m_nSize / m_pExt2Fs->m_BlockSize;
39  if (m_nSize % m_pExt2Fs->m_BlockSize)
40  {
41  ++dataBlockCount;
42  }
43 
44  m_Blocks.reserve(dataBlockCount, false);
45  m_nMetadataBlocks = totalBlocks - dataBlockCount;
46 
47  for (size_t i = 0; i < 12 && i < dataBlockCount; i++)
48  m_Blocks.pushBack(LITTLE_TO_HOST32(m_pInode->i_block[i]));
49 
50  // We'll read these later.
51  for (size_t i = 12; i < dataBlockCount; ++i)
52  {
53  m_Blocks.pushBack(~0);
54  }
55 }
56 
58 {
59 }
60 
61 uintptr_t Ext2Node::readBlock(uint64_t location)
62 {
63  // Sanity check.
64  uint32_t nBlock = location / m_pExt2Fs->m_BlockSize;
65  if (nBlock > m_Blocks.count())
66  {
67  ERROR(
68  "Ext2Node::readBlock beyond blocks [" << nBlock << ", "
69  << m_Blocks.count() << "]");
70  return 0;
71  }
72  if (location > m_nSize)
73  {
74  ERROR(
75  "Ext2Node::readBlock beyond size [" << location << ", " << m_nSize
76  << "]");
77  return 0;
78  }
79 
80  ensureBlockLoaded(nBlock);
81  uintptr_t result = m_pExt2Fs->readBlock(m_Blocks[nBlock]);
82 
83  // Add any remaining offset we chopped off.
84  result += location % m_pExt2Fs->m_BlockSize;
85  return result;
86 }
87 
88 void Ext2Node::writeBlock(uint64_t location)
89 {
90  // Sanity check.
91  uint32_t nBlock = location / m_pExt2Fs->m_BlockSize;
92  if (nBlock > m_Blocks.count())
93  return;
94  if (location > m_nSize)
95  return;
96 
97  // Update on disk.
98  ensureBlockLoaded(nBlock);
99  m_pExt2Fs->writeBlock(m_Blocks[nBlock]);
100 }
101 
102 void Ext2Node::trackBlock(uint32_t block)
103 {
104  m_Blocks.pushBack(block);
105 
106  // Inode i_blocks field is actually the count of 512-byte blocks.
107  uint32_t i_blocks =
108  ((m_Blocks.count() + m_nMetadataBlocks) * m_pExt2Fs->m_BlockSize) / 512;
109  m_pInode->i_blocks = HOST_TO_LITTLE32(i_blocks);
110 
111  // Write updated inode.
112  m_pExt2Fs->writeInode(getInodeNumber());
113 }
114 
116 {
117  for (size_t i = 0; i < m_Blocks.count(); ++i)
118  {
119  ensureBlockLoaded(i);
120  m_pExt2Fs->releaseBlock(m_Blocks[i]);
121  }
122  m_Blocks.clear();
123 
124  m_nSize = 0;
125 
126  m_pInode->i_size = 0;
127  m_pInode->i_blocks = 0;
128  ByteSet(m_pInode->i_block, 0, sizeof(uint32_t) * 15);
129 
130  // Write updated inode.
131  m_pExt2Fs->writeInode(getInodeNumber());
132 }
133 
134 void Ext2Node::extend(size_t newSize)
135 {
136  ensureLargeEnough(newSize, 0, 0);
137 }
138 
139 void Ext2Node::extend(size_t newSize, uint64_t location, uint64_t size)
140 {
141  ensureLargeEnough(newSize, location, size);
142 }
143 
144 bool Ext2Node::ensureLargeEnough(size_t size, uint64_t location, uint64_t opsize, bool onlyBlocks, bool nozeroblocks)
145 {
146  // The majority of times this is called, we won't need to allocate blocks.
147  // So, we check for that early. Then, we can move on to actually allocating
148  // blocks if that is necessary.
149  size_t blockSize = m_pExt2Fs->m_BlockSize;
150  size_t currentMaxSize = m_Blocks.count() * blockSize;
151  if (LIKELY(size <= currentMaxSize))
152  {
153  if (size > m_nSize && !onlyBlocks)
154  {
155  // preallocate() doesn't change this, so fix the mismatch now.
156  m_nSize = size;
158  m_nSize, LITTLE_TO_HOST32(m_pInode->i_atime),
159  LITTLE_TO_HOST32(m_pInode->i_mtime),
160  LITTLE_TO_HOST32(m_pInode->i_ctime));
161  }
162  return true;
163  }
164  else if (!onlyBlocks)
165  {
166  m_nSize = size;
168  m_nSize, LITTLE_TO_HOST32(m_pInode->i_atime),
169  LITTLE_TO_HOST32(m_pInode->i_mtime),
170  LITTLE_TO_HOST32(m_pInode->i_ctime));
171  }
172 
173  size_t delta = size - currentMaxSize;
174  size_t deltaBlocks = delta / blockSize;
175  if (delta % blockSize)
176  {
177  ++deltaBlocks;
178  }
179 
180  // Allocate the needed blocks.
181  Vector<uint32_t> newBlocks;
182 #if 1
183  if (!m_pExt2Fs->findFreeBlocks(m_InodeNumber, deltaBlocks, newBlocks))
184  {
185  SYSCALL_ERROR(NoSpaceLeftOnDevice);
186  return false;
187  }
188 #else
189  for (size_t i = 0; i < deltaBlocks; ++i)
190  {
191  uint32_t block = m_pExt2Fs->findFreeBlock(m_InodeNumber);
192  if (!block)
193  {
194  SYSCALL_ERROR(NoSpaceLeftOnDevice);
195  return false;
196  }
197  else
198  {
199  newBlocks.pushBack(block);
200  }
201  }
202 #endif
203 
204  for (auto block : newBlocks)
205  {
206  if (!addBlock(block))
207  {
208  ERROR("Adding block " << block << " failed!");
209  return false;
210  }
211 
212  // Load the block
213  uint8_t *pBuffer =
214  reinterpret_cast<uint8_t *>(m_pExt2Fs->readBlock(block));
215 
216  // do we need to zero it?
217  bool zero = !nozeroblocks;
218  if (location || opsize)
219  {
220  uint64_t offset = block * blockSize;
221  if (offset >= location)
222  {
223  // block is within the range we've been given
224  size_t end = (block + 1) * blockSize;
225 
226  if (end <= (location + opsize))
227  {
228  zero = false;
229  }
230  }
231  }
232 
233  if (zero)
234  {
235  ByteSet(pBuffer, 0, m_pExt2Fs->m_BlockSize);
236  }
237  }
238 
239  return true;
240 }
241 
242 bool Ext2Node::ensureBlockLoaded(size_t nBlock)
243 {
244  if (nBlock > m_Blocks.count())
245  {
246  FATAL(
247  "EXT2: ensureBlockLoaded: Algorithmic error [block "
248  << nBlock << " > " << m_Blocks.count() << "].");
249  }
250  if (m_Blocks[nBlock] == ~0U)
251  getBlockNumber(nBlock);
252 
253  return true;
254 }
255 
256 bool Ext2Node::getBlockNumber(size_t nBlock)
257 {
258  size_t nPerBlock = m_pExt2Fs->m_BlockSize / 4;
259 
260  assert(nBlock >= 12);
261 
262  if (nBlock < nPerBlock + 12)
263  {
264  getBlockNumberIndirect(
265  LITTLE_TO_HOST32(m_pInode->i_block[12]), 12, nBlock);
266  return true;
267  }
268 
269  if (nBlock < (nPerBlock * nPerBlock) + nPerBlock + 12)
270  {
271  getBlockNumberBiindirect(
272  LITTLE_TO_HOST32(m_pInode->i_block[13]), nPerBlock + 12, nBlock);
273  return true;
274  }
275 
276  getBlockNumberTriindirect(
277  LITTLE_TO_HOST32(m_pInode->i_block[14]),
278  (nPerBlock * nPerBlock) + nPerBlock + 12, nBlock);
279 
280  return true;
281 }
282 
283 bool Ext2Node::getBlockNumberIndirect(
284  uint32_t inode_block, size_t nBlocks, size_t nBlock)
285 {
286  uint32_t *buffer =
287  reinterpret_cast<uint32_t *>(m_pExt2Fs->readBlock(inode_block));
288 
289  for (size_t i = 0;
290  i < m_pExt2Fs->m_BlockSize / 4 && nBlocks < m_Blocks.count(); i++)
291  {
292  m_Blocks[nBlocks++] = LITTLE_TO_HOST32(buffer[i]);
293  }
294 
295  return true;
296 }
297 
298 bool Ext2Node::getBlockNumberBiindirect(
299  uint32_t inode_block, size_t nBlocks, size_t nBlock)
300 {
301  size_t nPerBlock = m_pExt2Fs->m_BlockSize / 4;
302 
303  uint32_t *buffer =
304  reinterpret_cast<uint32_t *>(m_pExt2Fs->readBlock(inode_block));
305 
306  // What indirect block does nBlock exist on?
307  size_t nIndirectBlock = (nBlock - nBlocks) / nPerBlock;
308 
309  getBlockNumberIndirect(
310  LITTLE_TO_HOST32(buffer[nIndirectBlock]),
311  nBlocks + nIndirectBlock * nPerBlock, nBlock);
312 
313  return true;
314 }
315 
316 bool Ext2Node::getBlockNumberTriindirect(
317  uint32_t inode_block, size_t nBlocks, size_t nBlock)
318 {
319  size_t nPerBlock = m_pExt2Fs->m_BlockSize / 4;
320 
321  uint32_t *buffer =
322  reinterpret_cast<uint32_t *>(m_pExt2Fs->readBlock(inode_block));
323 
324  // What biindirect block does nBlock exist on?
325  size_t nBiBlock = (nBlock - nBlocks) / (nPerBlock * nPerBlock);
326 
327  getBlockNumberBiindirect(
328  LITTLE_TO_HOST32(buffer[nBiBlock]),
329  nBlocks + nBiBlock * nPerBlock * nPerBlock, nBlock);
330 
331  return true;
332 }
333 
334 bool Ext2Node::addBlock(uint32_t blockValue)
335 {
336  size_t nEntriesPerBlock = m_pExt2Fs->m_BlockSize / 4;
337 
338  // Calculate whether direct, indirect or tri-indirect addressing is needed.
339  if (m_Blocks.count() < 12)
340  {
341  // Direct addressing is possible.
342  m_pInode->i_block[m_Blocks.count()] = HOST_TO_LITTLE32(blockValue);
343  }
344  else if (m_Blocks.count() < 12 + nEntriesPerBlock)
345  {
346  // Indirect addressing needed.
347  size_t indirectIdx = m_Blocks.count() - 12;
348 
349  // If this is the first indirect block, we need to reserve a new table
350  // block.
351  if (m_Blocks.count() == 12)
352  {
353  uint32_t newBlock = m_pExt2Fs->findFreeBlock(m_InodeNumber);
354  m_pInode->i_block[12] = HOST_TO_LITTLE32(newBlock);
355  if (m_pInode->i_block[12] == 0)
356  {
357  // We had a problem.
358  SYSCALL_ERROR(NoSpaceLeftOnDevice);
359  return false;
360  }
361 
362  void *buffer =
363  reinterpret_cast<void *>(m_pExt2Fs->readBlock(newBlock));
364  ByteSet(buffer, 0, m_pExt2Fs->m_BlockSize);
365 
366  // Write back the zeroed block to prepare the indirect block.
367  m_pExt2Fs->writeBlock(newBlock);
368 
369  // Taken on a new block - update block count (but don't track in
370  // m_Blocks, as this is a metadata block).
371  m_nMetadataBlocks++;
372  }
373 
374  // Now we can set the block.
375  uint32_t bufferBlock = LITTLE_TO_HOST32(m_pInode->i_block[12]);
376  uint32_t *buffer =
377  reinterpret_cast<uint32_t *>(m_pExt2Fs->readBlock(bufferBlock));
378 
379  buffer[indirectIdx] = HOST_TO_LITTLE32(blockValue);
380  m_pExt2Fs->writeBlock(bufferBlock);
381  }
382  else if (
383  m_Blocks.count() <
384  12 + nEntriesPerBlock + nEntriesPerBlock * nEntriesPerBlock)
385  {
386  // Bi-indirect addressing required.
387 
388  // Index from the start of the bi-indirect block (i.e. ignore the 12
389  // direct entries and one indirect block).
390  size_t biIdx = m_Blocks.count() - 12 - nEntriesPerBlock;
391  // Block number inside the bi-indirect table of where to find the
392  // indirect block table.
393  size_t indirectBlock = biIdx / nEntriesPerBlock;
394  // Index inside the indirect block table.
395  size_t indirectIdx = biIdx % nEntriesPerBlock;
396 
397  // If this is the first bi-indirect block, we need to reserve a
398  // bi-indirect table block.
399  if (biIdx == 0)
400  {
401  uint32_t newBlock = m_pExt2Fs->findFreeBlock(m_InodeNumber);
402  m_pInode->i_block[13] = HOST_TO_LITTLE32(newBlock);
403  if (m_pInode->i_block[13] == 0)
404  {
405  // We had a problem.
406  SYSCALL_ERROR(NoSpaceLeftOnDevice);
407  return false;
408  }
409 
410  void *buffer =
411  reinterpret_cast<void *>(m_pExt2Fs->readBlock(newBlock));
412  ByteSet(buffer, 0, m_pExt2Fs->m_BlockSize);
413 
414  // Taken on a new block - update block count (but don't track in
415  // m_Blocks, as this is a metadata block).
416  m_nMetadataBlocks++;
417  }
418 
419  // Now we can safely read the bi-indirect block.
420  uint32_t bufferBlock = LITTLE_TO_HOST32(m_pInode->i_block[13]);
421  uint32_t *pBlock =
422  reinterpret_cast<uint32_t *>(m_pExt2Fs->readBlock(bufferBlock));
423 
424  // Do we need to start a new indirect block?
425  if (indirectIdx == 0)
426  {
427  uint32_t newBlock = m_pExt2Fs->findFreeBlock(m_InodeNumber);
428  pBlock[indirectBlock] = HOST_TO_LITTLE32(newBlock);
429  if (pBlock[indirectBlock] == 0)
430  {
431  // We had a problem.
432  SYSCALL_ERROR(NoSpaceLeftOnDevice);
433  return false;
434  }
435 
436  m_pExt2Fs->writeBlock(bufferBlock);
437 
438  void *buffer =
439  reinterpret_cast<void *>(m_pExt2Fs->readBlock(newBlock));
440  ByteSet(buffer, 0, m_pExt2Fs->m_BlockSize);
441 
442  // Taken on a new block - update block count (but don't track in
443  // m_Blocks, as this is a metadata block).
444  m_nMetadataBlocks++;
445  }
446 
447  // Cache this as it gets clobbered by the readBlock call (using the same
448  // buffer).
449  uint32_t nIndirectBlockNum = LITTLE_TO_HOST32(pBlock[indirectBlock]);
450 
451  // Grab the indirect block.
452  pBlock = reinterpret_cast<uint32_t *>(
453  m_pExt2Fs->readBlock(nIndirectBlockNum));
454  if (pBlock == reinterpret_cast<uint32_t *>(~0))
455  {
456  ERROR(
457  "Could not read block (" << nIndirectBlockNum
458  << ") that we wanted to add.");
459  return false;
460  }
461 
462  // Set the correct entry.
463  pBlock[indirectIdx] = HOST_TO_LITTLE32(blockValue);
464  m_pExt2Fs->writeBlock(nIndirectBlockNum);
465  }
466  else
467  {
468  // Tri-indirect addressing required.
469  FATAL("EXT2: Tri-indirect addressing required, but not implemented.");
470  return false;
471  }
472 
473  trackBlock(blockValue);
474 
475  return true;
476 }
477 
479  size_t size, size_t atime, size_t mtime, size_t ctime)
480 {
481  // Reconstruct the inode from the cached fields.
482  uint32_t i_blocks =
483  ((m_Blocks.count() + m_nMetadataBlocks) * m_pExt2Fs->m_BlockSize) / 512;
484  m_pInode->i_blocks = HOST_TO_LITTLE32(i_blocks);
485  m_pInode->i_size = HOST_TO_LITTLE32(size);
486  m_pInode->i_atime = HOST_TO_LITTLE32(atime);
487  m_pInode->i_mtime = HOST_TO_LITTLE32(mtime);
488  m_pInode->i_ctime = HOST_TO_LITTLE32(ctime);
489 
490  // Update our internal record of the file size accordingly.
491  m_nSize = size;
492 
493  // Write updated inode.
494  m_pExt2Fs->writeInode(getInodeNumber());
495 }
496 
497 void Ext2Node::updateMetadata(uint16_t uid, uint16_t gid, uint32_t perms)
498 {
499  // Avoid wiping out extra mode bits that Pedigree doesn't yet care about.
500  uint32_t curr_mode = LITTLE_TO_HOST32(m_pInode->i_mode);
501  curr_mode &= ~((1 << 9) - 1);
502  curr_mode |= perms;
503 
504  m_pInode->i_uid = HOST_TO_LITTLE16(uid);
505  m_pInode->i_gid = HOST_TO_LITTLE16(gid);
506  m_pInode->i_mode = HOST_TO_LITTLE32(curr_mode);
507 
508  // Write updated inode.
509  m_pExt2Fs->writeInode(getInodeNumber());
510 }
511 
512 void Ext2Node::sync(size_t offset, bool async)
513 {
514  uint32_t nBlock = offset / m_pExt2Fs->m_BlockSize;
515  if (nBlock > m_Blocks.count())
516  return;
517  if (offset > m_nSize)
518  return;
519 
520  // Sync the block.
521  ensureBlockLoaded(nBlock);
522  m_pExt2Fs->sync(m_Blocks[nBlock] * m_pExt2Fs->m_BlockSize, async);
523 }
524 
525 void Ext2Node::pinBlock(uint64_t location)
526 {
527  uint32_t nBlock = location / m_pExt2Fs->m_BlockSize;
528  if (nBlock > m_Blocks.count())
529  return;
530  if (location > m_nSize)
531  return;
532 
533  ensureBlockLoaded(nBlock);
534  m_pExt2Fs->pinBlock(m_Blocks[nBlock]);
535 }
536 
537 void Ext2Node::unpinBlock(uint64_t location)
538 {
539  uint32_t nBlock = location / m_pExt2Fs->m_BlockSize;
540  if (nBlock > m_Blocks.count())
541  return;
542  if (location > m_nSize)
543  return;
544 
545  ensureBlockLoaded(nBlock);
546  m_pExt2Fs->unpinBlock(m_Blocks[nBlock]);
547 }
548 
549 uint32_t Ext2Node::modeToPermissions(uint32_t mode) const
550 {
551  uint32_t permissions = 0;
552  if (mode & EXT2_S_IRUSR)
553  permissions |= FILE_UR;
554  if (mode & EXT2_S_IWUSR)
555  permissions |= FILE_UW;
556  if (mode & EXT2_S_IXUSR)
557  permissions |= FILE_UX;
558  if (mode & EXT2_S_IRGRP)
559  permissions |= FILE_GR;
560  if (mode & EXT2_S_IWGRP)
561  permissions |= FILE_GW;
562  if (mode & EXT2_S_IXGRP)
563  permissions |= FILE_GX;
564  if (mode & EXT2_S_IROTH)
565  permissions |= FILE_OR;
566  if (mode & EXT2_S_IWOTH)
567  permissions |= FILE_OW;
568  if (mode & EXT2_S_IXOTH)
569  permissions |= FILE_OX;
570  return permissions;
571 }
572 
573 uint32_t Ext2Node::permissionsToMode(uint32_t permissions) const
574 {
575  uint32_t mode = 0;
576  if (permissions & FILE_UR)
577  mode |= EXT2_S_IRUSR;
578  if (permissions & FILE_UW)
579  mode |= EXT2_S_IWUSR;
580  if (permissions & FILE_UX)
581  mode |= EXT2_S_IXUSR;
582  if (permissions & FILE_GR)
583  mode |= EXT2_S_IRGRP;
584  if (permissions & FILE_GW)
585  mode |= EXT2_S_IWGRP;
586  if (permissions & FILE_GX)
587  mode |= EXT2_S_IXGRP;
588  if (permissions & FILE_OR)
589  mode |= EXT2_S_IROTH;
590  if (permissions & FILE_OW)
591  mode |= EXT2_S_IWOTH;
592  if (permissions & FILE_OX)
593  mode |= EXT2_S_IXOTH;
594  return mode;
595 }
void pushBack(const T &value)
Definition: Vector.h:270
size_t count() const
Definition: Vector.h:264
Ext2Node(const Ext2Node &file)
Definition: ext2.h:152
uint32_t m_BlockSize
void releaseBlock(uint32_t block)
void updateMetadata(uint16_t uid, uint16_t gid, uint32_t perms)
Definition: Ext2Node.cc:497
bool ensureLargeEnough(size_t size, uint64_t location, uint64_t opsize, bool onlyBlocks=false, bool nozeroblocks=false)
Definition: Ext2Node.cc:144
#define assert(x)
Definition: assert.h:37
void fileAttributeChanged(size_t size, size_t atime, size_t mtime, size_t ctime)
Definition: Ext2Node.cc:478
void wipe()
Definition: Ext2Node.cc:115
bool findFreeBlocks(uint32_t inode, size_t count, Vector< uint32_t > &blocks)
void reserve(size_t size, bool copy)
Definition: Vector.h:390
uintptr_t readBlock(uint32_t block)
virtual ~Ext2Node()
Definition: Ext2Node.cc:57
#define ERROR(text)
Definition: Log.h:82
#define FATAL(text)
Definition: Log.h:89
void clear(bool freeMem=false)
Definition: Vector.h:337
void writeBlock(uint32_t block)