xref: /haiku/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp (revision df44e515de8caa190aa2b45f2ce928b961592c28)
1 /*
2  * Copyright 2012-2016 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Paweł Dziepak, pdziepak@quarnos.org
7  */
8 
9 
10 #include "DirectoryCache.h"
11 
12 #include <fs_cache.h>
13 #include <NodeMonitor.h>
14 
15 #include "Inode.h"
16 
17 
18 
NameCacheEntry(const char * name,ino_t node)19 NameCacheEntry::NameCacheEntry(const char* name, ino_t node)
20 	:
21 	fNode(node),
22 	fName(strdup(name))
23 {
24 	ASSERT(name != NULL);
25 }
26 
27 
NameCacheEntry(const NameCacheEntry & entry)28 NameCacheEntry::NameCacheEntry(const NameCacheEntry& entry)
29 	:
30 	fNode(entry.fNode),
31 	fName(strdup(entry.fName))
32 {
33 }
34 
35 
~NameCacheEntry()36 NameCacheEntry::~NameCacheEntry()
37 {
38 	free(const_cast<char*>(fName));
39 }
40 
41 
DirectoryCacheSnapshot()42 DirectoryCacheSnapshot::DirectoryCacheSnapshot()
43 {
44 	mutex_init(&fLock, NULL);
45 }
46 
47 
DirectoryCacheSnapshot(const DirectoryCacheSnapshot & snapshot)48 DirectoryCacheSnapshot::DirectoryCacheSnapshot(
49 	const DirectoryCacheSnapshot& snapshot)
50 {
51 	mutex_init(&fLock, NULL);
52 
53 	MutexLocker _(snapshot.fLock);
54 	NameCacheEntry* entry = snapshot.fEntries.Head();
55 	NameCacheEntry* new_entry;
56 	while (entry) {
57 		new_entry = new NameCacheEntry(*entry);
58 		if (new_entry == NULL)
59 			break;
60 
61 		fEntries.Add(new_entry);
62 
63 		entry = snapshot.fEntries.GetNext(entry);
64 	}
65 }
66 
67 
~DirectoryCacheSnapshot()68 DirectoryCacheSnapshot::~DirectoryCacheSnapshot()
69 {
70 	while (!fEntries.IsEmpty()) {
71 		NameCacheEntry* current = fEntries.RemoveHead();
72 		delete current;
73 	}
74 
75 	mutex_destroy(&fLock);
76 }
77 
78 
DirectoryCache(Inode * inode,bool attr)79 DirectoryCache::DirectoryCache(Inode* inode, bool attr)
80 	:
81 	fExpirationTime(inode->fFileSystem->GetConfiguration().fDirectoryCacheTime),
82 	fDirectoryCache(NULL),
83 	fInode(inode),
84 	fAttrDir(attr),
85 	fTrashed(true)
86 {
87 	ASSERT(inode != NULL);
88 
89 	mutex_init(&fLock, NULL);
90 }
91 
92 
~DirectoryCache()93 DirectoryCache::~DirectoryCache()
94 {
95 	mutex_destroy(&fLock);
96 }
97 
98 
99 void
Reset()100 DirectoryCache::Reset()
101 {
102 	Trash();
103 	fExpireTime = system_time() + fExpirationTime;
104 	fTrashed = false;
105 }
106 
107 
108 void
Trash()109 DirectoryCache::Trash()
110 {
111 	while (!fNameCache.IsEmpty()) {
112 		NameCacheEntry* current = fNameCache.RemoveHead();
113 		entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
114 			current->fName);
115 		delete current;
116 	}
117 
118 	_SetSnapshot(NULL);
119 
120 	fTrashed = true;
121 }
122 
123 
124 status_t
AddEntry(const char * name,ino_t node,bool created)125 DirectoryCache::AddEntry(const char* name, ino_t node, bool created)
126 {
127 	ASSERT(name != NULL);
128 
129 	NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node);
130 	if (entry == NULL)
131 		return B_NO_MEMORY;
132 	if (entry->fName == NULL) {
133 		delete entry;
134 		return B_NO_MEMORY;
135 	}
136 
137 	fNameCache.Add(entry);
138 
139 	if (created && fDirectoryCache != NULL) {
140 		MutexLocker _(fDirectoryCache->fLock);
141 		NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node);
142 		if (entry == NULL)
143 			return B_NO_MEMORY;
144 		if (entry->fName == NULL) {
145 			delete entry;
146 			return B_NO_MEMORY;
147 		}
148 
149 		fDirectoryCache->fEntries.Add(entry);
150 	}
151 
152 	if (!fAttrDir) {
153 		return entry_cache_add(fInode->GetFileSystem()->DevId(), fInode->ID(),
154 			name, node);
155 	}
156 
157 	return B_OK;
158 }
159 
160 
161 void
RemoveEntry(const char * name)162 DirectoryCache::RemoveEntry(const char* name)
163 {
164 	ASSERT(name != NULL);
165 
166 	SinglyLinkedList<NameCacheEntry>::ConstIterator iterator
167 		= fNameCache.GetIterator();
168 	NameCacheEntry* previous = NULL;
169 	NameCacheEntry* current = iterator.Next();
170 	while (current != NULL) {
171 		if (strcmp(current->fName, name) == 0) {
172 			fNameCache.Remove(previous, current);
173 			delete current;
174 			break;
175 		}
176 
177 		previous = current;
178 		current = iterator.Next();
179 	}
180 
181 	if (fDirectoryCache != NULL) {
182 		MutexLocker _(fDirectoryCache->fLock);
183 		iterator = fDirectoryCache->fEntries.GetIterator();
184 		previous = NULL;
185 		current = iterator.Next();
186 		while (current != NULL) {
187 			if (strcmp(current->fName, name) == 0) {
188 				fDirectoryCache->fEntries.Remove(previous, current);
189 				delete current;
190 				break;
191 			}
192 
193 			previous = current;
194 			current = iterator.Next();
195 		}
196 	}
197 
198 	if (!fAttrDir) {
199 		entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
200 			name);
201 	}
202 }
203 
204 
205 void
_SetSnapshot(DirectoryCacheSnapshot * snapshot)206 DirectoryCache::_SetSnapshot(DirectoryCacheSnapshot* snapshot)
207 {
208 	if (fDirectoryCache != NULL)
209 		fDirectoryCache->ReleaseReference();
210 	fDirectoryCache = snapshot;
211 }
212 
213 
214 status_t
_LoadSnapshot(bool trash)215 DirectoryCache::_LoadSnapshot(bool trash)
216 {
217 	DirectoryCacheSnapshot* oldSnapshot = fDirectoryCache;
218 	if (oldSnapshot != NULL)
219 		oldSnapshot->AcquireReference();
220 
221 	if (trash)
222 		Trash();
223 
224 	DirectoryCacheSnapshot* newSnapshot;
225 	status_t result = fInode->GetDirSnapshot(&newSnapshot, NULL, &fChange,
226 		fAttrDir);
227 	if (result != B_OK) {
228 		if (oldSnapshot != NULL)
229 			oldSnapshot->ReleaseReference();
230 		return result;
231 	}
232 	newSnapshot->AcquireReference();
233 
234 	_SetSnapshot(newSnapshot);
235 	fExpireTime = system_time() + fExpirationTime;
236 
237 	fTrashed = false;
238 
239 	if (oldSnapshot != NULL)
240 		NotifyChanges(oldSnapshot, newSnapshot);
241 
242 	if (oldSnapshot != NULL)
243 		oldSnapshot->ReleaseReference();
244 
245 	newSnapshot->ReleaseReference();
246 	return B_OK;
247 }
248 
249 
250 status_t
Revalidate()251 DirectoryCache::Revalidate()
252 {
253 	if (fExpireTime > system_time())
254 		return B_OK;
255 
256 	uint64 change;
257 	if (fInode->GetChangeInfo(&change, fAttrDir) != B_OK) {
258 		Trash();
259 		return B_ERROR;
260 	}
261 
262 	if (change == fChange) {
263 		fExpireTime = system_time() + fExpirationTime;
264 		return B_OK;
265 	}
266 
267 	return _LoadSnapshot(true);
268 }
269 
270 
271 void
NotifyChanges(DirectoryCacheSnapshot * oldSnapshot,DirectoryCacheSnapshot * newSnapshot)272 DirectoryCache::NotifyChanges(DirectoryCacheSnapshot* oldSnapshot,
273 	DirectoryCacheSnapshot* newSnapshot)
274 {
275 	ASSERT(newSnapshot != NULL);
276 	ASSERT(oldSnapshot != NULL);
277 
278 	MutexLocker _(newSnapshot->fLock);
279 
280 	SinglyLinkedList<NameCacheEntry>::ConstIterator oldIt
281 		= oldSnapshot->fEntries.GetIterator();
282 	NameCacheEntry* oldCurrent;
283 
284 	SinglyLinkedList<NameCacheEntry>::ConstIterator newIt
285 		= newSnapshot->fEntries.GetIterator();
286 	NameCacheEntry* newCurrent = newIt.Next();
287 	while (newCurrent != NULL) {
288 		oldIt = oldSnapshot->fEntries.GetIterator();
289 		oldCurrent = oldIt.Next();
290 
291 		bool found = false;
292 		NameCacheEntry* prev = NULL;
293 		while (oldCurrent != NULL) {
294 			if (oldCurrent->fNode == newCurrent->fNode
295 				&& strcmp(oldCurrent->fName, newCurrent->fName) == 0) {
296 				found = true;
297 				break;
298 			}
299 
300 			prev = oldCurrent;
301 			oldCurrent = oldIt.Next();
302 		}
303 
304 		if (!found) {
305 			if (fAttrDir) {
306 				notify_attribute_changed(fInode->GetFileSystem()->DevId(),
307 					-1, fInode->ID(), newCurrent->fName, B_ATTR_CREATED);
308 			} else {
309 				notify_entry_created(fInode->GetFileSystem()->DevId(),
310 					fInode->ID(), newCurrent->fName, newCurrent->fNode);
311 			}
312 		} else
313 			oldSnapshot->fEntries.Remove(prev, oldCurrent);
314 
315 		newCurrent = newIt.Next();
316 	}
317 
318 	oldIt = oldSnapshot->fEntries.GetIterator();
319 	oldCurrent = oldIt.Next();
320 
321 	while (oldCurrent != NULL) {
322 		if (fAttrDir) {
323 			notify_attribute_changed(fInode->GetFileSystem()->DevId(),
324 				-1, fInode->ID(), oldCurrent->fName, B_ATTR_REMOVED);
325 		} else {
326 			notify_entry_removed(fInode->GetFileSystem()->DevId(), fInode->ID(),
327 				oldCurrent->fName, oldCurrent->fNode);
328 		}
329 		oldCurrent = oldIt.Next();
330 	}
331 }
332 
333