xref: /haiku/src/kits/storage/MergedDirectory.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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