vfs: check userland buffers before reading them.
[haiku.git] / src / kits / debugger / files / FileManager.cpp
blob9615b337ef1feceb8885362269e04268fe89a113
1 /*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2011-2017, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
7 #include "FileManager.h"
9 #include <new>
11 #include <AutoDeleter.h>
12 #include <AutoLocker.h>
14 #include "LocatableDirectory.h"
15 #include "LocatableFile.h"
16 #include "SourceFile.h"
17 #include "StringUtils.h"
18 #include "TeamFileManagerSettings.h"
21 // #pragma mark - EntryPath
24 struct FileManager::EntryPath {
25 const char* directory;
26 const char* name;
28 EntryPath(const char* directory, const char* name)
30 directory(directory),
31 name(name)
35 EntryPath(const BString& directory, const BString& name)
37 directory(directory.Length() > 0 ? directory.String() : NULL),
38 name(name.String())
42 EntryPath(const LocatableEntry* entry)
44 directory(NULL),
45 name(entry->Name())
47 LocatableDirectory* parent = entry->Parent();
48 if (parent != NULL && strlen(parent->Path()) > 0)
49 directory = parent->Path();
52 EntryPath(const EntryPath& other)
54 directory(other.directory),
55 name(other.name)
59 size_t HashValue() const
61 return StringUtils::HashValue(directory)
62 ^ StringUtils::HashValue(name);
65 bool operator==(const EntryPath& other) const
67 if (directory != other.directory
68 && (directory == NULL || other.directory == NULL
69 || strcmp(directory, other.directory) != 0)) {
70 return false;
73 return strcmp(name, other.name) == 0;
78 // #pragma mark - EntryHashDefinition
81 struct FileManager::EntryHashDefinition {
82 typedef EntryPath KeyType;
83 typedef LocatableEntry ValueType;
85 size_t HashKey(const EntryPath& key) const
87 return key.HashValue();
90 size_t Hash(const LocatableEntry* value) const
92 return HashKey(EntryPath(value));
95 bool Compare(const EntryPath& key, const LocatableEntry* value) const
97 return EntryPath(value) == key;
100 LocatableEntry*& GetLink(LocatableEntry* value) const
102 return value->fNext;
107 // #pragma mark - Domain
110 class FileManager::Domain : private LocatableEntryOwner {
111 public:
112 Domain(FileManager* manager, bool isLocal)
114 fManager(manager),
115 fIsLocal(isLocal)
119 ~Domain()
121 LocatableEntry* entry = fEntries.Clear(true);
122 while (entry != NULL) {
123 LocatableEntry* next = entry->fNext;
124 entry->ReleaseReference();
125 entry = next;
128 while ((entry = fDeadEntries.RemoveHead()) != NULL)
129 entry->ReleaseReference();
132 status_t Init()
134 status_t error = fEntries.Init();
135 if (error != B_OK)
136 return error;
138 return B_OK;
141 LocatableFile* GetFile(const BString& directoryPath,
142 const BString& relativePath)
144 if (directoryPath.Length() == 0 || relativePath[0] == '/')
145 return GetFile(relativePath);
146 return GetFile(BString(directoryPath) << '/' << relativePath);
149 LocatableFile* GetFile(const BString& path)
151 BString directoryPath;
152 BString name;
153 _SplitPath(path, directoryPath, name);
154 LocatableFile* file = _GetFile(directoryPath, name);
155 if (file == NULL)
156 return NULL;
158 // try to auto-locate the file
159 if (LocatableDirectory* directory = file->Parent()) {
160 if (directory->State() == LOCATABLE_ENTRY_UNLOCATED) {
161 // parent not yet located -- try locate with the entry's path
162 BString path;
163 file->GetPath(path);
164 _LocateEntry(file, path, true, true);
165 } else {
166 // parent already located -- locate the entry in the parent
167 BString locatedDirectoryPath;
168 if (directory->GetLocatedPath(locatedDirectoryPath))
169 _LocateEntryInParentDir(file, locatedDirectoryPath, true);
173 return file;
176 void EntryLocated(const BString& path, const BString& locatedPath)
178 BString directory;
179 BString name;
180 _SplitPath(path, directory, name);
182 LocatableEntry* entry = _LookupEntry(EntryPath(directory, name));
183 if (entry == NULL)
184 return;
186 _LocateEntry(entry, locatedPath, false, true);
189 private:
190 virtual bool Lock()
192 return fManager->Lock();
195 virtual void Unlock()
197 fManager->Unlock();
200 virtual void LocatableEntryUnused(LocatableEntry* entry)
202 AutoLocker<FileManager> lock(fManager);
203 if (fEntries.Lookup(EntryPath(entry)) == entry)
204 fEntries.Remove(entry);
205 else {
206 DeadEntryList::Iterator iterator = fDeadEntries.GetIterator();
207 while (iterator.HasNext()) {
208 if (iterator.Next() == entry) {
209 fDeadEntries.Remove(entry);
210 break;
215 LocatableDirectory* parent = entry->Parent();
216 if (parent != NULL)
217 parent->RemoveEntry(entry);
219 delete entry;
222 bool _LocateDirectory(LocatableDirectory* directory,
223 const BString& locatedPath, bool implicit)
225 if (directory == NULL
226 || directory->State() != LOCATABLE_ENTRY_UNLOCATED) {
227 return false;
230 if (!_LocateEntry(directory, locatedPath, implicit, true))
231 return false;
233 _LocateEntries(directory, locatedPath, implicit);
235 return true;
238 bool _LocateEntry(LocatableEntry* entry, const BString& locatedPath,
239 bool implicit, bool locateAncestors)
241 if (implicit && entry->State() == LOCATABLE_ENTRY_LOCATED_EXPLICITLY)
242 return false;
244 struct stat st;
245 if (stat(locatedPath, &st) != 0)
246 return false;
248 if (S_ISDIR(st.st_mode)) {
249 LocatableDirectory* directory
250 = dynamic_cast<LocatableDirectory*>(entry);
251 if (directory == NULL)
252 return false;
253 entry->SetLocatedPath(locatedPath, implicit);
254 } else if (S_ISREG(st.st_mode)) {
255 LocatableFile* file = dynamic_cast<LocatableFile*>(entry);
256 if (file == NULL)
257 return false;
258 entry->SetLocatedPath(locatedPath, implicit);
261 // locate the ancestor directories, if requested
262 if (locateAncestors) {
263 BString locatedDirectory;
264 BString locatedName;
265 _SplitPath(locatedPath, locatedDirectory, locatedName);
266 if (locatedName == entry->Name())
267 _LocateDirectory(entry->Parent(), locatedDirectory, implicit);
270 return true;
273 bool _LocateEntryInParentDir(LocatableEntry* entry,
274 const BString& locatedDirectoryPath, bool implicit)
276 // construct the located entry path
277 BString locatedEntryPath(locatedDirectoryPath);
278 int32 pathLength = locatedEntryPath.Length();
279 if (pathLength >= 1 && locatedEntryPath[pathLength - 1] != '/')
280 locatedEntryPath << '/';
281 locatedEntryPath << entry->Name();
283 return _LocateEntry(entry, locatedEntryPath, implicit, false);
286 void _LocateEntries(LocatableDirectory* directory,
287 const BString& locatedPath, bool implicit)
289 for (LocatableEntryList::ConstIterator it
290 = directory->Entries().GetIterator();
291 LocatableEntry* entry = it.Next();) {
292 if (entry->State() == LOCATABLE_ENTRY_LOCATED_EXPLICITLY)
293 continue;
295 if (_LocateEntryInParentDir(entry, locatedPath, implicit)) {
296 // recurse for directories
297 if (LocatableDirectory* subDir
298 = dynamic_cast<LocatableDirectory*>(entry)) {
299 BString locatedEntryPath;
300 if (subDir->GetLocatedPath(locatedEntryPath))
301 _LocateEntries(subDir, locatedEntryPath, implicit);
307 LocatableFile* _GetFile(const BString& directoryPath, const BString& name)
309 // if already known return the file
310 LocatableEntry* entry = _LookupEntry(EntryPath(directoryPath, name));
311 if (entry != NULL) {
312 LocatableFile* file = dynamic_cast<LocatableFile*>(entry);
313 if (file == NULL)
314 return NULL;
316 if (file->AcquireReference() == 0) {
317 fEntries.Remove(file);
318 fDeadEntries.Insert(file);
319 } else
320 return file;
323 // no such file yet -- create it
324 BString normalizedDirPath;
325 _NormalizePath(directoryPath, normalizedDirPath);
326 LocatableDirectory* directory = _GetDirectory(normalizedDirPath);
327 if (directory == NULL)
328 return NULL;
330 LocatableFile* file = new(std::nothrow) LocatableFile(this, directory,
331 name);
332 if (file == NULL) {
333 directory->ReleaseReference();
334 return NULL;
337 directory->AddEntry(file);
339 fEntries.Insert(file);
340 return file;
343 LocatableDirectory* _GetDirectory(const BString& path)
345 BString directoryPath;
346 BString fileName;
347 _SplitNormalizedPath(path, directoryPath, fileName);
349 // if already know return the directory
350 LocatableEntry* entry
351 = _LookupEntry(EntryPath(directoryPath, fileName));
352 if (entry != NULL) {
353 LocatableDirectory* directory
354 = dynamic_cast<LocatableDirectory*>(entry);
355 if (directory == NULL)
356 return NULL;
357 directory->AcquireReference();
358 return directory;
361 // get the parent directory
362 LocatableDirectory* parentDirectory = NULL;
363 if (directoryPath.Length() > 0) {
364 parentDirectory = _GetDirectory(directoryPath);
365 if (parentDirectory == NULL)
366 return NULL;
369 // create a new directory
370 LocatableDirectory* directory = new(std::nothrow) LocatableDirectory(
371 this, parentDirectory, path);
372 if (directory == NULL) {
373 parentDirectory->ReleaseReference();
374 return NULL;
377 // auto-locate, if possible
378 if (fIsLocal) {
379 BString dirPath;
380 directory->GetPath(dirPath);
381 directory->SetLocatedPath(dirPath, false);
382 } else if (parentDirectory != NULL
383 && parentDirectory->State() != LOCATABLE_ENTRY_UNLOCATED) {
384 BString locatedDirectoryPath;
385 if (parentDirectory->GetLocatedPath(locatedDirectoryPath))
386 _LocateEntryInParentDir(directory, locatedDirectoryPath, true);
389 if (parentDirectory != NULL)
390 parentDirectory->AddEntry(directory);
392 fEntries.Insert(directory);
393 return directory;
396 LocatableEntry* _LookupEntry(const EntryPath& entryPath)
398 LocatableEntry* entry = fEntries.Lookup(entryPath);
399 if (entry == NULL)
400 return NULL;
402 // if already unreferenced, remove it
403 if (entry->CountReferences() == 0) {
404 fEntries.Remove(entry);
405 return NULL;
408 return entry;
411 void _NormalizePath(const BString& path, BString& _normalizedPath)
413 BString normalizedPath;
414 char* buffer = normalizedPath.LockBuffer(path.Length());
415 int32 outIndex = 0;
416 const char* remaining = path.String();
418 while (*remaining != '\0') {
419 // collapse repeated slashes
420 if (*remaining == '/') {
421 buffer[outIndex++] = '/';
422 remaining++;
423 while (*remaining == '/')
424 remaining++;
427 if (*remaining == '\0') {
428 // remove trailing slash (unless it's "/" only)
429 if (outIndex > 1)
430 outIndex--;
431 break;
434 // skip "." components
435 if (*remaining == '.') {
436 if (remaining[1] == '\0')
437 break;
439 if (remaining[1] == '/') {
440 remaining += 2;
441 while (*remaining == '/')
442 remaining++;
443 continue;
447 // copy path component
448 while (*remaining != '\0' && *remaining != '/')
449 buffer[outIndex++] = *(remaining++);
452 // If the path didn't change, use the original path (BString's copy on
453 // write mechanism) rather than the new string.
454 if (outIndex == path.Length()) {
455 _normalizedPath = path;
456 } else {
457 normalizedPath.UnlockBuffer(outIndex);
458 _normalizedPath = normalizedPath;
462 void _SplitPath(const BString& path, BString& _directory, BString& _name)
464 BString normalized;
465 _NormalizePath(path, normalized);
466 _SplitNormalizedPath(normalized, _directory, _name);
469 void _SplitNormalizedPath(const BString& path, BString& _directory,
470 BString& _name)
472 // handle single component (including root dir) cases
473 int32 lastSlash = path.FindLast('/');
474 if (lastSlash < 0 || path.Length() == 1) {
475 _directory = (const char*)NULL;
476 _name = path;
477 return;
480 // handle root dir + one component and multi component cases
481 if (lastSlash == 0)
482 _directory = "/";
483 else
484 _directory.SetTo(path, lastSlash);
485 _name = path.String() + (lastSlash + 1);
488 private:
489 FileManager* fManager;
490 LocatableEntryTable fEntries;
491 DeadEntryList fDeadEntries;
492 bool fIsLocal;
496 // #pragma mark - SourceFileEntry
499 struct FileManager::SourceFileEntry : public SourceFileOwner {
501 FileManager* manager;
502 BString path;
503 SourceFile* file;
504 SourceFileEntry* next;
506 SourceFileEntry(FileManager* manager, const BString& path)
508 manager(manager),
509 path(path),
510 file(NULL)
514 virtual void SourceFileUnused(SourceFile* sourceFile)
516 manager->_SourceFileUnused(this);
519 virtual void SourceFileDeleted(SourceFile* sourceFile)
521 // We have already been removed from the table, so commit suicide.
522 delete this;
527 // #pragma mark - SourceFileHashDefinition
530 struct FileManager::SourceFileHashDefinition {
531 typedef BString KeyType;
532 typedef SourceFileEntry ValueType;
534 size_t HashKey(const BString& key) const
536 return StringUtils::HashValue(key);
539 size_t Hash(const SourceFileEntry* value) const
541 return HashKey(value->path);
544 bool Compare(const BString& key, const SourceFileEntry* value) const
546 return value->path == key;
549 SourceFileEntry*& GetLink(SourceFileEntry* value) const
551 return value->next;
556 // #pragma mark - FileManager
559 FileManager::FileManager()
561 fLock("file manager"),
562 fTargetDomain(NULL),
563 fSourceDomain(NULL),
564 fSourceFiles(NULL)
569 FileManager::~FileManager()
571 delete fTargetDomain;
572 delete fSourceDomain;
574 SourceFileEntry* entry = fSourceFiles->Clear();
575 while (entry != NULL) {
576 SourceFileEntry* next = entry->next;
577 delete entry;
578 next = entry;
580 delete fSourceFiles;
584 status_t
585 FileManager::Init(bool targetIsLocal)
587 status_t error = fLock.InitCheck();
588 if (error != B_OK)
589 return error;
591 // create target domain
592 fTargetDomain = new(std::nothrow) Domain(this, targetIsLocal);
593 if (fTargetDomain == NULL)
594 return B_NO_MEMORY;
596 error = fTargetDomain->Init();
597 if (error != B_OK)
598 return error;
600 // create source domain
601 fSourceDomain = new(std::nothrow) Domain(this, false);
602 if (fSourceDomain == NULL)
603 return B_NO_MEMORY;
605 error = fSourceDomain->Init();
606 if (error != B_OK)
607 return error;
609 // create source file table
610 fSourceFiles = new(std::nothrow) SourceFileTable;
611 if (fSourceFiles == NULL)
612 return B_NO_MEMORY;
614 error = fSourceFiles->Init();
615 if (error != B_OK)
616 return error;
618 return B_OK;
622 LocatableFile*
623 FileManager::GetTargetFile(const BString& directory,
624 const BString& relativePath)
626 AutoLocker<FileManager> locker(this);
627 return fTargetDomain->GetFile(directory, relativePath);
631 LocatableFile*
632 FileManager::GetTargetFile(const BString& path)
634 AutoLocker<FileManager> locker(this);
635 return fTargetDomain->GetFile(path);
639 void
640 FileManager::TargetEntryLocated(const BString& path,
641 const BString& locatedPath)
643 AutoLocker<FileManager> locker(this);
644 fTargetDomain->EntryLocated(path, locatedPath);
648 LocatableFile*
649 FileManager::GetSourceFile(const BString& directory,
650 const BString& relativePath)
652 AutoLocker<FileManager> locker(this);
653 LocatableFile* file = fSourceDomain->GetFile(directory, relativePath);
655 return file;
659 LocatableFile*
660 FileManager::GetSourceFile(const BString& path)
662 AutoLocker<FileManager> locker(this);
663 LocatableFile* file = fSourceDomain->GetFile(path);
665 return file;
669 status_t
670 FileManager::SourceEntryLocated(const BString& path,
671 const BString& locatedPath)
673 AutoLocker<FileManager> locker(this);
675 // check if we already have this path mapped. If so,
676 // first clear the mapping, as the user may be attempting
677 // to correct an existing entry.
678 SourceFileEntry* entry = _LookupSourceFile(path);
679 if (entry != NULL)
680 _SourceFileUnused(entry);
682 fSourceDomain->EntryLocated(path, locatedPath);
684 try {
685 fSourceLocationMappings[path] = locatedPath;
686 } catch (...) {
687 return B_NO_MEMORY;
690 return B_OK;
694 status_t
695 FileManager::LoadSourceFile(LocatableFile* file, SourceFile*& _sourceFile)
697 AutoLocker<FileManager> locker(this);
699 // get the path
700 BString path;
701 BString originalPath;
702 file->GetPath(originalPath);
703 if (!file->GetLocatedPath(path)) {
704 // see if this is a file we have a lazy mapping for.
705 if (!_LocateFileIfMapped(originalPath, file)
706 || !file->GetLocatedPath(path)) {
707 return B_ENTRY_NOT_FOUND;
711 // we might already know the source file
712 SourceFileEntry* entry = _LookupSourceFile(originalPath);
713 if (entry != NULL) {
714 entry->file->AcquireReference();
715 _sourceFile = entry->file;
716 return B_OK;
719 // create the hash table entry
720 entry = new(std::nothrow) SourceFileEntry(this, originalPath);
721 if (entry == NULL)
722 return B_NO_MEMORY;
724 // load the file
725 SourceFile* sourceFile = new(std::nothrow) SourceFile(entry);
726 if (sourceFile == NULL) {
727 delete entry;
728 return B_NO_MEMORY;
730 ObjectDeleter<SourceFile> sourceFileDeleter(sourceFile);
732 entry->file = sourceFile;
734 status_t error = sourceFile->Init(path);
735 if (error != B_OK)
736 return error;
738 fSourceFiles->Insert(entry);
740 _sourceFile = sourceFileDeleter.Detach();
741 return B_OK;
745 status_t
746 FileManager::LoadLocationMappings(TeamFileManagerSettings* settings)
748 AutoLocker<FileManager> locker(this);
749 for (int32 i = 0; i < settings->CountSourceMappings(); i++) {
750 BString sourcePath;
751 BString locatedPath;
753 if (settings->GetSourceMappingAt(i, sourcePath, locatedPath) != B_OK)
754 return B_NO_MEMORY;
756 try {
757 fSourceLocationMappings[sourcePath] = locatedPath;
758 } catch (...) {
759 return B_NO_MEMORY;
763 return B_OK;
767 status_t
768 FileManager::SaveLocationMappings(TeamFileManagerSettings* settings)
770 AutoLocker<FileManager> locker(this);
772 for (LocatedFileMap::const_iterator it = fSourceLocationMappings.begin();
773 it != fSourceLocationMappings.end(); ++it) {
774 status_t error = settings->AddSourceMapping(it->first, it->second);
775 if (error != B_OK)
776 return error;
779 return B_OK;
783 FileManager::SourceFileEntry*
784 FileManager::_LookupSourceFile(const BString& path)
786 SourceFileEntry* entry = fSourceFiles->Lookup(path);
787 if (entry == NULL)
788 return NULL;
790 // the entry might be unused already -- in that case remove it
791 if (entry->file->CountReferences() == 0) {
792 fSourceFiles->Remove(entry);
793 return NULL;
796 return entry;
800 void
801 FileManager::_SourceFileUnused(SourceFileEntry* entry)
803 AutoLocker<FileManager> locker(this);
805 SourceFileEntry* otherEntry = fSourceFiles->Lookup(entry->path);
806 if (otherEntry == entry)
807 fSourceFiles->Remove(entry);
811 bool
812 FileManager::_LocateFileIfMapped(const BString& sourcePath,
813 LocatableFile* file)
815 // called with lock held
817 LocatedFileMap::const_iterator it = fSourceLocationMappings.find(
818 sourcePath);
819 if (it != fSourceLocationMappings.end()
820 && file->State() != LOCATABLE_ENTRY_LOCATED_EXPLICITLY
821 && file->State() != LOCATABLE_ENTRY_LOCATED_IMPLICITLY) {
822 fSourceDomain->EntryLocated(it->first, it->second);
823 return true;
826 return false;