xref: /haiku/src/add-ons/kernel/file_systems/ramfs/Volume.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
1 // Volume.cpp
2 //
3 // Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de)
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 //
19 // You can alternatively use *this file* under the terms of the the MIT
20 // license included in this package.
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 #include <vm/vm_page.h>
30 
31 #include "DebugSupport.h"
32 #include "Directory.h"
33 #include "DirectoryEntryTable.h"
34 #include "Entry.h"
35 #include "EntryListener.h"
36 #include "IndexDirectory.h"
37 #include "Locking.h"
38 #include "Misc.h"
39 #include "NameIndex.h"
40 #include "Node.h"
41 #include "NodeListener.h"
42 #include "NodeTable.h"
43 #include "TwoKeyAVLTree.h"
44 #include "Volume.h"
45 
46 // default volume name
47 static const char *kDefaultVolumeName = "RAM FS";
48 
49 // NodeListenerGetPrimaryKey
50 class NodeListenerGetPrimaryKey {
51 public:
52 	inline Node *operator()(const NodeListenerValue &a)
53 	{
54 		return a.node;
55 	}
56 
57 	inline Node *operator()(const NodeListenerValue &a) const
58 	{
59 		return a.node;
60 	}
61 };
62 
63 // NodeListenerGetSecondaryKey
64 class NodeListenerGetSecondaryKey {
65 public:
66 	inline NodeListener *operator()(const NodeListenerValue &a)
67 	{
68 		return a.listener;
69 	}
70 
71 	inline NodeListener *operator()(const NodeListenerValue &a) const
72 	{
73 		return a.listener;
74 	}
75 };
76 
77 // NodeListenerTree
78 typedef TwoKeyAVLTree<NodeListenerValue, Node*,
79 					  TwoKeyAVLTreeStandardCompare<Node*>,
80 					  NodeListenerGetPrimaryKey, NodeListener*,
81 					  TwoKeyAVLTreeStandardCompare<NodeListener*>,
82 					  NodeListenerGetSecondaryKey > _NodeListenerTree;
83 class NodeListenerTree : public _NodeListenerTree {};
84 
85 // EntryListenerGetPrimaryKey
86 class EntryListenerGetPrimaryKey {
87 public:
88 	inline Entry *operator()(const EntryListenerValue &a)
89 	{
90 		return a.entry;
91 	}
92 
93 	inline Entry *operator()(const EntryListenerValue &a) const
94 	{
95 		return a.entry;
96 	}
97 };
98 
99 // EntryListenerGetSecondaryKey
100 class EntryListenerGetSecondaryKey {
101 public:
102 	inline EntryListener *operator()(const EntryListenerValue &a)
103 	{
104 		return a.listener;
105 	}
106 
107 	inline EntryListener *operator()(const EntryListenerValue &a) const
108 	{
109 		return a.listener;
110 	}
111 };
112 
113 // EntryListenerTree
114 typedef TwoKeyAVLTree<EntryListenerValue, Entry*,
115 					  TwoKeyAVLTreeStandardCompare<Entry*>,
116 					  EntryListenerGetPrimaryKey, EntryListener*,
117 					  TwoKeyAVLTreeStandardCompare<EntryListener*>,
118 					  EntryListenerGetSecondaryKey > _EntryListenerTree;
119 class EntryListenerTree : public _EntryListenerTree {};
120 
121 
122 /*!
123 	\class Volume
124 	\brief Represents a volume.
125 */
126 
127 // constructor
128 Volume::Volume(fs_volume* volume)
129 	:
130 	fVolume(volume),
131 	fNextNodeID(kRootParentID + 1),
132 	fNodeTable(NULL),
133 	fDirectoryEntryTable(NULL),
134 	fIndexDirectory(NULL),
135 	fRootDirectory(NULL),
136 	fName(kDefaultVolumeName),
137 	fNodeListeners(NULL),
138 	fAnyNodeListeners(),
139 	fEntryListeners(NULL),
140 	fAnyEntryListeners(),
141 	fAccessTime(0),
142 	fMounted(false)
143 {
144 	rw_lock_init(&fLocker, "ramfs volume");
145 	recursive_lock_init(&fIteratorLocker, "ramfs iterators");
146 	recursive_lock_init(&fQueryLocker, "ramfs queries");
147 }
148 
149 
150 // destructor
151 Volume::~Volume()
152 {
153 	Unmount();
154 
155 	recursive_lock_destroy(&fIteratorLocker);
156 	recursive_lock_destroy(&fQueryLocker);
157 	rw_lock_destroy(&fLocker);
158 }
159 
160 
161 // Mount
162 status_t
163 Volume::Mount(uint32 flags)
164 {
165 	Unmount();
166 
167 	status_t error = B_OK;
168 	// create the listener trees
169 	if (error == B_OK) {
170 		fNodeListeners = new(nothrow) NodeListenerTree;
171 		if (!fNodeListeners)
172 			error = B_NO_MEMORY;
173 	}
174 	if (error == B_OK) {
175 		fEntryListeners = new(nothrow) EntryListenerTree;
176 		if (!fEntryListeners)
177 			error = B_NO_MEMORY;
178 	}
179 	// create the node table
180 	if (error == B_OK) {
181 		fNodeTable = new(nothrow) NodeTable;
182 		if (fNodeTable)
183 			error = fNodeTable->InitCheck();
184 		else
185 			SET_ERROR(error, B_NO_MEMORY);
186 	}
187 	// create the directory entry table
188 	if (error == B_OK) {
189 		fDirectoryEntryTable = new(nothrow) DirectoryEntryTable;
190 		if (fDirectoryEntryTable)
191 			error = fDirectoryEntryTable->InitCheck();
192 		else
193 			SET_ERROR(error, B_NO_MEMORY);
194 	}
195 	// create the index directory
196 	if (error == B_OK) {
197 		fIndexDirectory = new(nothrow) IndexDirectory(this);
198 		if (!fIndexDirectory)
199 			SET_ERROR(error, B_NO_MEMORY);
200 	}
201 	// create the root dir
202 	if (error == B_OK) {
203 		fRootDirectory = new(nothrow) Directory(this);
204 		if (fRootDirectory) {
205 			// set permissions: -rwxr-xr-x
206 			fRootDirectory->SetMode(
207 				S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
208 			error = PublishVNode(fRootDirectory);
209 		} else
210 			SET_ERROR(error, B_NO_MEMORY);
211 	}
212 	// set mounted flag / cleanup on error
213 	if (error == B_OK)
214 		fMounted = true;
215 	else
216 		Unmount();
217 	RETURN_ERROR(error);
218 }
219 
220 // Unmount
221 status_t
222 Volume::Unmount()
223 {
224 	fMounted = false;
225 	// delete the root directory
226 	if (fRootDirectory) {
227 		// deleting the root directory destroys the complete hierarchy
228 		delete fRootDirectory;
229 		fRootDirectory = NULL;
230 	}
231 	// delete the index directory
232 	if (fIndexDirectory) {
233 		delete fIndexDirectory;
234 		fIndexDirectory = NULL;
235 	}
236 	// delete the listener trees
237 	if (fEntryListeners) {
238 		delete fEntryListeners;
239 		fEntryListeners = NULL;
240 	}
241 	if (fNodeListeners) {
242 		delete fNodeListeners;
243 		fNodeListeners = NULL;
244 	}
245 	// delete the tables
246 	if (fDirectoryEntryTable) {
247 		delete fDirectoryEntryTable;
248 		fDirectoryEntryTable = NULL;
249 	}
250 	if (fNodeTable) {
251 		delete fNodeTable;
252 		fNodeTable = NULL;
253 	}
254 	return B_OK;
255 }
256 
257 // CountBlocks
258 off_t
259 Volume::CountBlocks() const
260 {
261 	// TODO: Compute how many pages we are using across all DataContainers.
262 	return 0;
263 }
264 
265 // CountFreeBlocks
266 off_t
267 Volume::CountFreeBlocks() const
268 {
269 	return vm_page_num_free_pages();
270 }
271 
272 // SetName
273 status_t
274 Volume::SetName(const char *name)
275 {
276 	status_t error = (name ? B_OK : B_BAD_VALUE);
277 	if (error == B_OK) {
278 		if (!fName.SetTo(name))
279 			SET_ERROR(error, B_NO_MEMORY);
280 	}
281 	return error;
282 }
283 
284 // GetName
285 const char *
286 Volume::GetName() const
287 {
288 	return fName.GetString();
289 }
290 
291 // NewVNode
292 status_t
293 Volume::NewVNode(Node *node)
294 {
295 	status_t error = NodeAdded(node);
296 	if (error == B_OK) {
297 		error = new_vnode(FSVolume(), node->GetID(), node, &gRamFSVnodeOps);
298 		if (error != B_OK)
299 			NodeRemoved(node);
300 	}
301 	return error;
302 }
303 
304 // PublishVNode
305 status_t
306 Volume::PublishVNode(Node *node)
307 {
308 	status_t error = NodeAdded(node);
309 	if (error == B_OK) {
310 		error = publish_vnode(FSVolume(), node->GetID(), node, &gRamFSVnodeOps,
311 			node->GetMode(), 0);
312 		if (error != B_OK)
313 			NodeRemoved(node);
314 	}
315 	return error;
316 }
317 
318 // GetVNode
319 status_t
320 Volume::GetVNode(ino_t id, Node **node)
321 {
322 	return (fMounted ? get_vnode(FSVolume(), id, (void**)node) : B_BAD_VALUE);
323 }
324 
325 // GetVNode
326 status_t
327 Volume::GetVNode(Node *node)
328 {
329 	Node *dummy = NULL;
330 	status_t error = (fMounted ? GetVNode(node->GetID(), &dummy)
331 							   : B_BAD_VALUE );
332 	if (error == B_OK && dummy != node) {
333 		FATAL("Two Nodes have the same ID: %" B_PRIdINO "!\n", node->GetID());
334 		PutVNode(dummy);
335 		error = B_ERROR;
336 	}
337 	return error;
338 }
339 
340 // PutVNode
341 status_t
342 Volume::PutVNode(ino_t id)
343 {
344 	return (fMounted ? put_vnode(FSVolume(), id) : B_BAD_VALUE);
345 }
346 
347 // PutVNode
348 status_t
349 Volume::PutVNode(Node *node)
350 {
351 	return (fMounted ? put_vnode(FSVolume(), node->GetID()) : B_BAD_VALUE);
352 }
353 
354 // RemoveVNode
355 status_t
356 Volume::RemoveVNode(Node *node)
357 {
358 	if (fMounted)
359 		return remove_vnode(FSVolume(), node->GetID());
360 
361 	status_t error = NodeRemoved(node);
362 	if (error == B_OK)
363 		delete node;
364 	return error;
365 }
366 
367 // UnremoveVNode
368 status_t
369 Volume::UnremoveVNode(Node *node)
370 {
371 	return (fMounted ? unremove_vnode(FSVolume(), node->GetID()) : B_BAD_VALUE);
372 }
373 
374 // NodeAdded
375 status_t
376 Volume::NodeAdded(Node *node)
377 {
378 	status_t error = (node ? B_OK : B_BAD_VALUE);
379 	if (error == B_OK) {
380 		error = fNodeTable->AddNode(node);
381 		// notify listeners
382 		if (error == B_OK) {
383 			// listeners interested in that node
384 			NodeListenerTree::Iterator it;
385 			if (fNodeListeners->FindFirst(node, &it)) {
386 				for (NodeListenerValue *value = it.GetCurrent();
387 					 value && value->node == node;
388 					 value = it.GetNext()) {
389 					 if (value->flags & NODE_LISTEN_ADDED)
390 						 value->listener->NodeAdded(node);
391 				}
392 			}
393 			// listeners interested in any node
394 			int32 count = fAnyNodeListeners.CountItems();
395 			for (int32 i = 0; i < count; i++) {
396 				const NodeListenerValue &value = fAnyNodeListeners.ItemAt(i);
397 				 if (value.flags & NODE_LISTEN_ADDED)
398 					 value.listener->NodeAdded(node);
399 			}
400 		}
401 	}
402 	return error;
403 }
404 
405 // NodeRemoved
406 status_t
407 Volume::NodeRemoved(Node *node)
408 {
409 	status_t error = (node ? B_OK : B_BAD_VALUE);
410 	if (error == B_OK) {
411 		error = fNodeTable->RemoveNode(node);
412 		// notify listeners
413 		if (error == B_OK) {
414 			// listeners interested in that node
415 			NodeListenerTree::Iterator it;
416 			if (fNodeListeners->FindFirst(node, &it)) {
417 				for (NodeListenerValue *value = it.GetCurrent();
418 					 value && value->node == node;
419 					 value = it.GetNext()) {
420 					 if (value->flags & NODE_LISTEN_REMOVED)
421 						 value->listener->NodeRemoved(node);
422 				}
423 			}
424 			// listeners interested in any node
425 			int32 count = fAnyNodeListeners.CountItems();
426 			for (int32 i = 0; i < count; i++) {
427 				const NodeListenerValue &value = fAnyNodeListeners.ItemAt(i);
428 				 if (value.flags & NODE_LISTEN_REMOVED)
429 					 value.listener->NodeRemoved(node);
430 			}
431 		}
432 	}
433 	return error;
434 }
435 
436 // FindNode
437 /*!	\brief Finds the node identified by a ino_t.
438 
439 	\note The method does not initialize the parent ID for non-directory nodes.
440 
441 	\param id ID of the node to be found.
442 	\param node pointer to a pre-allocated Node* to be set to the found node.
443 	\return \c B_OK, if everything went fine.
444 */
445 status_t
446 Volume::FindNode(ino_t id, Node **node)
447 {
448 	status_t error = (node ? B_OK : B_BAD_VALUE);
449 	if (error == B_OK) {
450 		*node = fNodeTable->GetNode(id);
451 		if (!*node)
452 			error = B_ENTRY_NOT_FOUND;
453 	}
454 	return error;
455 }
456 
457 // AddNodeListener
458 status_t
459 Volume::AddNodeListener(NodeListener *listener, Node *node, uint32 flags)
460 {
461 	// check parameters
462 	if (!listener || (!node && !(flags & NODE_LISTEN_ANY_NODE))
463 		|| !(flags & NODE_LISTEN_ALL)) {
464 		return B_BAD_VALUE;
465 	}
466 	// add the listener to the right container
467 	status_t error = B_OK;
468 	NodeListenerValue value(listener, node, flags);
469 	if (flags & NODE_LISTEN_ANY_NODE) {
470 		if (!fAnyNodeListeners.AddItem(value))
471 			error = B_NO_MEMORY;
472 	} else
473 		error = fNodeListeners->Insert(value);
474 	return error;
475 }
476 
477 // RemoveNodeListener
478 status_t
479 Volume::RemoveNodeListener(NodeListener *listener, Node *node)
480 {
481 	if (!listener)
482 		return B_BAD_VALUE;
483 	status_t error = B_OK;
484 	if (node)
485 		error = fNodeListeners->Remove(node, listener);
486 	else {
487 		NodeListenerValue value(listener, node, 0);
488 		if (!fAnyNodeListeners.RemoveItem(value))
489 			error = B_ENTRY_NOT_FOUND;
490 	}
491 	return error;
492 }
493 
494 // EntryAdded
495 status_t
496 Volume::EntryAdded(ino_t id, Entry *entry)
497 {
498 	status_t error = (entry ? B_OK : B_BAD_VALUE);
499 	if (error == B_OK) {
500 		error = fDirectoryEntryTable->AddEntry(id, entry);
501 		if (error == B_OK) {
502 			// notify listeners
503 			// listeners interested in that entry
504 			EntryListenerTree::Iterator it;
505 			if (fEntryListeners->FindFirst(entry, &it)) {
506 				for (EntryListenerValue *value = it.GetCurrent();
507 					 value && value->entry == entry;
508 					 value = it.GetNext()) {
509 					 if (value->flags & ENTRY_LISTEN_ADDED)
510 						 value->listener->EntryAdded(entry);
511 				}
512 			}
513 			// listeners interested in any entry
514 			int32 count = fAnyEntryListeners.CountItems();
515 			for (int32 i = 0; i < count; i++) {
516 				const EntryListenerValue &value = fAnyEntryListeners.ItemAt(i);
517 				 if (value.flags & ENTRY_LISTEN_ADDED)
518 					 value.listener->EntryAdded(entry);
519 			}
520 		}
521 	}
522 	return error;
523 }
524 
525 // EntryRemoved
526 status_t
527 Volume::EntryRemoved(ino_t id, Entry *entry)
528 {
529 	status_t error = (entry ? B_OK : B_BAD_VALUE);
530 	if (error == B_OK) {
531 		error = fDirectoryEntryTable->RemoveEntry(id, entry);
532 		if (error == B_OK) {
533 			// notify listeners
534 			// listeners interested in that entry
535 			EntryListenerTree::Iterator it;
536 			if (fEntryListeners->FindFirst(entry, &it)) {
537 				for (EntryListenerValue *value = it.GetCurrent();
538 					 value && value->entry == entry;
539 					 value = it.GetNext()) {
540 					 if (value->flags & ENTRY_LISTEN_REMOVED)
541 						 value->listener->EntryRemoved(entry);
542 				}
543 			}
544 			// listeners interested in any entry
545 			int32 count = fAnyEntryListeners.CountItems();
546 			for (int32 i = 0; i < count; i++) {
547 				const EntryListenerValue &value = fAnyEntryListeners.ItemAt(i);
548 				 if (value.flags & ENTRY_LISTEN_REMOVED)
549 					 value.listener->EntryRemoved(entry);
550 			}
551 		}
552 	}
553 	return error;
554 }
555 
556 // FindEntry
557 status_t
558 Volume::FindEntry(ino_t id, const char *name, Entry **entry)
559 {
560 	status_t error = (entry ? B_OK : B_BAD_VALUE);
561 	if (error == B_OK) {
562 		*entry = fDirectoryEntryTable->GetEntry(id, name);
563 		if (!*entry)
564 			error = B_ENTRY_NOT_FOUND;
565 	}
566 	return error;
567 }
568 
569 // AddEntryListener
570 status_t
571 Volume::AddEntryListener(EntryListener *listener, Entry *entry, uint32 flags)
572 {
573 	// check parameters
574 	if (!listener || (!entry && !(flags & ENTRY_LISTEN_ANY_ENTRY))
575 		|| !(flags & ENTRY_LISTEN_ALL)) {
576 		return B_BAD_VALUE;
577 	}
578 	// add the listener to the right container
579 	status_t error = B_OK;
580 	EntryListenerValue value(listener, entry, flags);
581 	if (flags & ENTRY_LISTEN_ANY_ENTRY) {
582 		if (!fAnyEntryListeners.AddItem(value))
583 			error = B_NO_MEMORY;
584 	} else
585 		error = fEntryListeners->Insert(value);
586 	return error;
587 }
588 
589 // RemoveEntryListener
590 status_t
591 Volume::RemoveEntryListener(EntryListener *listener, Entry *entry)
592 {
593 	if (!listener)
594 		return B_BAD_VALUE;
595 	status_t error = B_OK;
596 	if (entry)
597 		error = fEntryListeners->Remove(entry, listener);
598 	else {
599 		EntryListenerValue value(listener, entry, 0);
600 		if (!fAnyEntryListeners.RemoveItem(value))
601 			error = B_ENTRY_NOT_FOUND;
602 	}
603 	return error;
604 }
605 
606 // NodeAttributeAdded
607 status_t
608 Volume::NodeAttributeAdded(ino_t id, Attribute *attribute)
609 {
610 	status_t error = (attribute ? B_OK : B_BAD_VALUE);
611 	if (error == B_OK) {
612 		// notify the respective attribute index
613 		if (error == B_OK) {
614 			if (AttributeIndex *index = FindAttributeIndex(
615 					attribute->GetName(), attribute->GetType())) {
616 				index->Added(attribute);
617 			}
618 		}
619 	}
620 	return error;
621 }
622 
623 // NodeAttributeRemoved
624 status_t
625 Volume::NodeAttributeRemoved(ino_t id, Attribute *attribute)
626 {
627 	status_t error = (attribute ? B_OK : B_BAD_VALUE);
628 	if (error == B_OK) {
629 		// notify the respective attribute index
630 		if (error == B_OK) {
631 			if (AttributeIndex *index = FindAttributeIndex(
632 					attribute->GetName(), attribute->GetType())) {
633 				index->Removed(attribute);
634 			}
635 		}
636 
637 		// update live queries
638 		if (error == B_OK && attribute->GetNode()) {
639 			uint8 oldKey[kMaxIndexKeyLength];
640 			size_t oldLength;
641 			attribute->GetKey(oldKey, &oldLength);
642 			UpdateLiveQueries(NULL, attribute->GetNode(), attribute->GetName(),
643 				attribute->GetType(), oldKey, oldLength, NULL, 0);
644 		}
645 	}
646 	return error;
647 }
648 
649 // GetNameIndex
650 NameIndex *
651 Volume::GetNameIndex() const
652 {
653 	return (fIndexDirectory ? fIndexDirectory->GetNameIndex() : NULL);
654 }
655 
656 // GetLastModifiedIndex
657 LastModifiedIndex *
658 Volume::GetLastModifiedIndex() const
659 {
660 	return (fIndexDirectory ? fIndexDirectory->GetLastModifiedIndex() : NULL);
661 }
662 
663 // GetSizeIndex
664 SizeIndex *
665 Volume::GetSizeIndex() const
666 {
667 	return (fIndexDirectory ? fIndexDirectory->GetSizeIndex() : NULL);
668 }
669 
670 // FindIndex
671 Index *
672 Volume::FindIndex(const char *name)
673 {
674 	return (fIndexDirectory ? fIndexDirectory->FindIndex(name) : NULL);
675 }
676 
677 // FindAttributeIndex
678 AttributeIndex *
679 Volume::FindAttributeIndex(const char *name, uint32 type)
680 {
681 	return (fIndexDirectory
682 			? fIndexDirectory->FindAttributeIndex(name, type) : NULL);
683 }
684 
685 // AddQuery
686 void
687 Volume::AddQuery(Query *query)
688 {
689 	RecursiveLocker _(fQueryLocker);
690 
691 	if (query)
692 		fQueries.Insert(query);
693 }
694 
695 // RemoveQuery
696 void
697 Volume::RemoveQuery(Query *query)
698 {
699 	RecursiveLocker _(fQueryLocker);
700 
701 	if (query)
702 		fQueries.Remove(query);
703 }
704 
705 // UpdateLiveQueries
706 void
707 Volume::UpdateLiveQueries(Entry *entry, Node* node, const char *attribute,
708 	int32 type, const uint8 *oldKey, size_t oldLength, const uint8 *newKey,
709 	size_t newLength)
710 {
711 	RecursiveLocker _(fQueryLocker);
712 
713 	for (Query* query = fQueries.First();
714 		 query;
715 		 query = fQueries.GetNext(query)) {
716 		query->LiveUpdate(entry, node, attribute, type, oldKey, oldLength,
717 			newKey, newLength);
718 	}
719 }
720 
721 // GetAllocationInfo
722 void
723 Volume::GetAllocationInfo(AllocationInfo &info)
724 {
725 	// tables
726 	info.AddOtherAllocation(sizeof(NodeTable));
727 	fNodeTable->GetAllocationInfo(info);
728 	info.AddOtherAllocation(sizeof(DirectoryEntryTable));
729 	fDirectoryEntryTable->GetAllocationInfo(info);
730 	// node hierarchy
731 	fRootDirectory->GetAllocationInfo(info);
732 	// name
733 	info.AddStringAllocation(fName.GetLength());
734 }
735 
736 // ReadLock
737 bool
738 Volume::ReadLock()
739 {
740 	bool ok = rw_lock_read_lock(&fLocker) == B_OK;
741 	if (ok && fLocker.owner_count > 1)
742 		fAccessTime = system_time();
743 	return ok;
744 }
745 
746 // ReadUnlock
747 void
748 Volume::ReadUnlock()
749 {
750 	rw_lock_read_unlock(&fLocker);
751 }
752 
753 // WriteLock
754 bool
755 Volume::WriteLock()
756 {
757 	bool ok = rw_lock_write_lock(&fLocker) == B_OK;
758 	if (ok && fLocker.owner_count > 1)
759 		fAccessTime = system_time();
760 	return ok;
761 }
762 
763 // WriteUnlock
764 void
765 Volume::WriteUnlock()
766 {
767 	rw_lock_write_unlock(&fLocker);
768 }
769 
770 // IteratorLock
771 bool
772 Volume::IteratorLock()
773 {
774 	return recursive_lock_lock(&fIteratorLocker) == B_OK;
775 }
776 
777 // IteratorUnlock
778 void
779 Volume::IteratorUnlock()
780 {
781 	recursive_lock_unlock(&fIteratorLocker);
782 }
783 
784