The Pedigree Project  0.1
FatDirectory.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 "FatDirectory.h"
21 #include "FatFile.h"
22 #include "FatFilesystem.h"
23 #include "FatSymlink.h"
24 #include "fat.h"
25 #include "modules/system/vfs/File.h"
26 #include "pedigree/kernel/LockGuard.h"
27 #include "pedigree/kernel/Log.h"
28 #include "pedigree/kernel/time/Time.h"
29 #include "pedigree/kernel/utilities/PointerGuard.h"
30 #include "pedigree/kernel/utilities/StaticString.h"
31 #include "pedigree/kernel/utilities/utility.h"
32 
33 class Filesystem;
34 
36  String name, uintptr_t inode_num, FatFilesystem *pFs, File *pParent,
37  FatFileInfo &info, uint32_t dirClus, uint32_t dirOffset)
38  : Directory(
39  name, LITTLE_TO_HOST32(info.accessedTime),
40  LITTLE_TO_HOST32(info.modifiedTime),
41  LITTLE_TO_HOST32(info.creationTime), inode_num,
42  static_cast<Filesystem *>(pFs), LITTLE_TO_HOST32(0), pParent),
43  m_DirClus(dirClus), m_DirOffset(dirOffset), m_Type(FAT16), m_BlockSize(0),
44  m_bRootDir(false), m_Lock(false), m_DirBlockSize(0)
45 {
46  uint32_t permissions = 0777;
47 
48  setPermissions(permissions);
49  setUid(LITTLE_TO_HOST16(0));
50  setGid(LITTLE_TO_HOST16(0));
51 
52  m_BlockSize = pFs->m_BlockSize;
53  m_Type = pFs->m_Type;
54 
55  setInode(inode_num);
56 }
57 
59 {
60 }
61 
62 void FatDirectory::setInode(uintptr_t inode)
63 {
64  FatFilesystem *pFs = static_cast<FatFilesystem *>(m_pFilesystem);
65  m_Inode = inode;
66  uintptr_t clus = m_Inode;
67 
68  m_DirBlockSize = m_BlockSize;
69 
70  m_bRootDir = false;
71  if (clus == 0 && m_Type != FAT32)
72  {
73  m_DirBlockSize = pFs->m_RootDirCount * pFs->m_Superblock.BPB_BytsPerSec;
74  m_bRootDir = true;
75  }
76  else if (pFs->m_Type == FAT32)
77  if (clus == pFs->m_Superblock32.BPB_RootClus)
78  m_bRootDir = true;
79 }
80 
81 bool FatDirectory::addEntry(String filename, File *pFile, size_t type)
82 {
83  FatFilesystem *pFs = static_cast<FatFilesystem *>(m_pFilesystem);
84 
85 #ifdef SUPERDEBUG
86  NOTICE("FatDirectory::addEntry(" << filename << ")");
87 #endif
88  LockGuard<Mutex> guard(m_Lock);
89 
90  // grab the first cluster of the parent directory
91  uint32_t clus = m_Inode;
92  uint8_t *buffer =
93  reinterpret_cast<uint8_t *>(pFs->readDirectoryPortion(clus));
94  PointerGuard<uint8_t> bufferGuard(buffer);
95  if (!buffer)
96  return false;
97 
98  // how many long filename entries does the filename require?
99  size_t numRequired = 1; // need *at least* one for the short filename entry
100  size_t fnLength = filename.length();
101 
102  // Dot and DotDot entries?
103  if (!StringCompareN(
104  static_cast<const char *>(filename), ".", filename.length()) ||
105  !StringCompareN(
106  static_cast<const char *>(filename), "..", filename.length()))
107  {
108  // Only one entry required for the dot/dotdot entries, all else get
109  // a free long filename entry.
110  }
111  else
112  {
113  // each long filename entry is 13 bytes of filename
114  size_t numSplit = (fnLength + 12) / 13;
115  numRequired += numSplit;
116  }
117 
118  size_t longFilenameOffset = fnLength;
119 
120  // find the first free element
121  bool spaceFound = false;
122  size_t offset = 0;
123  size_t consecutiveFree = 0;
124  while (true)
125  {
126  // Look for space in the directory
128  while (!spaceFound)
129  {
130  consecutiveFree = 0;
131  for (offset = 0; offset < m_BlockSize; offset += sizeof(Dir))
132  {
133  if (buffer[offset] == 0 || buffer[offset] == 0xE5)
134  {
135  consecutiveFree++;
136  }
137  else
138  consecutiveFree = 0;
139 
140  if (consecutiveFree == numRequired)
141  {
142  spaceFound = true;
143  break;
144  }
145  }
146 
147  // If space was found, quit
148  if (spaceFound)
149  break;
150 
151  // Root Directory check:
152  // If no space found for our file, and if not FAT32, the root
153  // directory is not resizeable so we have to fail
154  if (m_Type != FAT32 && clus == 0)
155  return false;
156 
157  // check the next cluster, add a new cluster if needed
158  uint32_t prev = clus;
159  clus = pFs->getClusterEntry(clus);
160 
161  if (pFs->isEof(clus))
162  {
163  uint32_t newClus = pFs->findFreeCluster();
164  if (!newClus)
165  return false;
166 
167  pFs->setClusterEntry(prev, newClus);
168  pFs->setClusterEntry(newClus, pFs->eofValue());
169 
170  clus = newClus;
171  }
172 
173  pFs->readCluster(clus, reinterpret_cast<uintptr_t>(buffer));
174  }
175 
176  {
177  // long filename entries first
178  if (numRequired)
179  {
180  size_t currOffset = offset - ((numRequired - 1) * sizeof(Dir));
181  size_t i;
182  for (i = 0; i < (numRequired - 1); i++)
183  {
184  // grab a pointer to the data
185  DirLongFilename *lfn = reinterpret_cast<DirLongFilename *>(
186  &buffer[currOffset]);
187  ByteSet(lfn, 0, sizeof(DirLongFilename));
188 
189  if (i == 0)
190  lfn->LDIR_Ord = 0x40 | (numRequired - 1);
191  else
192  lfn->LDIR_Ord = (numRequired - 1 - i);
193  lfn->LDIR_Attr = ATTR_LONG_NAME;
194 
195  // get the next 13 bytes
196  size_t nChars = 13;
197  if (longFilenameOffset >= 13)
198  longFilenameOffset -= nChars;
199  else
200  {
201  // longFilenameOffset is not bigger than 13, so it's the
202  // number of characters to copy
203  nChars = longFilenameOffset - 1;
204  longFilenameOffset = 0;
205  }
206 
207  size_t nOffset = longFilenameOffset;
208  size_t nWritten = 0;
209  size_t n;
210 
211  for (n = 0; n < 10; n += 2)
212  {
213  if (nWritten > nChars)
214  break;
215  lfn->LDIR_Name1[n] = filename[nOffset++];
216  nWritten++;
217  }
218  for (n = 0; n < 12; n += 2)
219  {
220  if (nWritten > nChars)
221  break;
222  lfn->LDIR_Name2[n] = filename[nOffset++];
223  nWritten++;
224  }
225  for (n = 0; n < 4; n += 2)
226  {
227  if (nWritten > nChars)
228  break;
229  lfn->LDIR_Name3[n] = filename[nOffset++];
230  nWritten++;
231  }
232 
233  currOffset += sizeof(Dir);
234  }
235  }
236 
237  // get a Dir struct for it so we can manipulate the data
238  Dir *ent = reinterpret_cast<Dir *>(&buffer[offset]);
239  ByteSet(ent, 0, sizeof(Dir));
240  ent->DIR_Attr = type ? ATTR_DIRECTORY : 0;
241 
242  String shortFilename = pFs->convertFilenameTo(filename);
243  MemoryCopy(
244  ent->DIR_Name, static_cast<const char *>(shortFilename), 11);
245  ent->DIR_FstClusLO = pFile->getInode() & 0xFFFF;
246  ent->DIR_FstClusHI = (pFile->getInode() >> 16) & 0xFFFF;
248 
249  pFs->writeDirectoryPortion(clus, buffer);
250 
251  if (pFile->isDirectory())
252  {
253  FatDirectory *fatDir = static_cast<FatDirectory *>(pFile);
254 
255  fatDir->setDirCluster(clus);
256  fatDir->setDirOffset(offset);
257  }
258  else if (pFile->isSymlink())
259  {
260  FatSymlink *fatLink = static_cast<FatSymlink *>(pFile);
261 
262  fatLink->setDirCluster(clus);
263  fatLink->setDirOffset(offset);
264  }
265  else
266  {
267  FatFile *fatFile = static_cast<FatFile *>(pFile);
268 
269  fatFile->setDirCluster(clus);
270  fatFile->setDirOffset(offset);
271  }
272 
273  // If the cache is *not yet* populated, don't add the entry to the
274  // cache. This allows cacheDirectoryContents to build the cache
275  // properly. We need to use the File::getName() method because it is
276  // now possible to add a directory entry with a name that does not
277  // match the VFS name (FAT symlinks).
278  if (isCachePopulated())
279  addDirectoryEntry(pFile->getName(), pFile);
280 
281 #ifdef SUPERDEBUG
282  NOTICE(
283  " -> FatFilesystem::addEntry(" << filename
284  << ") is successful");
285 #endif
286 
287  return true;
288  }
289  }
290 #ifdef SUPERDEBUG
291  NOTICE(
292  " -> FatFilesystem::addEntry(" << filename << ") is not successful");
293 #endif
294 }
295 
297 {
298  FatFilesystem *pFs = static_cast<FatFilesystem *>(m_pFilesystem);
299  FatDirectory *fatDir = static_cast<FatDirectory *>(pFile);
300  FatFile *fatFile = static_cast<FatFile *>(pFile);
301  FatSymlink *fatLink = static_cast<FatSymlink *>(pFile);
302  String filename = pFile->getName();
303  String real_filename(filename);
304 
305  // Adjust filename if we must.
306  if (pFile->isSymlink())
307  filename += symlinkSuffix();
308 
309  uint32_t dirClus, dirOffset;
310  if (pFile->isDirectory())
311  {
312  dirClus = fatDir->getDirCluster();
313  dirOffset = fatDir->getDirOffset();
314  }
315  else if (pFile->isSymlink())
316  {
317  dirClus = fatLink->getDirCluster();
318  dirOffset = fatLink->getDirOffset();
319  }
320  else
321  {
322  dirClus = fatFile->getDirCluster();
323  dirOffset = fatFile->getDirOffset();
324  }
325 
326  LockGuard<Mutex> guard(m_Lock);
327 
328  // First byte = 0xE5 means the file's been deleted.
329  Dir *dir = pFs->getDirectoryEntry(dirClus, dirOffset);
330  PointerGuard<Dir> dirGuard(dir);
331  if (!dir)
332  return false;
333  dir->DIR_Name[0] = 0xE5;
334 
335  // Check that we can actually use the previous directory entry!
336  if (dirOffset >= sizeof(Dir))
337  {
338  // The main entry is fixed, but there may be one or more long filename
339  // entries for this file...
340  size_t numLfnEntries = (filename.length() / 13) + 1;
341 
342  // Grab the first entry behind this one - check that it is in fact a LFN
343  // entry
344  Dir *dir_prev =
345  pFs->getDirectoryEntry(dirClus, dirOffset - sizeof(Dir));
346  PointerGuard<Dir> prevGuard(dir_prev);
347  if (!dir_prev)
348  return false;
349 
350  if ((dir_prev->DIR_Attr & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME)
351  {
352  // Previous entry is a long filename entry, so delete each entry
353  // This goes backwards up the list of LFN entries - if it finds an
354  // entry that does *not* match a standard LFN entry it'll dump a
355  // warning and break out.
356  for (size_t ent = 0; ent < numLfnEntries; ent++)
357  {
358  uint32_t bytesBack = sizeof(Dir) * (ent + 1);
359  if (bytesBack > dirOffset)
360  {
362  ERROR("LFN set crosses a cluster boundary!");
363  break;
364  }
365 
366  uint32_t newOffset = dirOffset - bytesBack;
367  Dir *lfn = pFs->getDirectoryEntry(dirClus, newOffset);
368  PointerGuard<Dir> lfnGuard(lfn);
369  if ((!lfn) ||
370  ((lfn->DIR_Attr & ATTR_LONG_NAME_MASK) != ATTR_LONG_NAME))
371  break;
372 
373  lfn->DIR_Name[0] = 0xE5;
374 
375  pFs->writeDirectoryEntry(lfn, dirClus, newOffset);
376  }
377  }
378  }
379 
380  pFs->writeDirectoryEntry(dir, dirClus, dirOffset);
381  if (isCachePopulated())
382  remove(real_filename);
383  return true;
384 }
385 
387 {
388  FatFilesystem *pFs = static_cast<FatFilesystem *>(m_pFilesystem);
389 
390  LockGuard<Mutex> guard(m_Lock);
391 
392  // first check that we're not working in the root directory - if so, handle
393  // . and .. for it
394  uint32_t clus = m_Inode;
395  uint32_t sz = m_DirBlockSize;
396  if (m_bRootDir)
397  {
398  NOTICE("Adding root directory");
399  FatFileInfo info;
400  info.creationTime = info.modifiedTime = info.accessedTime = 0;
402  String("."), new FatDirectory(String("."), m_Inode, pFs, 0, info));
404  }
405 
406  // read in the first cluster for this directory
407  uint8_t *buffer =
408  reinterpret_cast<uint8_t *>(pFs->readDirectoryPortion(clus));
409  PointerGuard<uint8_t> bufferGuard(buffer);
410  if (!buffer)
411  {
412  WARNING("FatDirectory::cacheDirectoryContents() - got a null buffer "
413  "from readDirectoryPortion!");
414  return;
415  }
416 
417  // moved this out of the main loop in case of a long filename set crossing a
418  // cluster boundary
419  NormalStaticString longFileName;
420  longFileName.clear();
421  int32_t longFileNameIndex = 0;
422  bool nextIsEnd =
423  false; // next entry is the short filename entry for this long filename
424 
425  size_t i, j = 0;
426  bool endOfDir = false;
427  while (true)
428  {
429  for (i = 0; i < sz; i += sizeof(Dir))
430  {
431  Dir *ent = reinterpret_cast<Dir *>(&buffer[i]);
432 
433  uint8_t firstChar = ent->DIR_Name[0];
434  if (firstChar == 0)
435  {
436  endOfDir = true;
437  break;
438  }
439 
440  if (firstChar == 0xE5)
441  {
442  // deleted file
443  longFileNameIndex = 0;
444  longFileName.clear();
445  nextIsEnd = false;
446  continue;
447  }
448 
449  if ((ent->DIR_Attr & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME)
450  {
451  if (firstChar & 0x40) // first LFN (ie, masked with 0x40 to
452  // state LAST_LONG_ENTRY
453  longFileNameIndex = firstChar ^ 0x40;
454 
455  char *entBuffer = reinterpret_cast<char *>(&buffer[i]);
456  char tmp[64];
457 
458  // probably a more efficient way of doing this somehow...
459  int a, b = 0;
460  for (a = 1; a < 11; a += 2)
461  tmp[b++] = entBuffer[a];
462  for (a = 14; a < 26; a += 2)
463  tmp[b++] = entBuffer[a];
464  for (a = 28; a < 32; a += 2)
465  tmp[b++] = entBuffer[a];
466 
467  tmp[b] = '\0';
468 
469  // hack to make long filenames work
470  NormalStaticString latterString = longFileName;
471  longFileName = tmp;
472  longFileName += latterString;
473 
474  // will be zero if the last entry
475  if ((longFileNameIndex == 0) || (--longFileNameIndex <= 1))
476  {
477  nextIsEnd = true;
478  }
479 
480  continue;
481  }
482 
483  uint8_t attr = ent->DIR_Attr;
484  if (attr != ATTR_VOLUME_ID)
485  {
486  uint32_t fileCluster =
487  ent->DIR_FstClusLO | (ent->DIR_FstClusHI << 16);
488  String filename;
489  if (nextIsEnd)
490  filename = static_cast<const char *>(
491  longFileName); // use the long filename rather than the
492  // short one
493  else
494  {
495  // WARNING("FAT: Using short filename rather than long
496  // filename");
497  filename = pFs->convertFilenameFrom(
498  String(reinterpret_cast<const char *>(ent->DIR_Name)));
499  }
500 
501  Time::Timestamp writeTime =
502  pFs->getUnixTimestamp(ent->DIR_WrtTime, ent->DIR_WrtDate);
503  Time::Timestamp accTime =
504  pFs->getUnixTimestamp(0, ent->DIR_LstAccDate);
505  Time::Timestamp createTime =
506  pFs->getUnixTimestamp(ent->DIR_CrtTime, ent->DIR_CrtDate);
507 
508  FatFileInfo info;
509  info.accessedTime = accTime;
510  info.modifiedTime = writeTime;
511  info.creationTime = createTime;
512 
513  File *pF;
514  if ((attr & ATTR_DIRECTORY) == ATTR_DIRECTORY)
515  pF = new FatDirectory(
516  filename, fileCluster, pFs, this, info);
517  else
518  {
519  if (filename.endswith(symlinkSuffix()))
520  {
521  // Remove the suffix (this is very ugly)
524  for (size_t z = 0; z < symlinkSuffix().length(); ++z)
525  filename.chomp();
526 
527  pF = new FatSymlink(
528  filename, accTime, writeTime, createTime,
529  fileCluster, pFs, ent->DIR_FileSize, clus, i, this);
530  }
531  else
532  {
533  pF = new FatFile(
534  filename, accTime, writeTime, createTime,
535  fileCluster, pFs, ent->DIR_FileSize, clus, i, this);
536  }
537  }
538 
539  // NOTICE("Inserting '" << filename << "'.");
540  addDirectoryEntry(filename, pF);
542  }
543 
544  longFileNameIndex = 0;
545  longFileName.clear();
546  nextIsEnd = false;
547  j++;
548  }
549 
550  if (endOfDir)
551  break;
552 
553  if (clus == 0 && m_Type != FAT32)
554  break; // not found
555 
556  // find the next cluster in the chain, if this is the end, break, if
557  // not, continue
558  clus = pFs->getClusterEntry(clus);
559  if (clus == 0)
560  break; // something broke!
561 
562  if (pFs->isEof(clus))
563  break;
564 
565  // continue by reading in this cluster
566  pFs->readCluster(clus, reinterpret_cast<uintptr_t>(buffer));
567  }
568 }
569 
571 {
572 }
void chomp()
Definition: String.cc:660
void writeDirectoryEntry(Dir *dir, uint32_t clus, uint32_t offset)
uint32_t setClusterEntry(uint32_t cluster, uint32_t value, bool bLock=true)
void fileAttributeChanged()
void writeDirectoryPortion(uint32_t clus, void *p)
virtual bool removeEntry(File *pFile)
Dir * getDirectoryEntry(uint32_t clus, uint32_t offset) const
uint32_t findFreeCluster(bool bLock=false)
virtual void setInode(uintptr_t inode)
Definition: FatDirectory.cc:62
Definition: String.h:49
uint32_t m_BlockSize
uint32_t getClusterEntry(uint32_t cluster, bool bLock=true)
void * readDirectoryPortion(uint32_t clus) const
void addDirectoryEntry(const String &name, File *pTarget)
Definition: Directory.cc:114
virtual bool isSymlink()
Definition: File.cc:431
#define WARNING(text)
Definition: Log.h:78
String convertFilenameFrom(String filename) const
bool readCluster(uint32_t block, uintptr_t buffer) const
#define NOTICE(text)
Definition: Log.h:74
FatDirectory(const FatDirectory &file)
Definition: ext2.h:175
virtual void cacheDirectoryContents()
String getName() const
Definition: File.cc:411
Superblock m_Superblock
uint32_t m_DirBlockSize
Definition: FatDirectory.h:100
String convertFilenameTo(String filename) const
#define ERROR(text)
Definition: Log.h:82
Time::Timestamp getUnixTimestamp(uint16_t time, uint16_t date) const
virtual bool isDirectory()
Definition: File.cc:436
bool isEof(uint32_t cluster) const
virtual bool addEntry(String filename, File *pFile, size_t type)
Definition: FatDirectory.cc:81
Definition: File.h:66
bool endswith(const char c) const
Definition: String.cc:710
uint32_t eofValue() const
void markCachePopulated()
Definition: Directory.h:187
virtual ~FatDirectory()
Definition: FatDirectory.cc:58
virtual bool isCachePopulated() const
Definition: Directory.h:90