1 /* 2 * Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * All rights reserved. Distributed under the terms of the MIT license. 4 */ 5 6 #include "AllocationInfo.h" 7 #include "DebugSupport.h" 8 #include "Directory.h" 9 #include "Entry.h" 10 #include "EntryIterator.h" 11 #include "File.h" 12 #include "SymLink.h" 13 #include "Volume.h" 14 15 // constructor 16 Directory::Directory(Volume *volume) 17 : Node(volume, NODE_TYPE_DIRECTORY), 18 fEntries() 19 { 20 } 21 22 // destructor 23 Directory::~Directory() 24 { 25 // delete all entries 26 while (Entry *entry = fEntries.First()) { 27 if (DeleteEntry(entry) != B_OK) { 28 FATAL("Could not delete all entries in directory.\n"); 29 break; 30 } 31 } 32 } 33 34 // Link 35 status_t 36 Directory::Link(Entry *entry) 37 { 38 if (fReferrers.IsEmpty()) 39 return Node::Link(entry); 40 return B_IS_A_DIRECTORY; 41 } 42 43 // Unlink 44 status_t 45 Directory::Unlink(Entry *entry) 46 { 47 if (entry == fReferrers.First()) 48 return Node::Unlink(entry); 49 return B_BAD_VALUE; 50 } 51 52 // SetSize 53 status_t 54 Directory::SetSize(off_t /*newSize*/) 55 { 56 return B_IS_A_DIRECTORY; 57 } 58 59 // GetSize 60 off_t 61 Directory::GetSize() const 62 { 63 return 0; 64 } 65 66 // GetParent 67 Directory * 68 Directory::GetParent() const 69 { 70 Entry *entry = fReferrers.First(); 71 return (entry ? entry->GetParent() : NULL); 72 } 73 74 // CreateDirectory 75 status_t 76 Directory::CreateDirectory(const char *name, Directory **directory) 77 { 78 status_t error = (name && directory ? B_OK : B_BAD_VALUE); 79 if (error == B_OK) { 80 // create directory 81 if (Directory *node = new(nothrow) Directory(GetVolume())) { 82 error = _CreateCommon(node, name); 83 // deletes the node on failure 84 if (error == B_OK) 85 *directory = node; 86 } else 87 SET_ERROR(error, B_NO_MEMORY); 88 } 89 return error; 90 } 91 92 // CreateFile 93 status_t 94 Directory::CreateFile(const char *name, File **file) 95 { 96 status_t error = (name && file ? B_OK : B_BAD_VALUE); 97 if (error == B_OK) { 98 // create file 99 if (File *node = new(nothrow) File(GetVolume())) { 100 error = _CreateCommon(node, name); 101 // deletes the node on failure 102 if (error == B_OK) 103 *file = node; 104 } else 105 SET_ERROR(error, B_NO_MEMORY); 106 } 107 return error; 108 } 109 110 // CreateSymLink 111 status_t 112 Directory::CreateSymLink(const char *name, const char *path, SymLink **symLink) 113 { 114 status_t error = (name && symLink ? B_OK : B_BAD_VALUE); 115 if (error == B_OK) { 116 // create symlink 117 if (SymLink *node = new(nothrow) SymLink(GetVolume())) { 118 error = node->SetLinkedPath(path); 119 if (error == B_OK) { 120 error = _CreateCommon(node, name); 121 // deletes the node on failure 122 if (error == B_OK) 123 *symLink = node; 124 } else 125 delete node; 126 } else 127 SET_ERROR(error, B_NO_MEMORY); 128 } 129 return error; 130 } 131 132 // AddEntry 133 status_t 134 Directory::AddEntry(Entry *entry) 135 { 136 status_t error = (entry && !entry->GetParent() ? B_OK : B_BAD_VALUE); 137 if (error == B_OK) { 138 fEntries.Insert(entry); 139 entry->SetParent(this); 140 error = GetVolume()->EntryAdded(GetID(), entry); 141 if (error == B_OK) { 142 MarkModified(B_STAT_MODIFICATION_TIME); 143 } else { 144 fEntries.Remove(entry); 145 entry->SetParent(NULL); 146 } 147 } 148 return error; 149 } 150 151 // CreateEntry 152 status_t 153 Directory::CreateEntry(Node *node, const char *name, Entry **_entry) 154 { 155 status_t error = (node ? B_OK : B_BAD_VALUE); 156 if (error == B_OK) { 157 // create an entry 158 Entry *entry = new(nothrow) Entry(name); 159 if (entry) { 160 error = entry->InitCheck(); 161 if (error == B_OK) { 162 // link to the node 163 error = entry->Link(node); 164 if (error == B_OK) { 165 // add the entry 166 error = AddEntry(entry); 167 if (error == B_OK) { 168 if (_entry) 169 *_entry = entry; 170 } else { 171 // failure: unlink the node 172 entry->Unlink(); 173 } 174 } 175 } 176 // delete the entry on failure 177 if (error != B_OK) 178 delete entry; 179 } else 180 SET_ERROR(error, B_NO_MEMORY); 181 } 182 return error; 183 } 184 185 // RemoveEntry 186 status_t 187 Directory::RemoveEntry(Entry *entry) 188 { 189 status_t error = (entry && entry->GetParent() == this ? B_OK 190 : B_BAD_VALUE); 191 if (error == B_OK) { 192 // move all iterators pointing to the entry to the next entry 193 if (GetVolume()->IteratorLock()) { 194 // set the iterators' current entry 195 Entry *nextEntry = fEntries.GetNext(entry); 196 DoublyLinkedList<EntryIterator> *iterators 197 = entry->GetEntryIteratorList(); 198 for (EntryIterator *iterator = iterators->First(); 199 iterator; 200 iterator = iterators->GetNext(iterator)) { 201 iterator->SetCurrent(nextEntry, true); 202 } 203 // Move the iterators from one list to the other, or just remove 204 // them, if there is no next entry. 205 if (nextEntry) { 206 DoublyLinkedList<EntryIterator> *nextIterators 207 = nextEntry->GetEntryIteratorList(); 208 nextIterators->MoveFrom(iterators); 209 } else 210 iterators->RemoveAll(); 211 GetVolume()->IteratorUnlock(); 212 } else 213 error = B_ERROR; 214 // remove the entry 215 if (error == B_OK) { 216 error = GetVolume()->EntryRemoved(GetID(), entry); 217 if (error == B_OK) { 218 fEntries.Remove(entry); 219 entry->SetParent(NULL); 220 MarkModified(B_STAT_MODIFICATION_TIME); 221 } 222 } 223 } 224 return error; 225 } 226 227 // DeleteEntry 228 status_t 229 Directory::DeleteEntry(Entry *entry) 230 { 231 status_t error = RemoveEntry(entry); 232 if (error == B_OK) { 233 error = entry->Unlink(); 234 if (error == B_OK) 235 delete entry; 236 else { 237 FATAL("Failed to Unlink() entry %p from node %" B_PRIdINO "!\n", entry, 238 entry->GetNode()->GetID()); 239 AddEntry(entry); 240 } 241 } 242 return error; 243 } 244 245 // FindEntry 246 status_t 247 Directory::FindEntry(const char *name, Entry **_entry) const 248 { 249 status_t error = (name && _entry ? B_OK : B_BAD_VALUE); 250 if (error == B_OK) { 251 /* 252 Entry *entry = NULL; 253 while (GetNextEntry(&entry) == B_OK) { 254 if (!strcmp(entry->GetName(), name)) { 255 *_entry = entry; 256 return B_OK; 257 } 258 } 259 error = B_ENTRY_NOT_FOUND; 260 */ 261 error = GetVolume()->FindEntry(GetID(), name, _entry); 262 } 263 return error; 264 } 265 266 // FindNode 267 status_t 268 Directory::FindNode(const char *name, Node **node) const 269 { 270 status_t error = (name && node ? B_OK : B_BAD_VALUE); 271 Entry *entry = NULL; 272 if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK) 273 *node = entry->GetNode(); 274 return error; 275 } 276 277 // FindAndGetNode 278 status_t 279 Directory::FindAndGetNode(const char *name, Node **node, Entry **_entry) const 280 { 281 status_t error = (name && node ? B_OK : B_BAD_VALUE); 282 Entry *entry = NULL; 283 if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK) { 284 *node = entry->GetNode(); 285 if (_entry) 286 *_entry = entry; 287 error = GetVolume()->GetVNode(*node); 288 } 289 return error; 290 } 291 292 // GetPreviousEntry 293 status_t 294 Directory::GetPreviousEntry(Entry **entry) const 295 { 296 status_t error = (entry ? B_OK : B_BAD_VALUE); 297 if (error == B_OK) { 298 if (!*entry) 299 *entry = fEntries.Last(); 300 else if ((*entry)->GetParent() == this) 301 *entry = fEntries.GetPrevious(*entry); 302 else 303 error = B_BAD_VALUE; 304 if (error == B_OK && !*entry) 305 error = B_ENTRY_NOT_FOUND; 306 } 307 return error; 308 } 309 310 // GetNextEntry 311 status_t 312 Directory::GetNextEntry(Entry **entry) const 313 { 314 status_t error = (entry ? B_OK : B_BAD_VALUE); 315 if (error == B_OK) { 316 if (!*entry) 317 *entry = fEntries.First(); 318 else if ((*entry)->GetParent() == this) 319 *entry = fEntries.GetNext(*entry); 320 else 321 error = B_BAD_VALUE; 322 if (error == B_OK && !*entry) 323 error = B_ENTRY_NOT_FOUND; 324 } 325 return error; 326 } 327 328 // GetAllocationInfo 329 void 330 Directory::GetAllocationInfo(AllocationInfo &info) 331 { 332 Node::GetAllocationInfo(info); 333 info.AddDirectoryAllocation(); 334 Entry *entry = NULL; 335 while (GetNextEntry(&entry) == B_OK) 336 entry->GetAllocationInfo(info); 337 } 338 339 // _CreateCommon 340 status_t 341 Directory::_CreateCommon(Node *node, const char *name) 342 { 343 status_t error = node->InitCheck(); 344 if (error == B_OK) { 345 // add node to directory 346 error = CreateEntry(node, name); 347 } 348 if (error != B_OK) 349 delete node; 350 return error; 351 } 352 353