xref: /haiku/src/add-ons/kernel/file_systems/ramfs/Directory.cpp (revision 1acbe440b8dd798953bec31d18ee589aa3f71b73)
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.GetFirst()) {
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.GetFirst())
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.GetFirst();
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 			DLList<EntryIterator> *iterators = entry->GetEntryIteratorList();
194 			for (EntryIterator *iterator = iterators->GetFirst();
195 				 iterator;
196 				 iterator = iterators->GetNext(iterator)) {
197 				iterator->SetCurrent(nextEntry, true);
198 			}
199 			// Move the iterators from one list to the other, or just remove
200 			// them, if there is no next entry.
201 			if (nextEntry) {
202 				DLList<EntryIterator> *nextIterators
203 					= nextEntry->GetEntryIteratorList();
204 				nextIterators->MoveFrom(iterators);
205 			} else
206 				iterators->RemoveAll();
207 			GetVolume()->IteratorUnlock();
208 		} else
209 			error = B_ERROR;
210 		// remove the entry
211 		if (error == B_OK) {
212 			error = GetVolume()->EntryRemoved(GetID(), entry);
213 			if (error == B_OK) {
214 				fEntries.Remove(entry);
215 				entry->SetParent(NULL);
216 				MarkModified(B_STAT_MODIFICATION_TIME);
217 			}
218 		}
219 	}
220 	return error;
221 }
222 
223 // DeleteEntry
224 status_t
225 Directory::DeleteEntry(Entry *entry)
226 {
227 	status_t error = RemoveEntry(entry);
228 	if (error == B_OK) {
229 		error = entry->Unlink();
230 		if (error == B_OK)
231 			delete entry;
232 		else {
233 			FATAL(("Failed to Unlink() entry %p from node %Ld!\n", entry,
234 				   entry->GetNode()->GetID()));
235 			AddEntry(entry);
236 		}
237 	}
238 	return error;
239 }
240 
241 // FindEntry
242 status_t
243 Directory::FindEntry(const char *name, Entry **_entry) const
244 {
245 	status_t error = (name && _entry ? B_OK : B_BAD_VALUE);
246 	if (error == B_OK) {
247 /*
248 		Entry *entry = NULL;
249 		while (GetNextEntry(&entry) == B_OK) {
250 			if (!strcmp(entry->GetName(), name)) {
251 				*_entry = entry;
252 				return B_OK;
253 			}
254 		}
255 		error = B_ENTRY_NOT_FOUND;
256 */
257 		error = GetVolume()->FindEntry(GetID(), name, _entry);
258 	}
259 	return error;
260 }
261 
262 // FindNode
263 status_t
264 Directory::FindNode(const char *name, Node **node) const
265 {
266 	status_t error = (name && node ? B_OK : B_BAD_VALUE);
267 	Entry *entry = NULL;
268 	if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK)
269 		*node = entry->GetNode();
270 	return error;
271 }
272 
273 // FindAndGetNode
274 status_t
275 Directory::FindAndGetNode(const char *name, Node **node, Entry **_entry) const
276 {
277 	status_t error = (name && node ? B_OK : B_BAD_VALUE);
278 	Entry *entry = NULL;
279 	if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK) {
280 		*node = entry->GetNode();
281 		if (_entry)
282 			*_entry = entry;
283 		error = GetVolume()->GetVNode(*node);
284 	}
285 	return error;
286 }
287 
288 // GetPreviousEntry
289 status_t
290 Directory::GetPreviousEntry(Entry **entry) const
291 {
292 	status_t error = (entry ? B_OK : B_BAD_VALUE);
293 	if (error == B_OK) {
294 		if (!*entry)
295 			*entry = fEntries.GetLast();
296 		else if ((*entry)->GetParent() == this)
297 			*entry = fEntries.GetPrevious(*entry);
298 		else
299 			error = B_BAD_VALUE;
300 		if (error == B_OK && !*entry)
301 			error = B_ENTRY_NOT_FOUND;
302 	}
303 	return error;
304 }
305 
306 // GetNextEntry
307 status_t
308 Directory::GetNextEntry(Entry **entry) const
309 {
310 	status_t error = (entry ? B_OK : B_BAD_VALUE);
311 	if (error == B_OK) {
312 		if (!*entry)
313 			*entry = fEntries.GetFirst();
314 		else if ((*entry)->GetParent() == this)
315 			*entry = fEntries.GetNext(*entry);
316 		else
317 			error = B_BAD_VALUE;
318 		if (error == B_OK && !*entry)
319 			error = B_ENTRY_NOT_FOUND;
320 	}
321 	return error;
322 }
323 
324 // GetAllocationInfo
325 void
326 Directory::GetAllocationInfo(AllocationInfo &info)
327 {
328 	Node::GetAllocationInfo(info);
329 	info.AddDirectoryAllocation();
330 	Entry *entry = NULL;
331 	while (GetNextEntry(&entry) == B_OK)
332 		entry->GetAllocationInfo(info);
333 }
334 
335 // _CreateCommon
336 status_t
337 Directory::_CreateCommon(Node *node, const char *name)
338 {
339 	status_t error = node->InitCheck();
340 	if (error == B_OK) {
341 		// add node to directory
342 		error = CreateEntry(node, name);
343 	}
344 	if (error != B_OK)
345 		delete node;
346 	return error;
347 }
348 
349