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 26 struct BMergedDirectory::EntryNameSet : std::set<std::string> { 27 }; 28 29 30 BMergedDirectory::BMergedDirectory(BPolicy policy) 31 : 32 BEntryList(), 33 fDirectories(10, true), 34 fPolicy(policy), 35 fDirectoryIndex(0), 36 fVisitedEntries(NULL) 37 { 38 } 39 40 41 BMergedDirectory::~BMergedDirectory() 42 { 43 delete fVisitedEntries; 44 } 45 46 47 status_t 48 BMergedDirectory::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 58 BMergedDirectory::BPolicy 59 BMergedDirectory::Policy() const 60 { 61 return fPolicy; 62 } 63 64 65 void 66 BMergedDirectory::SetPolicy(BPolicy policy) 67 { 68 fPolicy = policy; 69 } 70 71 72 status_t 73 BMergedDirectory::AddDirectory(BDirectory* directory) 74 { 75 return fDirectories.AddItem(directory) ? B_OK : B_NO_MEMORY; 76 } 77 78 79 status_t 80 BMergedDirectory::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 99 status_t 100 BMergedDirectory::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 111 status_t 112 BMergedDirectory::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 128 int32 129 BMergedDirectory::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 180 status_t 181 BMergedDirectory::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 194 int32 195 BMergedDirectory::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 205 bool 206 BMergedDirectory::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 215 void 216 BMergedDirectory::_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