xref: /haiku/src/add-ons/kernel/file_systems/ramfs/Directory.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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