The Pedigree Project  0.1
Filesystem.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 "Filesystem.h"
21 #include "Directory.h"
22 #include "File.h"
23 #include "Symlink.h"
24 #include "VFS.h"
25 #include "pedigree/kernel/Log.h"
26 #include "pedigree/kernel/process/Process.h"
27 #include "pedigree/kernel/process/Thread.h"
28 #include "pedigree/kernel/processor/Processor.h"
29 #include "pedigree/kernel/processor/ProcessorInformation.h"
30 #include "pedigree/kernel/syscallError.h"
31 #include "pedigree/kernel/utilities/LazyEvaluate.h"
32 #include "pedigree/kernel/utilities/StringView.h"
33 #include "pedigree/kernel/utilities/utility.h"
34 
35 Filesystem::Filesystem() : m_bReadOnly(false), m_pDisk(0), m_nAliases(0)
36 {
37 }
38 
39 Filesystem::~Filesystem() = default;
40 
42 {
43 #ifdef THREADS
44  Process *pProcess =
45  Processor::information().getCurrentThread()->getParent();
46  File *maybeRoot = pProcess->getRootFile();
47  if (maybeRoot)
48  return maybeRoot;
49 #endif
50  return getRoot();
51 }
52 
54 {
55  return findNode(getTrueRoot(), path);
56 }
57 
58 File *Filesystem::find(const String &path)
59 {
60  return findNode(getTrueRoot(), path.view());
61 }
62 
63 File *Filesystem::find(const StringView &path, File *pStartNode)
64 {
65  assert(pStartNode != nullptr);
66  File *a = findNode(pStartNode, path);
67  return a;
68 }
69 
70 File *Filesystem::find(const String &path, File *pStartNode)
71 {
72  return find(path.view(), pStartNode);
73 }
74 
76  const StringView &path, uint32_t mask, File *pStartNode)
77 {
78  if (!pStartNode)
79  pStartNode = getTrueRoot();
80  File *pFile = findNode(pStartNode, path);
81 
82  if (pFile)
83  {
84  SYSCALL_ERROR(FileExists);
85  return false;
86  }
87 
88  String filename;
89  File *pParent = findParent(path, pStartNode, filename);
90 
91  // Check the parent existed.
92  if (!pParent)
93  {
94  SYSCALL_ERROR(DoesNotExist);
95  return false;
96  }
97 
98  // Are we allowed to make the file?
99  if (!VFS::checkAccess(pParent, false, true, true))
100  {
101  return false;
102  }
103 
104  // May need to create on a different filesytem (if the traversal crossed
105  // over to a different fs)
106  Filesystem *pFs = pParent->getFilesystem();
107 
108  // Now make the file.
109  return pFs->createFile(pParent, filename, mask);
110 }
111 
113  const StringView &path, uint32_t mask, File *pStartNode)
114 {
115  if (!pStartNode)
116  pStartNode = getTrueRoot();
117  File *pFile = findNode(pStartNode, path);
118 
119  if (pFile)
120  {
121  SYSCALL_ERROR(FileExists);
122  return false;
123  }
124 
125  String filename;
126  File *pParent = findParent(path, pStartNode, filename);
127 
128  // Check the parent existed.
129  if (!pParent)
130  {
131  SYSCALL_ERROR(DoesNotExist);
132  return false;
133  }
134 
135  // Are we allowed to make the file?
136  if (!VFS::checkAccess(pParent, false, true, true))
137  {
138  return false;
139  }
140 
141  // May need to create on a different filesytem (if the traversal crossed
142  // over to a different fs)
143  Filesystem *pFs = pParent->getFilesystem();
144 
145  // Now make the directory.
146  return pFs->createDirectory(pParent, filename, mask);
147 }
148 
150  const StringView &path, const String &value, File *pStartNode)
151 {
152  if (!pStartNode)
153  pStartNode = getTrueRoot();
154  File *pFile = findNode(pStartNode, path);
155 
156  if (pFile)
157  {
158  SYSCALL_ERROR(FileExists);
159  return false;
160  }
161 
162  String filename;
163  File *pParent = findParent(path, pStartNode, filename);
164 
165  // Check the parent existed.
166  if (!pParent)
167  {
168  SYSCALL_ERROR(DoesNotExist);
169  return false;
170  }
171 
172  // Are we allowed to make the file?
173  if (!VFS::checkAccess(pParent, false, true, true))
174  {
175  return false;
176  }
177 
178  // May need to create on a different filesytem (if the traversal crossed
179  // over to a different fs)
180  Filesystem *pFs = pParent->getFilesystem();
181 
182  // Now make the symlink.
183  pFs->createSymlink(pParent, filename, value);
184 
185  return true;
186 }
187 
189  const StringView &path, File *target, File *pStartNode)
190 {
191  if (!pStartNode)
192  pStartNode = getTrueRoot();
193  File *pFile = findNode(pStartNode, path);
194 
195  if (pFile)
196  {
197  SYSCALL_ERROR(FileExists);
198  return false;
199  }
200 
201  String filename;
202  File *pParent = findParent(path, pStartNode, filename);
203 
204  // Check the parent existed.
205  if (!pParent)
206  {
207  SYSCALL_ERROR(DoesNotExist);
208  return false;
209  }
210 
211  // Are we allowed to make the file?
212  if (!VFS::checkAccess(pParent, false, true, true))
213  {
214  return false;
215  }
216 
217  // Links can't cross filesystems (symlinks can, though).
218  if (this != target->getFilesystem())
219  {
220  SYSCALL_ERROR(CrossDeviceLink);
221  return false;
222  }
223 
224  // May need to create on a different filesytem (if the traversal crossed
225  // over to a different fs)
226  Filesystem *pFs = pParent->getFilesystem();
227 
228  // Now make the symlink.
229  pFs->createLink(pParent, filename, target);
230 
231  return true;
232 }
233 
234 bool Filesystem::remove(const StringView &path, File *pStartNode)
235 {
236  if (!pStartNode)
237  pStartNode = getTrueRoot();
238 
239  File *pFile = findNode(pStartNode, path);
240 
241  if (!pFile)
242  {
243  SYSCALL_ERROR(DoesNotExist);
244  return false;
245  }
246 
247  String filename;
248  File *pParent = findParent(path, pStartNode, filename);
249 
250  // Check the parent existed.
251  if (!pParent)
252  {
253  FATAL("Filesystem::remove: Massive algorithmic error.");
254  return false;
255  }
256 
257  // Are we allowed to delete the file?
258  if (!VFS::checkAccess(pParent, false, true, true))
259  {
260  return false;
261  }
262 
263  Directory *pDParent = Directory::fromFile(pParent);
264  if (!pDParent)
265  {
266  FATAL("Filesystem::remove: Massive algorithmic error (2)");
267  return false;
268  }
269 
270  // May need to create on a different filesytem (if the traversal crossed
271  // over to a different fs)
272  Filesystem *pFs = pParent->getFilesystem();
273 
274  if (pFile->isDirectory())
275  {
276  Directory *removalDir = Directory::fromFile(pFile);
277  if (removalDir->getNumChildren())
278  {
279  if (removalDir->getNumChildren() > 2)
280  {
281  // There's definitely more than just . and .. here.
282  SYSCALL_ERROR(NotEmpty);
283  return false;
284  }
285 
286  // Are the entries only ., ..?
287  for (auto it : removalDir->getCache())
288  {
289  String name = (*it)->getName();
290  if (name != "." && name != "..")
291  {
292  SYSCALL_ERROR(NotEmpty);
293  return false;
294  }
295  }
296 
297  // Clean out the . and .. entries
298  if (!removalDir->empty())
299  {
300  // ?????
301  SYSCALL_ERROR(IoError);
302  return false;
303  }
304  }
305  }
306 
307  // Remove the file from disk & parent directory cache.
308  bool bRemoved = pFs->remove(pParent, pFile);
309  if (bRemoved)
310  {
311  pDParent->remove(filename);
312  }
313  return bRemoved;
314 }
315 
317 {
318  if (UNLIKELY(path.length() == 0))
319  {
320  return pNode;
321  }
322 
323  // If the pathname has a leading slash, cd to root and remove it.
324  else if (path[0] == '/')
325  {
326  pNode = getTrueRoot();
327  path = path.substring(1, path.length());
328  }
329 
330  // Grab the next filename component.
331  size_t i = 0;
332  size_t nExtra = 0;
333  while ((i < path.length()) && path[i] != '/')
334  {
335  i = path.nextCharacter(i);
336  }
337  while (i < path.length())
338  {
339  size_t n = path.nextCharacter(i);
340  if (n >= path.length())
341  {
342  break;
343  }
344  else if (path[n] == '/')
345  {
346  i = n;
347  ++nExtra;
348  }
349  else
350  {
351  break;
352  }
353  }
354 
355  StringView currentComponent = path.substring(0, i - nExtra);
356  StringView restOfPath =
357  path.substring(path.nextCharacter(i), path.length());
358 
359  // At this point 'currentComponent' contains the token to search for.
360  // 'restOfPath' contains the path for the next recursion (or nil).
361 
362  // If 'path' is zero-lengthed, ignore and recurse.
363  if (currentComponent.length() == 0)
364  {
365  return findNode(pNode, restOfPath);
366  }
367 
368  // Firstly, if the current node is a symlink, follow it.
370  while (pNode->isSymlink())
371  {
372  pNode = Symlink::fromFile(pNode)->followLink();
373  }
374 
375  // Next, if the current node isn't a directory, die.
376  if (!pNode->isDirectory())
377  {
378  SYSCALL_ERROR(NotADirectory);
379  return 0;
380  }
381 
382  bool dot = currentComponent == ".";
383  bool dotdot = currentComponent == "..";
384 
385  // '.' section, or '..' with no parent, or '..' and we're at the root.
386  if (dot || (dotdot && pNode->m_pParent == 0) ||
387  (dotdot && pNode == getTrueRoot()))
388  {
389  return findNode(pNode, restOfPath);
390  }
391  else if (dotdot)
392  {
393  return findNode(pNode->m_pParent, restOfPath);
394  }
395 
396  Directory *pDir = Directory::fromFile(pNode);
397  if (!pDir)
398  {
399  SYSCALL_ERROR(NotADirectory);
400  return 0;
401  }
402 
403  // Is this a reparse point? If so we need to change where we perform the
404  // next lookup.
405  Directory *reparse = pDir->getReparsePoint();
406  if (reparse)
407  {
408  WARNING(
409  "VFS: found reparse point at '" << pDir->getFullPath()
410  << "', following it (new target: "
411  << reparse->getFullPath() << ")");
412  pDir = reparse;
413  }
414 
415  // Are we allowed to access files in this directory?
416  if (!VFS::checkAccess(pNode, false, false, true))
417  {
418  return 0;
419  }
420 
421  // Cache lookup.
422  File *pFile;
423  if (!pDir->isCachePopulated())
424  {
425  // Directory contents not cached - cache them now.
426  pDir->cacheDirectoryContents();
427  }
428 
429  pFile = pDir->lookup(currentComponent);
430  if (pFile)
431  {
432  // Cache lookup succeeded, recurse and return.
433  return findNode(pFile, restOfPath);
434  }
435  else
436  {
437  // Cache lookup failed, does not exist.
438  return 0;
439  }
440 }
441 
442 File *
443 Filesystem::findParent(StringView path, File *pStartNode, String &filename)
444 {
445  // If the final character of the string is '/', this log falls apart. So,
446  // check for that and chomp it. But, we also need to not do that for e.g.
447  // path == '/'.
448  if (path.length() > 1 && path[path.length() - 1] == '/')
449  {
450  path = path.substring(0, path.length() - 1);
451  }
452 
453  // Work forwards to the end of the path string, attempting to find the last
454  // '/'.
455  ssize_t lastSlash = -1;
456  for (ssize_t i = path.length() - 1; i >= 0; i = path.prevCharacter(i))
457  {
458  if (path[i] == '/')
459  {
460  lastSlash = i;
461  break;
462  }
463  }
464 
465  // Now, if there were no slashes, the parent node is pStartNode.
466  File *parentNode = nullptr;
467  if (lastSlash == -1)
468  {
469  filename = path.toString();
470  parentNode = pStartNode;
471  }
472  else
473  {
474  // Else split the filename off from the rest of the path and follow it.
475  filename = path.substring(path.nextCharacter(lastSlash), path.length())
476  .toString();
477  path = path.substring(0, lastSlash);
478  parentNode = findNode(pStartNode, path);
479  }
480 
481  // Handle immediate parent node being a reparse point.
482  if (parentNode)
483  {
484  if (parentNode->isDirectory())
485  {
486  File *reparseNode =
487  Directory::fromFile(parentNode)->getReparsePoint();
488  if (reparseNode)
489  {
490  parentNode = reparseNode;
491  }
492  }
493  }
494 
495  return parentNode;
496 }
497 
498 bool Filesystem::createLink(File *parent, const String &filename, File *target)
499 {
500  // Default stubbed implementation, works for filesystems that can't handle
501  // hard links.
502  return false;
503 }
virtual ~Filesystem()
StringView substring(size_t start, size_t end, bool hashed=HASH_STRINGVIEWS_BY_DEFAULT) const
Definition: StringView.cc:131
File * getRootFile() const
Definition: Process.h:396
bool createLink(const StringView &path, File *target, File *pStartNode=0)
Definition: Filesystem.cc:188
static Directory * fromFile(File *pF)
Definition: Directory.h:50
bool createFile(const StringView &path, uint32_t mask, File *pStartNode=0)
Definition: Filesystem.cc:75
Definition: String.h:49
static ProcessorInformation & information()
Definition: Processor.cc:45
bool empty()
Definition: Directory.cc:180
File * findNode(File *pNode, StringView path)
Definition: Filesystem.cc:316
bool remove(const StringView &path, File *pStartNode=0)
Definition: Filesystem.cc:234
virtual bool isSymlink()
Definition: File.cc:431
#define WARNING(text)
Definition: Log.h:78
StringView view() const
Definition: String.cc:880
virtual File * find(const StringView &path)
Definition: Filesystem.cc:53
File * findParent(StringView path, File *pStartNode, String &filename)
Definition: Filesystem.cc:443
virtual String getFullPath(bool bWithLabel=true)
Definition: File.cc:718
#define assert(x)
Definition: assert.h:37
bool createDirectory(const StringView &path, uint32_t mask, File *pStartNode=0)
Definition: Filesystem.cc:112
File * getTrueRoot()
Definition: Filesystem.cc:41
virtual const DirectoryEntryCache & getCache()
Definition: Directory.h:181
void remove(const HashedStringView &s)
Definition: Directory.cc:102
size_t getNumChildren()
Definition: Directory.cc:74
virtual bool isDirectory()
Definition: File.cc:436
File * lookup(const HashedStringView &s) const
Definition: Directory.cc:89
#define FATAL(text)
Definition: Log.h:89
Directory * getReparsePoint() const
Get the reparse point attached to this directory. Reparse points allow locations on the filesystem to...
Definition: Directory.cc:149
virtual void cacheDirectoryContents()
Definition: Directory.cc:85
Definition: File.h:66
bool createSymlink(const StringView &path, const String &value, File *pStartNode=0)
Definition: Filesystem.cc:149
static bool checkAccess(File *pFile, bool bRead, bool bWrite, bool bExecute)
Definition: VFS.cc:448
virtual bool isCachePopulated() const
Definition: Directory.h:90
virtual File * getRoot() const =0