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