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