xref: /haiku/src/add-ons/kernel/file_systems/ramfs/Volume.cpp (revision cb29eafe2586fdb2d7685afa69fdab5d88a8b576)
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 = fRootDirectory->Link(NULL);
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 	status_t error = NodeRemoved(node);
361 	if (error == B_OK)
362 		delete node;
363 	return error;
364 }
365 
366 // UnremoveVNode
367 status_t
368 Volume::UnremoveVNode(Node *node)
369 {
370 	return (fMounted ? unremove_vnode(FSVolume(), node->GetID()) : B_BAD_VALUE);
371 }
372 
373 // NodeAdded
374 status_t
375 Volume::NodeAdded(Node *node)
376 {
377 	status_t error = (node ? B_OK : B_BAD_VALUE);
378 	if (error == B_OK) {
379 		error = fNodeTable->AddNode(node);
380 		// notify listeners
381 		if (error == B_OK) {
382 			// listeners interested in that node
383 			NodeListenerTree::Iterator it;
384 			if (fNodeListeners->FindFirst(node, &it)) {
385 				for (NodeListenerValue *value = it.GetCurrent();
386 					 value && value->node == node;
387 					 value = it.GetNext()) {
388 					 if (value->flags & NODE_LISTEN_ADDED)
389 						 value->listener->NodeAdded(node);
390 				}
391 			}
392 			// listeners interested in any node
393 			int32 count = fAnyNodeListeners.CountItems();
394 			for (int32 i = 0; i < count; i++) {
395 				const NodeListenerValue &value = fAnyNodeListeners.ItemAt(i);
396 				 if (value.flags & NODE_LISTEN_ADDED)
397 					 value.listener->NodeAdded(node);
398 			}
399 		}
400 	}
401 	return error;
402 }
403 
404 // NodeRemoved
405 status_t
406 Volume::NodeRemoved(Node *node)
407 {
408 	status_t error = (node ? B_OK : B_BAD_VALUE);
409 	if (error == B_OK) {
410 		error = fNodeTable->RemoveNode(node);
411 		// notify listeners
412 		if (error == B_OK) {
413 			// listeners interested in that node
414 			NodeListenerTree::Iterator it;
415 			if (fNodeListeners->FindFirst(node, &it)) {
416 				for (NodeListenerValue *value = it.GetCurrent();
417 					 value && value->node == node;
418 					 value = it.GetNext()) {
419 					 if (value->flags & NODE_LISTEN_REMOVED)
420 						 value->listener->NodeRemoved(node);
421 				}
422 			}
423 			// listeners interested in any node
424 			int32 count = fAnyNodeListeners.CountItems();
425 			for (int32 i = 0; i < count; i++) {
426 				const NodeListenerValue &value = fAnyNodeListeners.ItemAt(i);
427 				 if (value.flags & NODE_LISTEN_REMOVED)
428 					 value.listener->NodeRemoved(node);
429 			}
430 		}
431 	}
432 	return error;
433 }
434 
435 // FindNode
436 /*!	\brief Finds the node identified by a ino_t.
437 
438 	\note The method does not initialize the parent ID for non-directory nodes.
439 
440 	\param id ID of the node to be found.
441 	\param node pointer to a pre-allocated Node* to be set to the found node.
442 	\return \c B_OK, if everything went fine.
443 */
444 status_t
445 Volume::FindNode(ino_t id, Node **node)
446 {
447 	status_t error = (node ? B_OK : B_BAD_VALUE);
448 	if (error == B_OK) {
449 		*node = fNodeTable->GetNode(id);
450 		if (!*node)
451 			error = B_ENTRY_NOT_FOUND;
452 	}
453 	return error;
454 }
455 
456 // AddNodeListener
457 status_t
458 Volume::AddNodeListener(NodeListener *listener, Node *node, uint32 flags)
459 {
460 	// check parameters
461 	if (!listener || (!node && !(flags & NODE_LISTEN_ANY_NODE))
462 		|| !(flags & NODE_LISTEN_ALL)) {
463 		return B_BAD_VALUE;
464 	}
465 	// add the listener to the right container
466 	status_t error = B_OK;
467 	NodeListenerValue value(listener, node, flags);
468 	if (flags & NODE_LISTEN_ANY_NODE) {
469 		if (!fAnyNodeListeners.AddItem(value))
470 			error = B_NO_MEMORY;
471 	} else
472 		error = fNodeListeners->Insert(value);
473 	return error;
474 }
475 
476 // RemoveNodeListener
477 status_t
478 Volume::RemoveNodeListener(NodeListener *listener, Node *node)
479 {
480 	if (!listener)
481 		return B_BAD_VALUE;
482 	status_t error = B_OK;
483 	if (node)
484 		error = fNodeListeners->Remove(node, listener);
485 	else {
486 		NodeListenerValue value(listener, node, 0);
487 		if (!fAnyNodeListeners.RemoveItem(value))
488 			error = B_ENTRY_NOT_FOUND;
489 	}
490 	return error;
491 }
492 
493 // EntryAdded
494 status_t
495 Volume::EntryAdded(ino_t id, Entry *entry)
496 {
497 	status_t error = (entry ? B_OK : B_BAD_VALUE);
498 	if (error == B_OK) {
499 		error = fDirectoryEntryTable->AddEntry(id, entry);
500 		if (error == B_OK) {
501 			// notify listeners
502 			// listeners interested in that entry
503 			EntryListenerTree::Iterator it;
504 			if (fEntryListeners->FindFirst(entry, &it)) {
505 				for (EntryListenerValue *value = it.GetCurrent();
506 					 value && value->entry == entry;
507 					 value = it.GetNext()) {
508 					 if (value->flags & ENTRY_LISTEN_ADDED)
509 						 value->listener->EntryAdded(entry);
510 				}
511 			}
512 			// listeners interested in any entry
513 			int32 count = fAnyEntryListeners.CountItems();
514 			for (int32 i = 0; i < count; i++) {
515 				const EntryListenerValue &value = fAnyEntryListeners.ItemAt(i);
516 				 if (value.flags & ENTRY_LISTEN_ADDED)
517 					 value.listener->EntryAdded(entry);
518 			}
519 		}
520 	}
521 	return error;
522 }
523 
524 // EntryRemoved
525 status_t
526 Volume::EntryRemoved(ino_t id, Entry *entry)
527 {
528 	status_t error = (entry ? B_OK : B_BAD_VALUE);
529 	if (error == B_OK) {
530 		error = fDirectoryEntryTable->RemoveEntry(id, entry);
531 		if (error == B_OK) {
532 			// notify listeners
533 			// listeners interested in that entry
534 			EntryListenerTree::Iterator it;
535 			if (fEntryListeners->FindFirst(entry, &it)) {
536 				for (EntryListenerValue *value = it.GetCurrent();
537 					 value && value->entry == entry;
538 					 value = it.GetNext()) {
539 					 if (value->flags & ENTRY_LISTEN_REMOVED)
540 						 value->listener->EntryRemoved(entry);
541 				}
542 			}
543 			// listeners interested in any entry
544 			int32 count = fAnyEntryListeners.CountItems();
545 			for (int32 i = 0; i < count; i++) {
546 				const EntryListenerValue &value = fAnyEntryListeners.ItemAt(i);
547 				 if (value.flags & ENTRY_LISTEN_REMOVED)
548 					 value.listener->EntryRemoved(entry);
549 			}
550 		}
551 	}
552 	return error;
553 }
554 
555 // FindEntry
556 status_t
557 Volume::FindEntry(ino_t id, const char *name, Entry **entry)
558 {
559 	status_t error = (entry ? B_OK : B_BAD_VALUE);
560 	if (error == B_OK) {
561 		*entry = fDirectoryEntryTable->GetEntry(id, name);
562 		if (!*entry)
563 			error = B_ENTRY_NOT_FOUND;
564 	}
565 	return error;
566 }
567 
568 // AddEntryListener
569 status_t
570 Volume::AddEntryListener(EntryListener *listener, Entry *entry, uint32 flags)
571 {
572 	// check parameters
573 	if (!listener || (!entry && !(flags & ENTRY_LISTEN_ANY_ENTRY))
574 		|| !(flags & ENTRY_LISTEN_ALL)) {
575 		return B_BAD_VALUE;
576 	}
577 	// add the listener to the right container
578 	status_t error = B_OK;
579 	EntryListenerValue value(listener, entry, flags);
580 	if (flags & ENTRY_LISTEN_ANY_ENTRY) {
581 		if (!fAnyEntryListeners.AddItem(value))
582 			error = B_NO_MEMORY;
583 	} else
584 		error = fEntryListeners->Insert(value);
585 	return error;
586 }
587 
588 // RemoveEntryListener
589 status_t
590 Volume::RemoveEntryListener(EntryListener *listener, Entry *entry)
591 {
592 	if (!listener)
593 		return B_BAD_VALUE;
594 	status_t error = B_OK;
595 	if (entry)
596 		error = fEntryListeners->Remove(entry, listener);
597 	else {
598 		EntryListenerValue value(listener, entry, 0);
599 		if (!fAnyEntryListeners.RemoveItem(value))
600 			error = B_ENTRY_NOT_FOUND;
601 	}
602 	return error;
603 }
604 
605 // NodeAttributeAdded
606 status_t
607 Volume::NodeAttributeAdded(ino_t id, Attribute *attribute)
608 {
609 	status_t error = (attribute ? B_OK : B_BAD_VALUE);
610 	if (error == B_OK) {
611 		// notify the respective attribute index
612 		if (error == B_OK) {
613 			if (AttributeIndex *index = FindAttributeIndex(
614 					attribute->GetName(), attribute->GetType())) {
615 				index->Added(attribute);
616 			}
617 		}
618 	}
619 	return error;
620 }
621 
622 // NodeAttributeRemoved
623 status_t
624 Volume::NodeAttributeRemoved(ino_t id, Attribute *attribute)
625 {
626 	status_t error = (attribute ? B_OK : B_BAD_VALUE);
627 	if (error == B_OK) {
628 		// notify the respective attribute index
629 		if (error == B_OK) {
630 			if (AttributeIndex *index = FindAttributeIndex(
631 					attribute->GetName(), attribute->GetType())) {
632 				index->Removed(attribute);
633 			}
634 		}
635 
636 		// update live queries
637 		if (error == B_OK && attribute->GetNode()) {
638 			uint8 oldKey[kMaxIndexKeyLength];
639 			size_t oldLength;
640 			attribute->GetKey(oldKey, &oldLength);
641 			UpdateLiveQueries(NULL, attribute->GetNode(), attribute->GetName(),
642 				attribute->GetType(), oldKey, oldLength, NULL, 0);
643 		}
644 	}
645 	return error;
646 }
647 
648 // GetNameIndex
649 NameIndex *
650 Volume::GetNameIndex() const
651 {
652 	return (fIndexDirectory ? fIndexDirectory->GetNameIndex() : NULL);
653 }
654 
655 // GetLastModifiedIndex
656 LastModifiedIndex *
657 Volume::GetLastModifiedIndex() const
658 {
659 	return (fIndexDirectory ? fIndexDirectory->GetLastModifiedIndex() : NULL);
660 }
661 
662 // GetSizeIndex
663 SizeIndex *
664 Volume::GetSizeIndex() const
665 {
666 	return (fIndexDirectory ? fIndexDirectory->GetSizeIndex() : NULL);
667 }
668 
669 // FindIndex
670 Index *
671 Volume::FindIndex(const char *name)
672 {
673 	return (fIndexDirectory ? fIndexDirectory->FindIndex(name) : NULL);
674 }
675 
676 // FindAttributeIndex
677 AttributeIndex *
678 Volume::FindAttributeIndex(const char *name, uint32 type)
679 {
680 	return (fIndexDirectory
681 			? fIndexDirectory->FindAttributeIndex(name, type) : NULL);
682 }
683 
684 // AddQuery
685 void
686 Volume::AddQuery(Query *query)
687 {
688 	RecursiveLocker _(fQueryLocker);
689 
690 	if (query)
691 		fQueries.Insert(query);
692 }
693 
694 // RemoveQuery
695 void
696 Volume::RemoveQuery(Query *query)
697 {
698 	RecursiveLocker _(fQueryLocker);
699 
700 	if (query)
701 		fQueries.Remove(query);
702 }
703 
704 // UpdateLiveQueries
705 void
706 Volume::UpdateLiveQueries(Entry *entry, Node* node, const char *attribute,
707 	int32 type, const uint8 *oldKey, size_t oldLength, const uint8 *newKey,
708 	size_t newLength)
709 {
710 	RecursiveLocker _(fQueryLocker);
711 
712 	for (Query* query = fQueries.First();
713 		 query;
714 		 query = fQueries.GetNext(query)) {
715 		query->LiveUpdate(entry, node, attribute, type, oldKey, oldLength,
716 			newKey, newLength);
717 	}
718 }
719 
720 // GetAllocationInfo
721 void
722 Volume::GetAllocationInfo(AllocationInfo &info)
723 {
724 	// tables
725 	info.AddOtherAllocation(sizeof(NodeTable));
726 	fNodeTable->GetAllocationInfo(info);
727 	info.AddOtherAllocation(sizeof(DirectoryEntryTable));
728 	fDirectoryEntryTable->GetAllocationInfo(info);
729 	// node hierarchy
730 	fRootDirectory->GetAllocationInfo(info);
731 	// name
732 	info.AddStringAllocation(fName.GetLength());
733 }
734 
735 // ReadLock
736 bool
737 Volume::ReadLock()
738 {
739 	bool ok = rw_lock_read_lock(&fLocker) == B_OK;
740 	if (ok && fLocker.owner_count > 1)
741 		fAccessTime = system_time();
742 	return ok;
743 }
744 
745 // ReadUnlock
746 void
747 Volume::ReadUnlock()
748 {
749 	rw_lock_read_unlock(&fLocker);
750 }
751 
752 // WriteLock
753 bool
754 Volume::WriteLock()
755 {
756 	bool ok = rw_lock_write_lock(&fLocker) == B_OK;
757 	if (ok && fLocker.owner_count > 1)
758 		fAccessTime = system_time();
759 	return ok;
760 }
761 
762 // WriteUnlock
763 void
764 Volume::WriteUnlock()
765 {
766 	rw_lock_write_unlock(&fLocker);
767 }
768 
769 // IteratorLock
770 bool
771 Volume::IteratorLock()
772 {
773 	return recursive_lock_lock(&fIteratorLocker) == B_OK;
774 }
775 
776 // IteratorUnlock
777 void
778 Volume::IteratorUnlock()
779 {
780 	recursive_lock_unlock(&fIteratorLocker);
781 }
782 
783