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