xref: /haiku/src/add-ons/kernel/file_systems/netfs/client/VirtualVolume.cpp (revision 0de25abadc86e260328c6f7c4255acbee8f70d4e)
1 // VirtualVolume.cpp
2 
3 #include <new>
4 #include <stdio.h>
5 #include <string.h>
6 
7 #include <AutoLocker.h>
8 
9 #include "Compatibility.h"
10 #include "DebugSupport.h"
11 #include "QueryIterator.h"
12 #include "QueryManager.h"
13 #include "VirtualDir.h"
14 #include "VirtualVolume.h"
15 #include "VolumeManager.h"
16 #include "VolumeSupport.h"
17 
18 // constructor
19 VirtualVolume::VirtualVolume(VolumeManager* volumeManager)
20 	: Volume(volumeManager),
21 	  fRootNode(NULL)
22 {
23 }
24 
25 // destructor
26 VirtualVolume::~VirtualVolume()
27 {
28 	delete fRootNode;
29 }
30 
31 // Init
32 status_t
33 VirtualVolume::Init(const char* name)
34 {
35 	status_t error = Volume::Init(name);
36 	if (error != B_OK)
37 		return error;
38 
39 	// get an ID for the root node
40 	vnode_id rootNodeID = fVolumeManager->NewNodeID(this);
41 	if (rootNodeID < 0) {
42 		Uninit();
43 		return rootNodeID;
44 	}
45 
46 	// create the root node
47 	fRootNode = new(std::nothrow) VirtualDir(this, rootNodeID);
48 	if (!fRootNode) {
49 		Uninit();
50 		fVolumeManager->RemoveNodeID(rootNodeID);
51 		return B_NO_MEMORY;
52 	}
53 	error = fRootNode->InitCheck();
54 	if (error != B_OK) {
55 		Uninit();
56 		return error;
57 	}
58 
59 	return B_OK;
60 }
61 
62 // Uninit
63 void
64 VirtualVolume::Uninit()
65 {
66 	if (fRootNode) {
67 		fVolumeManager->RemoveNodeID(fRootNode->GetID());
68 		delete fRootNode;
69 		fRootNode = NULL;
70 	}
71 
72 	Volume::Uninit();
73 }
74 
75 // GetRootNode
76 Node*
77 VirtualVolume::GetRootNode() const
78 {
79 	return fRootNode;
80 }
81 
82 // PrepareToUnmount
83 void
84 VirtualVolume::PrepareToUnmount()
85 {
86 	Volume::PrepareToUnmount();
87 
88 	// remove all child volumes
89 
90 	// init a directory iterator
91 	fLock.Lock();
92 	VirtualDirIterator iterator;
93 	iterator.SetDirectory(fRootNode, true);
94 
95 	// iterate through the directory
96 	const char* name;
97 	Node* node;
98 	while (iterator.GetCurrentEntry(&name, &node)) {
99 		iterator.NextEntry();
100 		Volume* volume = fVolumeManager->GetVolume(node->GetID());
101 		fLock.Unlock();
102 		if (volume) {
103 			RemoveChildVolume(volume);
104 			volume->SetUnmounting(true);
105 			volume->PutVolume();
106 		}
107 		fLock.Lock();
108 	}
109 
110 	// uninit the directory iterator
111 	iterator.SetDirectory(NULL);
112 
113 	// remove the our root node
114 	vnode_id rootNodeID = fRootNode->GetID();
115 
116 	fLock.Unlock();
117 
118 	if (GetVNode(rootNodeID, &node) == B_OK) {
119 		Volume::RemoveVNode(rootNodeID);
120 		PutVNode(rootNodeID);
121 	}
122 }
123 
124 // AddChildVolume
125 status_t
126 VirtualVolume::AddChildVolume(Volume* volume)
127 {
128 	if (!volume)
129 		return B_BAD_VALUE;
130 
131 	// don't add anything, if we are already unmounting
132 	AutoLocker<Locker> locker(fLock);
133 	if (fUnmounting)
134 		return B_BAD_VALUE;
135 
136 	// get and check the node name
137 	char name[B_FILE_NAME_LENGTH];
138 	int32 nameLen = strlen(volume->GetName());
139 	if (nameLen == 0 || nameLen >= B_FILE_NAME_LENGTH)
140 		return B_BAD_VALUE;
141 	strcpy(name, volume->GetName());
142 
143 	// add the volume's root node
144 	status_t error = fRootNode->AddEntry(name, volume->GetRootNode());
145 	if (error != B_OK)
146 		return error;
147 
148 	// set the volume's parent volume
149 	volume->SetParentVolume(this);
150 	AcquireReference();
151 
152 	// send out a notification
153 	vnode_id dirID = fRootNode->GetID();
154 	vnode_id nodeID = volume->GetRootID();
155 	locker.Unlock();
156 	NotifyListener(B_ENTRY_CREATED, fVolumeManager->GetID(), dirID, 0, nodeID,
157 		name);
158 
159 	return B_OK;
160 }
161 
162 // RemoveChildVolume
163 void
164 VirtualVolume::RemoveChildVolume(Volume* volume)
165 {
166 	if (!volume)
167 		return;
168 
169 	// check, if the volume's root node is a child of our root node
170 	AutoLocker<Locker> locker(fLock);
171 	Node* node = fRootNode->GetChildNode(volume->GetName());
172 	if (!node)
173 		return;
174 	if (node != volume->GetRootNode())
175 		return;
176 
177 	// remove it
178 	fRootNode->RemoveEntry(volume->GetName());
179 	volume->SetParentVolume(NULL);
180 
181 	// get the data needed for the node monitoring notification
182 	vnode_id dirID = fRootNode->GetID();
183 	vnode_id nodeID = volume->GetRootID();
184 	char name[B_FILE_NAME_LENGTH];
185 	strcpy(name, volume->GetName());
186 
187 	// surrender the child volumes reference to us
188 	// Since the caller of this method must have a valid reference to us,
189 	// this is unproblematic, even if fLock is being held, while this method
190 	// is invoked.
191 	locker.Unlock();
192 	PutVolume();
193 
194 	// send out a notification
195 	locker.Unlock();
196 	NotifyListener(B_ENTRY_REMOVED, fVolumeManager->GetID(), dirID, 0, nodeID,
197 		name);
198 }
199 
200 // GetChildVolume
201 Volume*
202 VirtualVolume::GetChildVolume(const char* name)
203 {
204 	if (!name)
205 		return NULL;
206 
207 	// get the child node of the root node
208 	AutoLocker<Locker> locker(fLock);
209 	Node* node = fRootNode->GetChildNode(name);
210 	if (!node)
211 		return NULL;
212 
213 	// get its volume, if it is the root volume
214 	Volume* volume = node->GetVolume();
215 	if (volume->GetRootNode() != node)
216 		return NULL;
217 	locker.Unlock();
218 
219 	return fVolumeManager->GetVolume(node->GetID());
220 }
221 
222 // GetUniqueEntryName
223 status_t
224 VirtualVolume::GetUniqueEntryName(const char* baseName, char* buffer)
225 {
226 	if (!baseName || !buffer)
227 		return B_BAD_VALUE;
228 
229 	// check the base name len
230 	int32 baseLen = strlen(baseName);
231 	if (baseLen == 0 || baseLen >= B_FILE_NAME_LENGTH)
232 		return B_BAD_VALUE;
233 
234 	strcpy(buffer, baseName);
235 
236 	AutoLocker<Locker> _(fLock);
237 
238 	// adjust the name, if necessary
239 	int32 suffixNumber = 2;
240 	while (fRootNode->GetChildNode(baseName)) {
241 		// create a suffix
242 		char suffix[13];
243 		sprintf(suffix, " %" B_PRId32, suffixNumber);
244 		suffixNumber++;
245 
246 		// check the len
247 		int32 suffixLen = strlen(suffix);
248 		if (baseLen + suffixLen >= B_FILE_NAME_LENGTH)
249 			return B_NAME_TOO_LONG;
250 
251 		// compose the final name
252 		strcpy(buffer + baseLen, suffix);
253 	}
254 
255 	return B_OK;
256 }
257 
258 
259 // #pragma mark -
260 // #pragma mark ----- FS -----
261 
262 // Unmount
263 status_t
264 VirtualVolume::Unmount()
265 {
266 	return B_OK;
267 }
268 
269 // Sync
270 status_t
271 VirtualVolume::Sync()
272 {
273 // TODO: Recursively call Sync().
274 	return B_BAD_VALUE;
275 }
276 
277 
278 // #pragma mark -
279 // #pragma mark ----- vnodes -----
280 
281 // ReadVNode
282 status_t
283 VirtualVolume::ReadVNode(vnode_id vnid, char reenter, Node** node)
284 {
285 	if (vnid != GetRootID())
286 		return B_BAD_VALUE;
287 
288 	AutoLocker<Locker> _(fLock);
289 	fRootNode->SetKnownToVFS(true);
290 	*node = fRootNode;
291 
292 	// add a volume reference for the node
293 	AcquireReference();
294 
295 	return B_OK;
296 }
297 
298 // WriteVNode
299 status_t
300 VirtualVolume::WriteVNode(Node* node, char reenter)
301 {
302 	if (node != fRootNode)
303 		return B_BAD_VALUE;
304 
305 	AutoLocker<Locker> locker(fLock);
306 	fRootNode->SetKnownToVFS(false);
307 
308 	// surrender the volume reference of the node
309 	locker.Unlock();
310 	PutVolume();
311 
312 	return B_OK;
313 }
314 
315 // RemoveVNode
316 status_t
317 VirtualVolume::RemoveVNode(Node* node, char reenter)
318 {
319 	if (node != fRootNode)
320 		return B_BAD_VALUE;
321 
322 	AutoLocker<Locker> locker(fLock);
323 	fRootNode->SetKnownToVFS(false);
324 
325 	// surrender the volume reference of the node
326 	locker.Unlock();
327 	PutVolume();
328 
329 	return B_OK;
330 }
331 
332 // #pragma mark -
333 // #pragma mark ----- nodes -----
334 
335 // FSync
336 status_t
337 VirtualVolume::FSync(Node* node)
338 {
339 	return B_OK;
340 }
341 
342 // ReadStat
343 status_t
344 VirtualVolume::ReadStat(Node* node, struct stat* st)
345 {
346 	if (node != fRootNode)
347 		return B_BAD_VALUE;
348 
349 	AutoLocker<Locker> _(fLock);
350 	st->st_ino = node->GetID();
351 	st->st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH
352 		| S_IXOTH;
353 	st->st_nlink = 1;
354 	st->st_size = 1;
355 	st->st_blksize = 1024;
356 	st->st_crtime = fRootNode->GetCreationTime();
357 	st->st_ctime = st->st_mtime = st->st_atime = st->st_crtime;
358 	st->st_dev = fVolumeManager->GetID();
359 	// we set the UID/GID fields to the one who mounted the FS
360 	st->st_uid = fVolumeManager->GetMountUID();
361 	st->st_gid = fVolumeManager->GetMountGID();
362 	return B_OK;
363 }
364 
365 // WriteStat
366 status_t
367 VirtualVolume::WriteStat(Node* node, struct stat *st, uint32 mask)
368 {
369 	return B_NOT_ALLOWED;
370 }
371 
372 // Access
373 status_t
374 VirtualVolume::Access(Node* node, int mode)
375 {
376 	// TODO: Implement!
377 	return B_OK;
378 }
379 
380 // #pragma mark -
381 // #pragma mark ----- files -----
382 
383 // Create
384 status_t
385 VirtualVolume::Create(Node* dir, const char* name, int openMode, int mode,
386 	vnode_id* vnid, void** cookie)
387 {
388 	return B_NOT_ALLOWED;
389 }
390 
391 // Open
392 status_t
393 VirtualVolume::Open(Node* node, int openMode, void** cookie)
394 {
395 	if (node != fRootNode)
396 		return B_BAD_VALUE;
397 
398 	// we're read-only
399 	if ((openMode & O_RWMASK) == O_WRONLY)
400 		return B_PERMISSION_DENIED;
401 	if (openMode & O_TRUNC)
402 		return B_PERMISSION_DENIED;
403 	if ((openMode & O_RWMASK) == O_RDWR)
404 		openMode = (openMode & ~O_RWMASK) | O_RDONLY;
405 
406 	// set the result
407 	*cookie = NULL;
408 	return B_OK;
409 }
410 
411 // Close
412 status_t
413 VirtualVolume::Close(Node* node, void* cookie)
414 {
415 	// no-op: FreeCookie() does the job
416 	return B_OK;
417 }
418 
419 // FreeCookie
420 status_t
421 VirtualVolume::FreeCookie(Node* node, void* cookie)
422 {
423 	// nothing to do: we didn't allocate anything
424 	return B_OK;
425 }
426 
427 // Read
428 status_t
429 VirtualVolume::Read(Node* node, void* cookie, off_t pos, void* _buffer,
430 	size_t bufferSize, size_t* _bytesRead)
431 {
432 	// can't read() directories
433 	return B_NOT_ALLOWED;
434 }
435 
436 // Write
437 status_t
438 VirtualVolume::Write(Node* node, void* cookie, off_t pos, const void* _buffer,
439 	size_t bufferSize, size_t* bytesWritten)
440 {
441 	// can't write() directories
442 	return B_NOT_ALLOWED;
443 }
444 
445 // IOCtl
446 status_t
447 VirtualVolume::IOCtl(Node* node, void* cookie, int cmd, void* buffer,
448 	size_t bufferSize)
449 {
450 	return B_BAD_VALUE;
451 }
452 
453 // SetFlags
454 status_t
455 VirtualVolume::SetFlags(Node* node, void* cookie, int flags)
456 {
457 	return B_BAD_VALUE;
458 }
459 
460 // #pragma mark -
461 // #pragma mark ----- hard links / symlinks -----
462 
463 // Link
464 status_t
465 VirtualVolume::Link(Node* dir, const char* name, Node* node)
466 {
467 	// we're read-only
468 	return B_NOT_ALLOWED;
469 }
470 
471 // Unlink
472 status_t
473 VirtualVolume::Unlink(Node* dir, const char* name)
474 {
475 	// we're read-only
476 	return B_NOT_ALLOWED;
477 }
478 
479 // Symlink
480 status_t
481 VirtualVolume::Symlink(Node* dir, const char* name, const char* target)
482 {
483 	// we're read-only
484 	return B_NOT_ALLOWED;
485 }
486 
487 // ReadLink
488 status_t
489 VirtualVolume::ReadLink(Node* node, char* buffer, size_t bufferSize,
490 	size_t* bytesRead)
491 {
492 	// there are no symlinks
493 	return B_BAD_VALUE;
494 }
495 
496 // Rename
497 status_t
498 VirtualVolume::Rename(Node* oldDir, const char* oldName, Node* newDir,
499 	const char* newName)
500 {
501 	// we're read-only
502 	return B_NOT_ALLOWED;
503 }
504 
505 // #pragma mark -
506 // #pragma mark ----- directories -----
507 
508 // MkDir
509 status_t
510 VirtualVolume::MkDir(Node* dir, const char* name, int mode)
511 {
512 	// we're read-only
513 	return B_NOT_ALLOWED;
514 }
515 
516 // RmDir
517 status_t
518 VirtualVolume::RmDir(Node* dir, const char* name)
519 {
520 	// we're read-only
521 	return B_NOT_ALLOWED;
522 }
523 
524 // OpenDir
525 status_t
526 VirtualVolume::OpenDir(Node* node, void** cookie)
527 {
528 	if (node != fRootNode)
529 		return B_BAD_VALUE;
530 
531 	// allocate an iterator
532 	VirtualDirIterator* iterator = new(std::nothrow) VirtualDirIterator;
533 	if (!iterator)
534 		return B_NO_MEMORY;
535 
536 	AutoLocker<Locker> locker(fLock);
537 	iterator->SetDirectory(fRootNode);
538 	*cookie = iterator;
539 	return B_OK;
540 }
541 
542 // CloseDir
543 status_t
544 VirtualVolume::CloseDir(Node* node, void* cookie)
545 {
546 	// no-op: FreeDirCookie() does the job
547 	return B_OK;
548 }
549 
550 // FreeDirCookie
551 status_t
552 VirtualVolume::FreeDirCookie(Node* node, void* cookie)
553 {
554 	if (node != fRootNode)
555 		return B_BAD_VALUE;
556 
557 	// delete the iterator
558 	AutoLocker<Locker> locker(fLock);
559 	VirtualDirIterator* iterator = (VirtualDirIterator*)cookie;
560 	delete iterator;
561 	return B_OK;
562 }
563 
564 // ReadDir
565 status_t
566 VirtualVolume::ReadDir(Node* node, void* cookie, struct dirent* buffer,
567 	size_t bufferSize, int32 count, int32* countRead)
568 {
569 	if (node != fRootNode)
570 		return B_BAD_VALUE;
571 
572 	*countRead = 0;
573 
574 	AutoLocker<Locker> locker(fLock);
575 	VirtualDirIterator* iterator = (VirtualDirIterator*)cookie;
576 
577 	// get the current entry
578 	const char* name;
579 	Node* child;
580 	if (!iterator->GetCurrentEntry(&name, &child))
581 		return B_OK;
582 
583 	// set the name: this also checks the size of the buffer
584 	status_t error = set_dirent_name(buffer, bufferSize, name, strlen(name));
585 	if (error != B_OK)
586 		RETURN_ERROR(error);
587 
588 	// fill in the other fields
589 	buffer->d_pdev = fVolumeManager->GetID();
590 	buffer->d_pino = fRootNode->GetID();
591 	buffer->d_dev = fVolumeManager->GetID();
592 	buffer->d_ino = child->GetID();
593 	*countRead = 1;
594 
595 	// fix d_ino, if this is the parent of the root node
596 	if (strcmp(name, "..") == 0) {
597 		if (Volume* parentVolume = GetParentVolume())
598 			buffer->d_ino = parentVolume->GetRootID();
599 	}
600 
601 	iterator->NextEntry();
602 	return B_OK;
603 }
604 
605 // RewindDir
606 status_t
607 VirtualVolume::RewindDir(Node* node, void* cookie)
608 {
609 	if (node != fRootNode)
610 		return B_BAD_VALUE;
611 
612 	AutoLocker<Locker> locker(fLock);
613 	VirtualDirIterator* iterator = (VirtualDirIterator*)cookie;
614 	iterator->Rewind();
615 
616 	return B_OK;
617 }
618 
619 // Walk
620 status_t
621 VirtualVolume::Walk(Node* dir, const char* entryName, char** resolvedPath,
622 	vnode_id* vnid)
623 {
624 	if (dir != fRootNode)
625 		return B_BAD_VALUE;
626 
627 	// get the referred to node ID
628 	AutoLocker<Locker> locker(fLock);
629 	if (strcmp(entryName, ".") == 0) {
630 		*vnid = dir->GetID();
631 	} else if (strcmp(entryName, "..") == 0) {
632 		if (Volume* parentVolume = GetParentVolume())
633 			*vnid = parentVolume->GetRootID();
634 		else
635 			*vnid = dir->GetID();
636 	} else {
637 		Node* node = fRootNode->GetChildNode(entryName);
638 		if (!node)
639 			return B_ENTRY_NOT_FOUND;
640 		*vnid = node->GetID();
641 	}
642 	locker.Unlock();
643 
644 	// get a VFS node reference
645 	Node* dummyNode;
646 	status_t error = GetVNode(*vnid, &dummyNode);
647 	if (error != B_OK)
648 		return error;
649 	return B_OK;
650 }
651 
652 // #pragma mark -
653 // #pragma mark ----- attributes -----
654 
655 // OpenAttrDir
656 status_t
657 VirtualVolume::OpenAttrDir(Node* node, void** cookie)
658 {
659 	if (node != fRootNode)
660 		return B_BAD_VALUE;
661 
662 	// we support no attributes at this time
663 	*cookie = NULL;
664 	return B_OK;
665 }
666 
667 // CloseAttrDir
668 status_t
669 VirtualVolume::CloseAttrDir(Node* node, void* cookie)
670 {
671 	// no-op: FreeAttrDirCookie() does the job
672 	return B_OK;
673 }
674 
675 // FreeAttrDirCookie
676 status_t
677 VirtualVolume::FreeAttrDirCookie(Node* node, void* _cookie)
678 {
679 	// nothing to do: we didn't allocate anything
680 	return B_OK;
681 }
682 
683 // ReadAttrDir
684 status_t
685 VirtualVolume::ReadAttrDir(Node* node, void* _cookie, struct dirent* buffer,
686 	size_t bufferSize, int32 count, int32* countRead)
687 {
688 	// no attributes for the time being
689 	*countRead = 0;
690 	return B_OK;
691 }
692 
693 // RewindAttrDir
694 status_t
695 VirtualVolume::RewindAttrDir(Node* node, void* _cookie)
696 {
697 	return B_OK;
698 }
699 
700 // ReadAttr
701 status_t
702 VirtualVolume::ReadAttr(Node* node, const char* name, int type, off_t pos,
703 	void* _buffer, size_t bufferSize, size_t* bytesRead)
704 {
705 	// no attributes for the time being
706 	*bytesRead = 0;
707 	return B_ENTRY_NOT_FOUND;
708 }
709 
710 // WriteAttr
711 status_t
712 VirtualVolume::WriteAttr(Node* node, const char* name, int type, off_t pos,
713 	const void* _buffer, size_t bufferSize, size_t* bytesWritten)
714 {
715 	// no attributes for the time being
716 	*bytesWritten = 0;
717 	return B_NOT_ALLOWED;
718 }
719 
720 // RemoveAttr
721 status_t
722 VirtualVolume::RemoveAttr(Node* node, const char* name)
723 {
724 	return B_NOT_ALLOWED;
725 }
726 
727 // RenameAttr
728 status_t
729 VirtualVolume::RenameAttr(Node* node, const char* oldName, const char* newName)
730 {
731 	// no attributes for the time being
732 	return B_ENTRY_NOT_FOUND;
733 }
734 
735 // StatAttr
736 status_t
737 VirtualVolume::StatAttr(Node* node, const char* name, struct attr_info* attrInfo)
738 {
739 	// no attributes for the time being
740 	return B_ENTRY_NOT_FOUND;
741 }
742 
743 // #pragma mark -
744 // #pragma mark ----- queries -----
745 
746 // OpenQuery
747 status_t
748 VirtualVolume::OpenQuery(const char* queryString, uint32 flags, port_id port,
749 	int32 token, QueryIterator** _iterator)
750 {
751 	QueryManager* queryManager = fVolumeManager->GetQueryManager();
752 
753 	// allocate a hierarchical iterator
754 	HierarchicalQueryIterator* iterator
755 		= new(std::nothrow) HierarchicalQueryIterator(this);
756 	if (!iterator)
757 		return B_NO_MEMORY;
758 
759 	// add it to the query manager
760 	status_t error = queryManager->AddIterator(iterator);
761 	if (error != B_OK) {
762 		delete iterator;
763 		return error;
764 	}
765 	QueryIteratorPutter iteratorPutter(queryManager, iterator);
766 
767 	// iterate through the child volumes and open subqueries for them
768 	// init a directory iterator
769 	fLock.Lock();
770 	VirtualDirIterator dirIterator;
771 	dirIterator.SetDirectory(fRootNode, true);
772 
773 	// iterate through the directory
774 	const char* name;
775 	Node* node;
776 	while (dirIterator.GetCurrentEntry(&name, &node)) {
777 		dirIterator.NextEntry();
778 		Volume* volume = fVolumeManager->GetVolume(node->GetID());
779 		fLock.Unlock();
780 
781 		// open the subquery
782 		QueryIterator* subIterator;
783 		if (volume->OpenQuery(queryString, flags, port, token,
784 			&subIterator) == B_OK) {
785 			// add the subiterator
786 			if (queryManager->AddSubIterator(iterator, subIterator) != B_OK)
787 				queryManager->PutIterator(subIterator);
788 		}
789 		volume->PutVolume();
790 
791 		fLock.Lock();
792 	}
793 
794 	// uninit the directory iterator
795 	dirIterator.SetDirectory(NULL);
796 	fLock.Unlock();
797 
798 	// return the result
799 	*_iterator = iterator;
800 	iteratorPutter.Detach();
801 
802 	return B_OK;
803 }
804 
805 // FreeQueryIterator
806 void
807 VirtualVolume::FreeQueryIterator(QueryIterator* iterator)
808 {
809 	delete iterator;
810 }
811 
812 // ReadQuery
813 status_t
814 VirtualVolume::ReadQuery(QueryIterator* _iterator, struct dirent* buffer,
815 	size_t bufferSize, int32 count, int32* countRead)
816 {
817 	HierarchicalQueryIterator* iterator
818 		= dynamic_cast<HierarchicalQueryIterator*>(_iterator);
819 
820 	QueryManager* queryManager = fVolumeManager->GetQueryManager();
821 	while (QueryIterator* subIterator
822 			= queryManager->GetCurrentSubIterator(iterator)) {
823 		QueryIteratorPutter _(queryManager, subIterator);
824 
825 		status_t error = subIterator->GetVolume()->ReadQuery(subIterator,
826 			buffer, bufferSize, count, countRead);
827 		if (error != B_OK)
828 			return error;
829 
830 		if (*countRead > 0)
831 			return B_OK;
832 
833 		queryManager->NextSubIterator(iterator, subIterator);
834 	}
835 
836 	*countRead = 0;
837 	return B_OK;
838 }
839 
840