The Pedigree Project  0.1
Ext2Directory.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 "Ext2Directory.h"
21 #include "Ext2File.h"
22 #include "Ext2Filesystem.h"
23 #include "Ext2Symlink.h"
24 #include "ext2.h"
25 #include "modules/system/vfs/File.h"
26 #include "pedigree/kernel/Log.h"
27 #include "pedigree/kernel/stddef.h"
28 #include "pedigree/kernel/syscallError.h"
29 #include "pedigree/kernel/utilities/Pointers.h"
30 #include "pedigree/kernel/utilities/Vector.h"
31 #include "pedigree/kernel/utilities/assert.h"
32 #include "pedigree/kernel/utilities/utility.h"
33 
34 class Filesystem;
35 
37  const String &name, uintptr_t inode_num, Inode *inode, Ext2Filesystem *pFs,
38  File *pParent)
39  : Directory(
40  name, LITTLE_TO_HOST32(inode->i_atime),
41  LITTLE_TO_HOST32(inode->i_mtime), LITTLE_TO_HOST32(inode->i_ctime),
42  inode_num, static_cast<Filesystem *>(pFs),
43  LITTLE_TO_HOST32(inode->i_size),
44  pParent),
45  Ext2Node(inode_num, inode, pFs)
46 {
47  uint32_t mode = LITTLE_TO_HOST32(inode->i_mode);
48  setPermissionsOnly(modeToPermissions(mode));
49  setUidOnly(LITTLE_TO_HOST16(inode->i_uid));
50  setGidOnly(LITTLE_TO_HOST16(inode->i_gid));
51 }
52 
54 {
55 }
56 
57 bool Ext2Directory::addEntry(String filename, File *pFile, size_t type)
58 {
59  // Make sure we're already cached before we add an entry.
61 
62  // Calculate the size of our Dir* entry.
63  size_t length =
64  4 + /* 32-bit inode number */
65  2 + /* 16-bit record length */
66  1 + /* 8-bit name length */
67  1 + /* 8-bit file type */
68  filename
69  .length(); /* Don't leave space for NULL-terminator, not needed. */
70 
71  bool bFound = false;
72 
73  uint32_t i;
74  Dir *pDir = 0;
75  Dir *pLastDir = 0;
76  Dir *pBlockEnd = 0;
77  for (i = 0; i < m_Blocks.count(); i++)
78  {
79  ensureBlockLoaded(i);
80  uintptr_t buffer = m_pExt2Fs->readBlock(m_Blocks[i]);
81  pLastDir = pDir;
82  pDir = reinterpret_cast<Dir *>(buffer);
83  pBlockEnd = adjust_pointer(pDir, m_pExt2Fs->m_BlockSize);
84  while (pDir < pBlockEnd)
85  {
86  // What's the minimum length of this directory entry?
87  size_t thisReclen = 4 + 2 + 1 + 1 + pDir->d_namelen;
88  // Align to 4-byte boundary.
89  if (thisReclen % 4)
90  {
91  thisReclen += 4 - (thisReclen % 4);
92  }
93 
94  // Valid directory entry?
95  uint16_t entryReclen = LITTLE_TO_HOST16(pDir->d_reclen);
96  if (pDir->d_inode > 0)
97  {
98  // Is there enough space to add this dirent?
100  if (entryReclen - thisReclen >= length)
101  {
102  bFound = true;
103  // Save the current reclen.
104  uint16_t oldReclen = entryReclen;
105  // Adjust the current record's reclen field to the minimum.
106  pDir->d_reclen = HOST_TO_LITTLE16(thisReclen);
107  // Move to the new directory entry location.
108  pDir = adjust_pointer(pDir, thisReclen);
109  // New record length.
110  uint16_t newReclen = oldReclen - thisReclen;
111  // Set the new record length.
112  pDir->d_reclen = HOST_TO_LITTLE16(newReclen);
113  break;
114  }
115  }
116  else if (entryReclen == 0)
117  {
118  // No more entries to follow.
119  break;
120  }
121  else if (entryReclen - thisReclen >= length)
122  {
123  // We can use this unused entry - we fit into it.
124  // The record length does not need to be adjusted.
125  bFound = true;
126  break;
127  }
128 
129  // Next.
130  pLastDir = pDir;
131  pDir = adjust_pointer(pDir, entryReclen);
132  }
133  if (bFound)
134  break;
135  }
136 
137  if (!bFound || !pDir)
138  {
139  // Need to make a new block.
140  uint32_t block = m_pExt2Fs->findFreeBlock(getInodeNumber());
141  if (block == 0)
142  {
143  // We had a problem.
144  SYSCALL_ERROR(NoSpaceLeftOnDevice);
145  return false;
146  }
147  if (!addBlock(block))
148  return false;
149  i = m_Blocks.count() - 1;
150 
151  m_Size = m_Blocks.count() * m_pExt2Fs->m_BlockSize;
153 
157 
158  ensureBlockLoaded(i);
159  uintptr_t buffer = m_pExt2Fs->readBlock(m_Blocks[i]);
160 
161  ByteSet(reinterpret_cast<void *>(buffer), 0, m_pExt2Fs->m_BlockSize);
162  pDir = reinterpret_cast<Dir *>(buffer);
163  pDir->d_reclen = HOST_TO_LITTLE16(m_pExt2Fs->m_BlockSize);
164 
166  }
167 
168  // Set the directory contents.
169  uint32_t entryInode = pFile->getInode();
170  pDir->d_inode = HOST_TO_LITTLE32(entryInode);
171  m_pExt2Fs->increaseInodeRefcount(entryInode);
172 
173  if (m_pExt2Fs->checkRequiredFeature(2))
174  {
175  // File type in directory entry.
176  switch (type)
177  {
178  case EXT2_S_IFREG:
179  pDir->d_file_type = EXT2_FILE;
180  break;
181  case EXT2_S_IFDIR:
182  pDir->d_file_type = EXT2_DIRECTORY;
183  break;
184  case EXT2_S_IFLNK:
185  pDir->d_file_type = EXT2_SYMLINK;
186  break;
187  default:
188  ERROR("Unrecognised filetype.");
189  pDir->d_file_type = EXT2_UNKNOWN;
190  }
191  }
192  else
193  {
194  // No file type in directory entries.
195  pDir->d_file_type = 0;
196  }
197 
198  pDir->d_namelen = filename.length();
199  MemoryCopy(
200  pDir->d_name, static_cast<const char *>(filename), filename.length());
201 
202  // We're all good - add the directory to our cache.
203  addDirectoryEntry(filename, pFile);
204 
205  // Trigger write back to disk.
206  m_pExt2Fs->writeBlock(m_Blocks[i]);
207 
208  m_Size = m_nSize;
209 
210  return true;
211 }
212 
213 bool Ext2Directory::removeEntry(const String &filename, Ext2Node *pFile)
214 {
215  // Find this file in the directory.
216  size_t fileInode = pFile->getInodeNumber();
217 
218  bool bFound = false;
219 
220  uint32_t i;
221  Dir *pDir, *pLastDir = 0;
222  for (i = 0; i < m_Blocks.count(); i++)
223  {
224  ensureBlockLoaded(i);
225  uintptr_t buffer = m_pExt2Fs->readBlock(m_Blocks[i]);
226  pDir = reinterpret_cast<Dir *>(buffer);
227  pLastDir = 0;
228  while (reinterpret_cast<uintptr_t>(pDir) <
229  buffer + m_pExt2Fs->m_BlockSize)
230  {
231  if (LITTLE_TO_HOST32(pDir->d_inode) == fileInode)
232  {
233  if (pDir->d_namelen == filename.length())
234  {
235  if (!StringCompareN(
236  pDir->d_name, static_cast<const char *>(filename),
237  pDir->d_namelen))
238  {
239  // Wipe out the directory entry.
240  uint16_t old_reclen = LITTLE_TO_HOST16(pDir->d_reclen);
241  ByteSet(pDir, 0, old_reclen);
242 
248 
249  pDir->d_reclen = HOST_TO_LITTLE16(old_reclen);
250 
251  m_pExt2Fs->writeBlock(m_Blocks[i]);
252  bFound = true;
253  break;
254  }
255  }
256  }
257  else if (!pDir->d_reclen)
258  {
259  // No more entries.
260  break;
261  }
262 
263  pDir = reinterpret_cast<Dir *>(
264  reinterpret_cast<uintptr_t>(pDir) +
265  LITTLE_TO_HOST16(pDir->d_reclen));
266  }
267 
268  if (bFound)
269  break;
270  }
271 
272  m_Size = m_nSize;
273 
274  if (bFound)
275  {
276  if (m_pExt2Fs->releaseInode(fileInode))
277  {
278  // Remove all blocks for the file, inode has hit zero refcount.
279  pFile->wipe();
280  }
281  return true;
282  }
283  else
284  {
285  SYSCALL_ERROR(DoesNotExist);
286  return false;
287  }
288 }
289 
291 {
292  if (isCachePopulated())
293  {
294  return;
295  }
296 
297  uint32_t i;
298  Dir *pDir;
299  for (i = 0; i < m_Blocks.count(); i++)
300  {
301  ensureBlockLoaded(i);
302 
303  // Grab the block and pin it while we parse it.
304  uintptr_t buffer = m_pExt2Fs->readBlock(m_Blocks[i]);
305  assert(buffer);
306  pDir = reinterpret_cast<Dir *>(buffer);
307 
308  while (reinterpret_cast<uintptr_t>(pDir) <
309  buffer + m_pExt2Fs->m_BlockSize)
310  {
311  size_t reclen = LITTLE_TO_HOST16(pDir->d_reclen);
312 
313  Dir *pNextDir = adjust_pointer(pDir, reclen);
314  if (pDir->d_inode == 0)
315  {
316  if (pDir == pNextDir)
317  {
318  // No further iteration possible (null entry).
319  break;
320  }
321 
322  // Oops, not a valid entry (possibly deleted file). Skip.
323  pDir = pNextDir;
324  continue;
325  }
326 
327  // we only need inode + file type fields, to save memory
328  size_t copylen = offsetof(Dir, d_name);
329 
331  meta.pDirectory = this;
332  meta.opaque =
333  pedigree_std::move(UniqueArray<char>::allocate(copylen));
334  MemoryCopy(meta.opaque.get(), pDir, copylen);
335 
336  size_t namelen = pDir->d_namelen;
337 
338  // Can we get the file type from the directory entry?
339  size_t fileType = EXT2_UNKNOWN;
340  bool ok = true;
341  if (m_pExt2Fs->checkRequiredFeature(2))
342  {
343  fileType = pDir->d_file_type;
344  switch (fileType)
345  {
346  case EXT2_FILE:
347  case EXT2_DIRECTORY:
348  case EXT2_SYMLINK:
349  break;
350  default:
351  ERROR(
352  "EXT2: Directory entry has unsupported file type: "
353  << pDir->d_file_type);
354  ok = false;
355  break;
356  }
357  }
358  else
359  {
360  uint32_t inodeNum = LITTLE_TO_HOST32(pDir->d_inode);
361  Inode *inode = m_pExt2Fs->getInode(inodeNum);
362 
363  // Acceptable file type?
364  size_t inode_ftype = inode->i_mode & 0xF000;
365  switch (inode_ftype)
366  {
367  case EXT2_S_IFLNK:
368  case EXT2_S_IFREG:
369  case EXT2_S_IFDIR:
370  break;
371  default:
372  ERROR(
373  "EXT2: Inode has unsupported file type: "
374  << inode_ftype << ".");
375  ok = false;
376  break;
377  }
378 
379  // In this case, the file type entry is the top 8 bits of the
380  // filename length.
381  namelen |= pDir->d_file_type << 8;
382  }
383 
384  if (ok)
385  {
386  String filename(pDir->d_name, namelen);
387  meta.filename = filename; // copy into the metadata structure
388  addDirectoryEntry(filename, pedigree_std::move(meta));
389  }
390 
391  // Next.
392  pDir = pNextDir;
393  }
394 
395  // Done with this block now; nothing remains that points to it.
396  m_pExt2Fs->unpinBlock(m_Blocks[i]);
397  }
398 
400 }
401 
403 {
404  static_cast<Ext2Node *>(this)->fileAttributeChanged(
405  m_Size, m_AccessedTime, m_ModifiedTime, m_CreationTime);
406  static_cast<Ext2Node *>(this)->updateMetadata(
407  getUid(), getGid(), permissionsToMode(getPermissions()));
408 }
409 
411 {
412  Dir *pDir = reinterpret_cast<Dir *>(meta.opaque.get());
413 
414  uint32_t inodeNum = LITTLE_TO_HOST32(pDir->d_inode);
415  Inode *inode = m_pExt2Fs->getInode(inodeNum);
416 
417  // Can we get the file type from the directory entry?
418  size_t fileType = EXT2_UNKNOWN;
419  if (m_pExt2Fs->checkRequiredFeature(2))
420  {
421  // Directory entry holds file type.
422  fileType = pDir->d_file_type;
423  }
424  else
425  {
426  // Inode holds file type.
427  size_t inode_ftype = inode->i_mode & 0xF000;
428  switch (inode_ftype)
429  {
430  case EXT2_S_IFLNK:
431  fileType = EXT2_SYMLINK;
432  break;
433  case EXT2_S_IFREG:
434  fileType = EXT2_FILE;
435  break;
436  case EXT2_S_IFDIR:
437  fileType = EXT2_DIRECTORY;
438  break;
439  default:
440  // this should have been validated previously
441  FATAL("Bad inode file type in Ext2Directory::convertToFile");
442  break;
443  }
444  }
445 
446  File *pFile = 0;
447  switch (fileType)
448  {
449  case EXT2_FILE:
450  pFile =
451  new Ext2File(meta.filename, inodeNum, inode, m_pExt2Fs, this);
452  break;
453  case EXT2_DIRECTORY:
454  pFile = new Ext2Directory(
455  meta.filename, inodeNum, inode, m_pExt2Fs, this);
456  break;
457  case EXT2_SYMLINK:
458  pFile = new Ext2Symlink(
459  meta.filename, inodeNum, inode, m_pExt2Fs, this);
460  break;
461  default:
462  // this should have been validated previously
463  FATAL("Bad file type in Ext2Directory::convertToFile");
464  break;
465  }
466 
467  return pFile;
468 }
virtual bool addEntry(String filename, File *pFile, size_t type)
size_t count() const
Definition: Vector.h:264
Ext2Directory(const Ext2Directory &file)
void setUidOnly(size_t uid)
Definition: File.cc:644
virtual ~Ext2Directory()
virtual File * convertToFile(const DirectoryEntryMetadata &meta)
Definition: String.h:49
Definition: ext2.h:152
void addDirectoryEntry(const String &name, File *pTarget)
Definition: Directory.cc:114
void setPermissionsOnly(uint32_t perms)
Definition: File.cc:639
uint32_t m_BlockSize
void updateMetadata(uint16_t uid, uint16_t gid, uint32_t perms)
Definition: Ext2Node.cc:497
#define assert(x)
Definition: assert.h:37
bool releaseInode(uint32_t inode)
void setGidOnly(size_t gid)
Definition: File.cc:649
void wipe()
Definition: Ext2Node.cc:115
Definition: ext2.h:175
uintptr_t readBlock(uint32_t block)
void fileAttributeChanged()
#define ERROR(text)
Definition: Log.h:82
#define FATAL(text)
Definition: Log.h:89
Definition: File.h:66
void writeBlock(uint32_t block)
virtual bool removeEntry(const String &filename, Ext2Node *pFile)
void markCachePopulated()
Definition: Directory.h:187
virtual void cacheDirectoryContents()
virtual bool isCachePopulated() const
Definition: Directory.h:90