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
19VirtualVolume::VirtualVolume(VolumeManager* volumeManager)
20	: Volume(volumeManager),
21	  fRootNode(NULL)
22{
23}
24
25// destructor
26VirtualVolume::~VirtualVolume()
27{
28	delete fRootNode;
29}
30
31// Init
32status_t
33VirtualVolume::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
63void
64VirtualVolume::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
76Node*
77VirtualVolume::GetRootNode() const
78{
79	return fRootNode;
80}
81
82// PrepareToUnmount
83void
84VirtualVolume::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
125status_t
126VirtualVolume::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
163void
164VirtualVolume::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
201Volume*
202VirtualVolume::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
223status_t
224VirtualVolume::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, " %ld", 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
263status_t
264VirtualVolume::Unmount()
265{
266	return B_OK;
267}
268
269// Sync
270status_t
271VirtualVolume::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
282status_t
283VirtualVolume::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
299status_t
300VirtualVolume::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
316status_t
317VirtualVolume::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
336status_t
337VirtualVolume::FSync(Node* node)
338{
339	return B_OK;
340}
341
342// ReadStat
343status_t
344VirtualVolume::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
366status_t
367VirtualVolume::WriteStat(Node* node, struct stat *st, uint32 mask)
368{
369	return B_NOT_ALLOWED;
370}
371
372// Access
373status_t
374VirtualVolume::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
384status_t
385VirtualVolume::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
392status_t
393VirtualVolume::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
412status_t
413VirtualVolume::Close(Node* node, void* cookie)
414{
415	// no-op: FreeCookie() does the job
416	return B_OK;
417}
418
419// FreeCookie
420status_t
421VirtualVolume::FreeCookie(Node* node, void* cookie)
422{
423	// nothing to do: we didn't allocate anything
424	return B_OK;
425}
426
427// Read
428status_t
429VirtualVolume::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
437status_t
438VirtualVolume::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
446status_t
447VirtualVolume::IOCtl(Node* node, void* cookie, int cmd, void* buffer,
448	size_t bufferSize)
449{
450	return B_BAD_VALUE;
451}
452
453// SetFlags
454status_t
455VirtualVolume::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
464status_t
465VirtualVolume::Link(Node* dir, const char* name, Node* node)
466{
467	// we're read-only
468	return B_NOT_ALLOWED;
469}
470
471// Unlink
472status_t
473VirtualVolume::Unlink(Node* dir, const char* name)
474{
475	// we're read-only
476	return B_NOT_ALLOWED;
477}
478
479// Symlink
480status_t
481VirtualVolume::Symlink(Node* dir, const char* name, const char* target)
482{
483	// we're read-only
484	return B_NOT_ALLOWED;
485}
486
487// ReadLink
488status_t
489VirtualVolume::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
497status_t
498VirtualVolume::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
509status_t
510VirtualVolume::MkDir(Node* dir, const char* name, int mode)
511{
512	// we're read-only
513	return B_NOT_ALLOWED;
514}
515
516// RmDir
517status_t
518VirtualVolume::RmDir(Node* dir, const char* name)
519{
520	// we're read-only
521	return B_NOT_ALLOWED;
522}
523
524// OpenDir
525status_t
526VirtualVolume::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
543status_t
544VirtualVolume::CloseDir(Node* node, void* cookie)
545{
546	// no-op: FreeDirCookie() does the job
547	return B_OK;
548}
549
550// FreeDirCookie
551status_t
552VirtualVolume::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
565status_t
566VirtualVolume::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
606status_t
607VirtualVolume::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
620status_t
621VirtualVolume::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
656status_t
657VirtualVolume::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
668status_t
669VirtualVolume::CloseAttrDir(Node* node, void* cookie)
670{
671	// no-op: FreeAttrDirCookie() does the job
672	return B_OK;
673}
674
675// FreeAttrDirCookie
676status_t
677VirtualVolume::FreeAttrDirCookie(Node* node, void* _cookie)
678{
679	// nothing to do: we didn't allocate anything
680	return B_OK;
681}
682
683// ReadAttrDir
684status_t
685VirtualVolume::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
694status_t
695VirtualVolume::RewindAttrDir(Node* node, void* _cookie)
696{
697	return B_OK;
698}
699
700// ReadAttr
701status_t
702VirtualVolume::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
711status_t
712VirtualVolume::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
721status_t
722VirtualVolume::RemoveAttr(Node* node, const char* name)
723{
724	return B_NOT_ALLOWED;
725}
726
727// RenameAttr
728status_t
729VirtualVolume::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
736status_t
737VirtualVolume::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
747status_t
748VirtualVolume::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
806void
807VirtualVolume::FreeQueryIterator(QueryIterator* iterator)
808{
809	delete iterator;
810}
811
812// ReadQuery
813status_t
814VirtualVolume::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