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 entry; 115 int32 result = GetNextDirents(&entry, sizeof(entry), 1); 116 if (result < 0) 117 return result; 118 if (result == 0) 119 return B_ENTRY_NOT_FOUND; 120 121 ref->device = entry.d_pdev; 122 ref->directory = entry.d_pino; 123 return ref->set_name(entry.d_name); 124 } 125 126 127 int32 128 BMergedDirectory::GetNextDirents(struct dirent* direntBuffer, size_t bufferSize, 129 int32 maxEntries) 130 { 131 if (maxEntries <= 0) 132 return B_BAD_VALUE; 133 134 while (fDirectoryIndex < fDirectories.CountItems()) { 135 int32 count = fDirectories.ItemAt(fDirectoryIndex)->GetNextDirents( 136 direntBuffer, bufferSize, 1); 137 if (count < 0) 138 return count; 139 if (count == 0) { 140 fDirectoryIndex++; 141 continue; 142 } 143 144 if (strcmp(direntBuffer->d_name, ".") == 0 145 || strcmp(direntBuffer->d_name, "..") == 0) { 146 continue; 147 } 148 149 switch (fPolicy) { 150 case B_ALLOW_DUPLICATES: 151 return count; 152 153 case B_ALWAYS_FIRST: 154 case B_COMPARE: 155 if (fVisitedEntries != NULL 156 && fVisitedEntries->find(direntBuffer->d_name) 157 != fVisitedEntries->end()) { 158 continue; 159 } 160 161 if (fVisitedEntries != NULL) { 162 try { 163 fVisitedEntries->insert(direntBuffer->d_name); 164 } catch (std::bad_alloc&) { 165 return B_NO_MEMORY; 166 } 167 } 168 169 if (fPolicy == B_COMPARE) 170 _FindBestEntry(direntBuffer); 171 return 1; 172 } 173 } 174 175 return 0; 176 } 177 178 179 status_t 180 BMergedDirectory::Rewind() 181 { 182 for (int32 i = 0; BDirectory* directory = fDirectories.ItemAt(i); i++) 183 directory->Rewind(); 184 185 if (fVisitedEntries != NULL) 186 fVisitedEntries->clear(); 187 188 fDirectoryIndex = 0; 189 return B_OK; 190 } 191 192 193 int32 194 BMergedDirectory::CountEntries() 195 { 196 int32 count = 0; 197 char buffer[sizeof(dirent) + B_FILE_NAME_LENGTH]; 198 while (GetNextDirents((dirent*)&buffer, sizeof(buffer), 1) == 1) 199 count++; 200 return count; 201 } 202 203 204 bool 205 BMergedDirectory::ShallPreferFirstEntry(const entry_ref& entry1, int32 index1, 206 const entry_ref& entry2, int32 index2) 207 { 208 // That's basically B_ALWAYS_FIRST semantics. A derived class will implement 209 // the desired semantics. 210 return true; 211 } 212 213 214 void 215 BMergedDirectory::_FindBestEntry(dirent* direntBuffer) 216 { 217 entry_ref bestEntry(direntBuffer->d_pdev, direntBuffer->d_pino, 218 direntBuffer->d_name); 219 if (bestEntry.name == NULL) 220 return; 221 int32 bestIndex = fDirectoryIndex; 222 223 int32 directoryCount = fDirectories.CountItems(); 224 for (int32 i = fDirectoryIndex + 1; i < directoryCount; i++) { 225 BEntry entry(fDirectories.ItemAt(i), bestEntry.name); 226 struct stat st; 227 entry_ref ref; 228 if (entry.GetStat(&st) == B_OK && entry.GetRef(&ref) == B_OK 229 && !ShallPreferFirstEntry(bestEntry, bestIndex, ref, i)) { 230 direntBuffer->d_pdev = ref.device; 231 direntBuffer->d_pino = ref.directory; 232 direntBuffer->d_dev = st.st_dev; 233 direntBuffer->d_ino = st.st_ino; 234 bestEntry.device = ref.device; 235 bestEntry.directory = ref.directory; 236 bestIndex = i; 237 } 238 } 239 } 240