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
19NameCacheEntry::NameCacheEntry(const char* name, ino_t node)
20	:
21	fNode(node),
22	fName(strdup(name))
23{
24	ASSERT(name != NULL);
25}
26
27
28NameCacheEntry::NameCacheEntry(const NameCacheEntry& entry)
29	:
30	fNode(entry.fNode),
31	fName(strdup(entry.fName))
32{
33}
34
35
36NameCacheEntry::~NameCacheEntry()
37{
38	free(const_cast<char*>(fName));
39}
40
41
42DirectoryCacheSnapshot::DirectoryCacheSnapshot()
43{
44	mutex_init(&fLock, NULL);
45}
46
47
48DirectoryCacheSnapshot::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
68DirectoryCacheSnapshot::~DirectoryCacheSnapshot()
69{
70	while (!fEntries.IsEmpty()) {
71		NameCacheEntry* current = fEntries.RemoveHead();
72		delete current;
73	}
74
75	mutex_destroy(&fLock);
76}
77
78
79DirectoryCache::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
93DirectoryCache::~DirectoryCache()
94{
95	mutex_destroy(&fLock);
96}
97
98
99void
100DirectoryCache::Reset()
101{
102	Trash();
103	fExpireTime = system_time() + fExpirationTime;
104	fTrashed = false;
105}
106
107
108void
109DirectoryCache::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
124status_t
125DirectoryCache::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
161void
162DirectoryCache::RemoveEntry(const char* name)
163{
164	ASSERT(name != NULL);
165
166	SinglyLinkedList<NameCacheEntry>::Iterator 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
205void
206DirectoryCache::_SetSnapshot(DirectoryCacheSnapshot* snapshot)
207{
208	if (fDirectoryCache != NULL)
209		fDirectoryCache->ReleaseReference();
210	fDirectoryCache = snapshot;
211}
212
213
214status_t
215DirectoryCache::_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
250status_t
251DirectoryCache::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
271void
272DirectoryCache::NotifyChanges(DirectoryCacheSnapshot* oldSnapshot,
273	DirectoryCacheSnapshot* newSnapshot)
274{
275	ASSERT(newSnapshot != NULL);
276	ASSERT(oldSnapshot != NULL);
277
278	MutexLocker _(newSnapshot->fLock);
279
280	SinglyLinkedList<NameCacheEntry>::Iterator oldIt
281		= oldSnapshot->fEntries.GetIterator();
282	NameCacheEntry* oldCurrent;
283
284	SinglyLinkedList<NameCacheEntry>::Iterator 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