1/* 2 * Copyright 2013, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 */ 8 9 10#include <MergedDirectory.h> 11 12#include <string.h> 13 14#include <new> 15#include <set> 16#include <string> 17 18#include <Directory.h> 19#include <Entry.h> 20 21#include <AutoDeleter.h> 22 23#include "storage_support.h" 24 25 26struct BMergedDirectory::EntryNameSet : std::set<std::string> { 27}; 28 29 30BMergedDirectory::BMergedDirectory(BPolicy policy) 31 : 32 BEntryList(), 33 fDirectories(10, true), 34 fPolicy(policy), 35 fDirectoryIndex(0), 36 fVisitedEntries(NULL) 37{ 38} 39 40 41BMergedDirectory::~BMergedDirectory() 42{ 43 delete fVisitedEntries; 44} 45 46 47status_t 48BMergedDirectory::Init() 49{ 50 delete fVisitedEntries; 51 fDirectories.MakeEmpty(true); 52 53 fVisitedEntries = new(std::nothrow) EntryNameSet; 54 return fVisitedEntries != NULL ? B_OK : B_NO_MEMORY; 55} 56 57 58BMergedDirectory::BPolicy 59BMergedDirectory::Policy() const 60{ 61 return fPolicy; 62} 63 64 65void 66BMergedDirectory::SetPolicy(BPolicy policy) 67{ 68 fPolicy = policy; 69} 70 71 72status_t 73BMergedDirectory::AddDirectory(BDirectory* directory) 74{ 75 return fDirectories.AddItem(directory) ? B_OK : B_NO_MEMORY; 76} 77 78 79status_t 80BMergedDirectory::AddDirectory(const char* path) 81{ 82 BDirectory* directory = new(std::nothrow) BDirectory(path); 83 if (directory == NULL) 84 return B_NO_MEMORY; 85 ObjectDeleter<BDirectory> directoryDeleter(directory); 86 87 if (directory->InitCheck() != B_OK) 88 return directory->InitCheck(); 89 90 status_t error = AddDirectory(directory); 91 if (error != B_OK) 92 return error; 93 directoryDeleter.Detach(); 94 95 return B_OK; 96} 97 98 99status_t 100BMergedDirectory::GetNextEntry(BEntry* entry, bool traverse) 101{ 102 entry_ref ref; 103 status_t error = GetNextRef(&ref); 104 if (error != B_OK) 105 return error; 106 107 return entry->SetTo(&ref, traverse); 108} 109 110 111status_t 112BMergedDirectory::GetNextRef(entry_ref* ref) 113{ 114 BPrivate::Storage::LongDirEntry longEntry; 115 struct dirent* entry = longEntry.dirent(); 116 int32 result = GetNextDirents(entry, sizeof(longEntry), 1); 117 if (result < 0) 118 return result; 119 if (result == 0) 120 return B_ENTRY_NOT_FOUND; 121 122 ref->device = entry->d_pdev; 123 ref->directory = entry->d_pino; 124 return ref->set_name(entry->d_name); 125} 126 127 128int32 129BMergedDirectory::GetNextDirents(struct dirent* direntBuffer, size_t bufferSize, 130 int32 maxEntries) 131{ 132 if (maxEntries <= 0) 133 return B_BAD_VALUE; 134 135 while (fDirectoryIndex < fDirectories.CountItems()) { 136 int32 count = fDirectories.ItemAt(fDirectoryIndex)->GetNextDirents( 137 direntBuffer, bufferSize, 1); 138 if (count < 0) 139 return count; 140 if (count == 0) { 141 fDirectoryIndex++; 142 continue; 143 } 144 145 if (strcmp(direntBuffer->d_name, ".") == 0 146 || strcmp(direntBuffer->d_name, "..") == 0) { 147 continue; 148 } 149 150 switch (fPolicy) { 151 case B_ALLOW_DUPLICATES: 152 return count; 153 154 case B_ALWAYS_FIRST: 155 case B_COMPARE: 156 if (fVisitedEntries != NULL 157 && fVisitedEntries->find(direntBuffer->d_name) 158 != fVisitedEntries->end()) { 159 continue; 160 } 161 162 if (fVisitedEntries != NULL) { 163 try { 164 fVisitedEntries->insert(direntBuffer->d_name); 165 } catch (std::bad_alloc&) { 166 return B_NO_MEMORY; 167 } 168 } 169 170 if (fPolicy == B_COMPARE) 171 _FindBestEntry(direntBuffer); 172 return 1; 173 } 174 } 175 176 return 0; 177} 178 179 180status_t 181BMergedDirectory::Rewind() 182{ 183 for (int32 i = 0; BDirectory* directory = fDirectories.ItemAt(i); i++) 184 directory->Rewind(); 185 186 if (fVisitedEntries != NULL) 187 fVisitedEntries->clear(); 188 189 fDirectoryIndex = 0; 190 return B_OK; 191} 192 193 194int32 195BMergedDirectory::CountEntries() 196{ 197 int32 count = 0; 198 char buffer[sizeof(dirent) + B_FILE_NAME_LENGTH]; 199 while (GetNextDirents((dirent*)&buffer, sizeof(buffer), 1) == 1) 200 count++; 201 return count; 202} 203 204 205bool 206BMergedDirectory::ShallPreferFirstEntry(const entry_ref& entry1, int32 index1, 207 const entry_ref& entry2, int32 index2) 208{ 209 // That's basically B_ALWAYS_FIRST semantics. A derived class will implement 210 // the desired semantics. 211 return true; 212} 213 214 215void 216BMergedDirectory::_FindBestEntry(dirent* direntBuffer) 217{ 218 entry_ref bestEntry(direntBuffer->d_pdev, direntBuffer->d_pino, 219 direntBuffer->d_name); 220 if (bestEntry.name == NULL) 221 return; 222 int32 bestIndex = fDirectoryIndex; 223 224 int32 directoryCount = fDirectories.CountItems(); 225 for (int32 i = fDirectoryIndex + 1; i < directoryCount; i++) { 226 BEntry entry(fDirectories.ItemAt(i), bestEntry.name); 227 struct stat st; 228 entry_ref ref; 229 if (entry.GetStat(&st) == B_OK && entry.GetRef(&ref) == B_OK 230 && !ShallPreferFirstEntry(bestEntry, bestIndex, ref, i)) { 231 direntBuffer->d_pdev = ref.device; 232 direntBuffer->d_pino = ref.directory; 233 direntBuffer->d_dev = st.st_dev; 234 direntBuffer->d_ino = st.st_ino; 235 bestEntry.device = ref.device; 236 bestEntry.directory = ref.directory; 237 bestIndex = i; 238 } 239 } 240} 241