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 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 28 NameCacheEntry::NameCacheEntry(const NameCacheEntry& entry) 29 : 30 fNode(entry.fNode), 31 fName(strdup(entry.fName)) 32 { 33 } 34 35 36 NameCacheEntry::~NameCacheEntry() 37 { 38 free(const_cast<char*>(fName)); 39 } 40 41 42 DirectoryCacheSnapshot::DirectoryCacheSnapshot() 43 { 44 mutex_init(&fLock, NULL); 45 } 46 47 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 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 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 93 DirectoryCache::~DirectoryCache() 94 { 95 mutex_destroy(&fLock); 96 } 97 98 99 void 100 DirectoryCache::Reset() 101 { 102 Trash(); 103 fExpireTime = system_time() + fExpirationTime; 104 fTrashed = false; 105 } 106 107 108 void 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 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 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 206 DirectoryCache::_SetSnapshot(DirectoryCacheSnapshot* snapshot) 207 { 208 if (fDirectoryCache != NULL) 209 fDirectoryCache->ReleaseReference(); 210 fDirectoryCache = snapshot; 211 } 212 213 214 status_t 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 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 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