1// ClientConnection.cpp
2
3#include "ClientConnection.h"
4
5#include <new>
6#include <typeinfo>
7
8#include <dirent.h>
9#include <fcntl.h>
10#include <errno.h>
11#include <stdlib.h>
12#include <unistd.h>
13#include <utime.h>
14
15#include <AutoDeleter.h>
16#include <AutoLocker.h>
17#include <Entry.h>
18#include <fs_query.h>
19#include <GraphicsDefs.h>
20#include <HashMap.h>
21#include <NodeMonitor.h>
22#include <Path.h>
23#include <Rect.h>
24#include <Mime.h>
25
26#include <fsproto.h>
27
28#include "Compatibility.h"
29#include "Connection.h"
30#include "DebugSupport.h"
31#include "Directory.h"
32#include "Entry.h"
33#include "FDManager.h"
34#include "NodeHandle.h"
35#include "NodeHandleMap.h"
36#include "NodeMonitoringEvent.h"
37#include "Path.h"
38#include "RequestBufferReplacer.h"
39#include "RequestChannel.h"
40#include "RequestConnection.h"
41#include "RequestDumper.h"
42#include "RequestFlattener.h"
43#include "Requests.h"
44#include "SecurityContext.h"
45#include "ServerNodeID.h"
46#include "UserSecurityContext.h"
47#include "Utils.h"
48#include "Volume.h"
49#include "VolumeManager.h"
50
51static const int32 kMaxSaneReadLinkSize		= 10240;	// 10 KB
52static const int32 kMaxReadBufferSize		= 10240;	// 10 KB
53static const int32 kMaxReadDirBufferSize	= 10240;
54
55// Locking:
56//
57// fLock: Guards fReferenceCount and fClosed.
58// fSecurityContextLock: Guards fSecurityContext.
59// fVolumes: Guards the map itself.
60
61
62// #pragma mark -
63// #pragma mark ----- ClientConnection -----
64
65// ConnectionReference
66class ClientConnection::ConnectionReference {
67public:
68	ConnectionReference(ClientConnection* connection)
69		: fConnection(connection)
70	{
71		if (!fConnection || !fConnection->GetReference())
72			fConnection = NULL;
73	}
74
75	~ConnectionReference()
76	{
77		if (fConnection)
78			fConnection->PutReference();
79	}
80
81	bool IsValid() const
82	{
83		return fConnection;
84	}
85
86private:
87	ClientConnection*	fConnection;
88};
89
90// VolumeMap
91struct ClientConnection::VolumeMap
92	: public SynchronizedHashMap<HashKey32<int32>, ClientVolume*> {
93};
94
95// ClientVolumePutter
96class ClientConnection::ClientVolumePutter {
97public:
98	ClientVolumePutter(ClientConnection* connection, ClientVolume* volume)
99		: fConnection(connection),
100		  fVolume(volume)
101	{
102	}
103
104	~ClientVolumePutter()
105	{
106		if (fConnection && fVolume)
107			fConnection->_PutVolume(fVolume);
108	}
109
110	void Detach()
111	{
112		fConnection = NULL;
113		fVolume = NULL;
114	}
115
116private:
117	ClientConnection*	fConnection;
118	ClientVolume*		fVolume;
119};
120
121// VolumeNodeMonitoringEvent
122struct ClientConnection::VolumeNodeMonitoringEvent {
123	VolumeNodeMonitoringEvent(int32 volumeID, NodeMonitoringEvent* event)
124		: volumeID(volumeID),
125		  event(event)
126	{
127		if (event)
128			event->AcquireReference();
129	}
130
131	~VolumeNodeMonitoringEvent()
132	{
133		if (event)
134			event->ReleaseReference();
135	}
136
137	int32					volumeID;
138	NodeMonitoringEvent*	event;
139};
140
141// NodeMonitoringEventQueue
142struct ClientConnection::NodeMonitoringEventQueue
143	: BlockingQueue<NodeMonitoringRequest> {
144	NodeMonitoringEventQueue()
145		: BlockingQueue<NodeMonitoringRequest>("client NM requests")
146	{
147	}
148};
149
150// QueryHandleUnlocker
151struct ClientConnection::QueryHandleUnlocker {
152	QueryHandleUnlocker(ClientConnection* connection, NodeHandle* nodeHandle)
153		: fConnection(connection),
154		  fHandle(nodeHandle)
155	{
156	}
157
158	~QueryHandleUnlocker()
159	{
160		if (fConnection && fHandle) {
161			fConnection->_UnlockQueryHandle(fHandle);
162			fConnection = NULL;
163			fHandle = NULL;
164		}
165	}
166
167private:
168	ClientConnection*	fConnection;
169	NodeHandle*			fHandle;
170};
171
172// ClientVolumeFilter
173struct ClientConnection::ClientVolumeFilter {
174	virtual ~ClientVolumeFilter() {}
175
176	virtual bool FilterVolume(ClientConnection* connection,
177		ClientVolume* volume) = 0;
178};
179
180// HasQueryPermissionClientVolumeFilter
181struct ClientConnection::HasQueryPermissionClientVolumeFilter
182	: ClientConnection::ClientVolumeFilter {
183	virtual bool FilterVolume(ClientConnection* connection,
184		ClientVolume* volume)
185	{
186		return volume->GetSharePermissions().ImpliesQuerySharePermission();
187	}
188};
189
190
191// #pragma mark -
192
193// constructor
194ClientConnection::ClientConnection(Connection* connection,
195	SecurityContext* securityContext, User* user,
196	ClientConnectionListener* listener)
197	: RequestHandler(),
198	  ClientVolume::NodeMonitoringProcessor(),
199	  fConnection(NULL),
200	  fSecurityContext(securityContext),
201	  fSecurityContextLock("security context lock"),
202	  fUser(user),
203	  fVolumes(NULL),
204	  fQueryHandles(NULL),
205	  fListener(listener),
206	  fNodeMonitoringEvents(NULL),
207	  fNodeMonitoringProcessor(-1),
208	  fLock("client connection locker"),
209	  fReferenceCount(0),
210	  fInitialized(0),
211	  fClosed(false),
212	  fError(false),
213	  fInverseClientEndianess(false)
214{
215	fConnection = new(std::nothrow) RequestConnection(connection, this);
216	if (!fConnection)
217		delete connection;
218}
219
220// destructor
221ClientConnection::~ClientConnection()
222{
223	_Close();
224	delete fConnection;
225
226	// delete all volumes
227	for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();)
228		delete it.Next().value;
229	delete fVolumes;
230
231	delete fQueryHandles;
232	delete fNodeMonitoringEvents;
233}
234
235// Init
236status_t
237ClientConnection::Init()
238{
239	// create a client volume map
240	fVolumes = new(std::nothrow) VolumeMap;
241	if (!fVolumes)
242		return B_NO_MEMORY;
243	status_t error = fVolumes->InitCheck();
244	if (error != B_OK)
245		return error;
246
247	// create the query handle map
248	fQueryHandles = new(std::nothrow) NodeHandleMap("query handles");
249	if (!fQueryHandles)
250		return B_NO_MEMORY;
251	error = fQueryHandles->Init();
252	if (error != B_OK)
253		return error;
254
255	// create the node monitoring event queue
256	fNodeMonitoringEvents = new(std::nothrow) NodeMonitoringEventQueue;
257	if (!fNodeMonitoringEvents)
258		return B_NO_MEMORY;
259	error = fNodeMonitoringEvents->InitCheck();
260	if (error != B_OK)
261		return error;
262
263	// initialize the connection
264	error = fConnection->Init();
265	if (error != B_OK)
266		return error;
267
268	// start the node monitoring processor
269	fNodeMonitoringProcessor = spawn_thread(_NodeMonitoringProcessorEntry,
270		"client connection NM processor", B_NORMAL_PRIORITY, this);
271	if (fNodeMonitoringProcessor < 0) {
272		_Close();
273		return fNodeMonitoringProcessor;
274	}
275	resume_thread(fNodeMonitoringProcessor);
276	return B_OK;
277}
278
279// Close
280/*!
281	Called by the NetFSServer. Not for internal use. Waits for the connection
282	to be closed (at least for the node monitoring thread to be gone).
283*/
284void
285ClientConnection::Close()
286{
287	{
288		ConnectionReference connectionReference(this);
289		if (connectionReference.IsValid())
290			_MarkClosed(false);
291		fListener = NULL;
292	}
293
294	// Wait at least for the node monitoring processor; this is not perfect,
295	// but not too bad either.
296	if (fNodeMonitoringProcessor >= 0
297		&& find_thread(NULL) != fNodeMonitoringProcessor) {
298		int32 result;
299		wait_for_thread(fNodeMonitoringProcessor, &result);
300	}
301}
302
303// GetReference
304bool
305ClientConnection::GetReference()
306{
307	AutoLocker<Locker> _(fLock);
308	if (fClosed || !atomic_or(&fInitialized, 0))
309		return false;
310	fReferenceCount++;
311	return true;
312}
313
314// PutReference
315void
316ClientConnection::PutReference()
317{
318	bool close = false;
319	{
320		AutoLocker<Locker> _(fLock);
321		--fReferenceCount;
322		if (fClosed)
323			close = (fReferenceCount == 0);
324	}
325	if (close)
326		_Close();
327}
328
329// UserRemoved
330void
331ClientConnection::UserRemoved(User* user)
332{
333	// get all volumes
334	ClientVolume** volumes = NULL;
335	int32 volumeCount = 0;
336	AutoLocker<VolumeMap> volumesLocker(fVolumes);
337	volumes = new(std::nothrow) ClientVolume*[fVolumes->Size()];
338	if (!volumes) {
339		ERROR(("ClientConnection::UserRemoved(): ERROR: Failed to "
340			"allocate memory for volume array.\n"));
341		volumesLocker.Unlock();
342		_UnmountAllVolumes();
343		return;
344	}
345	for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) {
346		if (ClientVolume* volume = _GetVolume(it.Next().value->GetID()))
347			volumes[volumeCount++] = volume;
348	}
349	volumesLocker.Unlock();
350
351	// unmount the concerned volumes
352	for (int32 i = 0; i < volumeCount; i++) {
353		ClientVolume* volume = volumes[i];
354
355		fSecurityContextLock.Lock();
356		bool unmount = (volume->GetSecurityContext()->GetUser() == user);
357		fSecurityContextLock.Unlock();
358
359		if (unmount)
360			_UnmountVolume(volume);
361	}
362
363	// put the volumes
364	for (int32 i = 0; i < volumeCount; i++)
365		_PutVolume(volumes[i]);
366	delete[] volumes;
367}
368
369// ShareRemoved
370void
371ClientConnection::ShareRemoved(Share* share)
372{
373	// get all volumes
374	ClientVolume** volumes = NULL;
375	int32 volumeCount = 0;
376	AutoLocker<VolumeMap> volumesLocker(fVolumes);
377	volumes = new(std::nothrow) ClientVolume*[fVolumes->Size()];
378	if (!volumes) {
379		ERROR(("ClientConnection::ShareRemoved(): ERROR: Failed to "
380			"allocate memory for volume array.\n"));
381		volumesLocker.Unlock();
382		_UnmountAllVolumes();
383		return;
384	}
385	for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) {
386		if (ClientVolume* volume = _GetVolume(it.Next().value->GetID()))
387			volumes[volumeCount++] = volume;
388	}
389	volumesLocker.Unlock();
390
391	// unmount the concerned volumes
392	for (int32 i = 0; i < volumeCount; i++) {
393		ClientVolume* volume = volumes[i];
394
395		fSecurityContextLock.Lock();
396		bool unmount = (volume->GetShare() == share);
397		fSecurityContextLock.Unlock();
398
399		if (unmount)
400			_UnmountVolume(volume);
401	}
402
403	// put the volumes
404	for (int32 i = 0; i < volumeCount; i++)
405		_PutVolume(volumes[i]);
406	delete[] volumes;
407}
408
409// UserPermissionsChanged
410void
411ClientConnection::UserPermissionsChanged(Share* share, User* user,
412	Permissions permissions)
413{
414	bool unmountAll = (!permissions.ImpliesMountSharePermission());
415
416	// get all volumes
417	ClientVolume** volumes = NULL;
418	int32 volumeCount = 0;
419	AutoLocker<VolumeMap> volumesLocker(fVolumes);
420	volumes = new(std::nothrow) ClientVolume*[fVolumes->Size()];
421	if (!volumes) {
422		ERROR(("ClientConnection::ShareRemoved(): ERROR: Failed to "
423			"allocate memory for volume array.\n"));
424		volumesLocker.Unlock();
425		_UnmountAllVolumes();
426		return;
427	}
428	for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) {
429		if (ClientVolume* volume = _GetVolume(it.Next().value->GetID()))
430			volumes[volumeCount++] = volume;
431	}
432	volumesLocker.Unlock();
433
434	// update the concerned volumes
435	for (int32 i = 0; i < volumeCount; i++) {
436		ClientVolume* volume = volumes[i];
437
438		fSecurityContextLock.Lock();
439		bool concerned = (volume->GetShare() == share
440			&& volume->GetSecurityContext()->GetUser() == user);
441		fSecurityContextLock.Unlock();
442
443		if (concerned) {
444			// create a new user security context for the volume
445			status_t error = B_OK;
446
447			if (unmountAll) {
448				_UnmountVolume(volume);
449			} else {
450				// create a new user security context
451				AutoLocker<Locker> securityContextLocker(fSecurityContextLock);
452				UserSecurityContext* userSecurityContext
453					= new(std::nothrow) UserSecurityContext;
454
455				// init it
456				if (userSecurityContext) {
457					error = fSecurityContext->GetUserSecurityContext(user,
458						userSecurityContext);
459				} else
460					error = B_NO_MEMORY;
461				if (error != B_OK) {
462					delete userSecurityContext;
463					securityContextLocker.Unlock();
464					_UnmountVolume(volume);
465					continue;
466				}
467
468				// set the volume's new user security context
469				securityContextLocker.Unlock();
470				volume->SetSecurityContext(userSecurityContext);
471			}
472		}
473	}
474
475	// put the volumes
476	for (int32 i = 0; i < volumeCount; i++)
477		_PutVolume(volumes[i]);
478	delete[] volumes;
479}
480
481
482// #pragma mark -
483
484// VisitConnectionBrokenRequest
485status_t
486ClientConnection::VisitConnectionBrokenRequest(ConnectionBrokenRequest* request)
487{
488	ConnectionReference connectionReference(this);
489	if (!connectionReference.IsValid())
490		return B_OK;
491
492	_MarkClosed(true);
493	return B_OK;
494}
495
496// VisitInitConnectionRequest
497status_t
498ClientConnection::VisitInitConnectionRequest(InitConnectionRequest* request)
499{
500	bool alreadyInitialized = atomic_or(&fInitialized, ~0);
501
502	ConnectionReference connectionReference(this);
503	if (!connectionReference.IsValid())
504		return B_OK;
505
506	if (!alreadyInitialized)
507		fInverseClientEndianess = (request->bigEndian != B_HOST_IS_BENDIAN);
508
509	// prepare the reply
510	InitConnectionReply reply;
511
512	// send the reply
513	reply.error = (alreadyInitialized ? B_BAD_VALUE : B_OK);
514	status_t error = GetChannel()->SendRequest(&reply);
515
516	// on error just close
517	if (error != B_OK)
518		_MarkClosed(true);
519	return B_OK;
520}
521
522// VisitMountRequest
523status_t
524ClientConnection::VisitMountRequest(MountRequest* request)
525{
526	ConnectionReference connectionReference(this);
527	if (!connectionReference.IsValid())
528		return B_OK;
529
530	status_t result = B_OK;
531	const char* shareName = request->share.GetString();
532	if (!shareName)
533		SET_ERROR(result, B_BAD_DATA);
534
535	// create a volume
536	ClientVolume* volume = NULL;
537	if (result == B_OK)
538		result = _CreateVolume(&volume);
539	ClientVolumePutter volumePutter(this, volume);
540
541	// if we haven't been supplied with a user yet, use the info from the
542	// mount request for authentication
543	VolumeManagerLocker managerLocker;
544	AutoLocker<Locker> securityContextLocker(fSecurityContextLock);
545	const char* userName = request->user.GetString();
546	User* user = fUser;
547	BReference<User> userReference(user);
548	bool noPermission = false;
549	if (result == B_OK && !user) {
550		if (userName) {
551			SET_ERROR(result, fSecurityContext->AuthenticateUser(userName,
552				request->password.GetString(), &user));
553			if (result == B_OK)
554				userReference.SetTo(user, true);
555		} else
556			result = B_PERMISSION_DENIED;
557		if (result == B_PERMISSION_DENIED)
558			noPermission = true;
559	}
560
561	// create a user security context
562	UserSecurityContext* securityContext = NULL;
563	if (result == B_OK) {
564		securityContext = new(std::nothrow) UserSecurityContext;
565		if (securityContext) {
566			SET_ERROR(result, fSecurityContext->GetUserSecurityContext(user,
567				securityContext));
568		} else
569			SET_ERROR(result, B_NO_MEMORY);
570	}
571	ObjectDeleter<UserSecurityContext> securityContextDeleter(securityContext);
572
573	// get the share
574	Share* share = NULL;
575	Permissions sharePermissions;
576	node_ref mountPoint;
577	if (result == B_OK) {
578		AutoLocker<SecurityContext> _(fSecurityContext);
579		share = fSecurityContext->FindShare(shareName);
580		if (share) {
581			mountPoint = share->GetNodeRef();
582			sharePermissions = securityContext->GetNodePermissions(
583				mountPoint);
584			if (!sharePermissions.ImpliesMountSharePermission()) {
585				SET_ERROR(result, B_PERMISSION_DENIED);
586				noPermission = true;
587			}
588		} else
589			SET_ERROR(result, B_ENTRY_NOT_FOUND);
590	}
591	BReference<Share> shareReference(share, true);
592
593	// mount the volume
594	MountReply reply;
595	if (result == B_OK) {
596		SET_ERROR(result, volume->Mount(securityContext, share));
597		securityContextDeleter.Detach();
598	}
599	if (result == B_OK) {
600		_GetNodeInfo(volume->GetRootDirectory(), &reply.nodeInfo);
601		reply.sharePermissions = sharePermissions.GetPermissions();
602		reply.volumeID = volume->GetID();
603	}
604
605	// make sure, the volume is removed on error
606	if (result != B_OK && volume) {
607		AutoLocker<VolumeMap> volumeMapLocker(fVolumes);
608		volume->MarkRemoved();
609	}
610
611	securityContextLocker.Unlock();
612	managerLocker.Unlock();
613
614	// send the reply
615	reply.error = result;
616	reply.noPermission = noPermission;
617	return GetChannel()->SendRequest(&reply);
618}
619
620// VisitUnmountRequest
621status_t
622ClientConnection::VisitUnmountRequest(UnmountRequest* request)
623{
624	ConnectionReference connectionReference(this);
625	if (!connectionReference.IsValid())
626		return B_OK;
627
628	if (ClientVolume* volume = _GetVolume(request->volumeID)) {
629		_UnmountVolume(volume);
630		_PutVolume(volume);
631	}
632
633	return B_OK;
634}
635
636// VisitReadVNodeRequest
637status_t
638ClientConnection::VisitReadVNodeRequest(ReadVNodeRequest* request)
639{
640	ConnectionReference connectionReference(this);
641	if (!connectionReference.IsValid())
642		return B_OK;
643
644	// get the volume
645	status_t result = B_OK;
646	ClientVolume* volume = _GetVolume(request->volumeID);
647	if (!volume)
648		result = B_BAD_VALUE;
649	ClientVolumePutter volumePutter(this, volume);
650
651	VolumeManagerLocker managerLocker;
652
653	// get the node
654	Node* node = NULL;
655	if (result == B_OK) {
656		node = volume->GetNode(request->nodeID);
657		if (!node)
658			result = B_ENTRY_NOT_FOUND;
659	}
660
661	// prepare the reply
662	ReadVNodeReply reply;
663	if (result == B_OK)
664		_GetNodeInfo(node, &reply.nodeInfo);
665
666	managerLocker.Unlock();
667
668	// send the reply
669	reply.error = result;
670	return GetChannel()->SendRequest(&reply);
671}
672
673// VisitWriteStatRequest
674status_t
675ClientConnection::VisitWriteStatRequest(WriteStatRequest* request)
676{
677	ConnectionReference connectionReference(this);
678	if (!connectionReference.IsValid())
679		return B_OK;
680
681	// get the volume
682	status_t result = B_OK;
683	ClientVolume* volume = _GetVolume(request->volumeID);
684	if (!volume)
685		result = B_BAD_VALUE;
686	ClientVolumePutter volumePutter(this, volume);
687
688	VolumeManagerLocker managerLocker;
689
690	// get the node
691	Node* node = NULL;
692	if (result == B_OK) {
693		node = volume->GetNode(request->nodeID);
694		if (!node)
695			result = B_ENTRY_NOT_FOUND;
696	}
697
698	// check permissions
699	if (result == B_OK) {
700		if (!volume->GetNodePermissions(node).ImpliesWritePermission())
701			result = B_PERMISSION_DENIED;
702	}
703
704	// get the path
705	Path path;
706	if (result == B_OK)
707		result = node->GetPath(&path);
708
709	// write the stat
710	uint32 mask = request->mask;
711	// size
712	if (result == B_OK && (mask & WSTAT_SIZE)) {
713		if (truncate(path.GetPath(), request->nodeInfo.st.st_size) < 0)
714			result = errno;
715	}
716	// mode
717	if (result == B_OK && (mask & WSTAT_MODE)) {
718		if (chmod(path.GetPath(), request->nodeInfo.st.st_mode) < 0)
719			result = errno;
720	}
721	// mtime
722	if (result == B_OK && (mask & (WSTAT_ATIME | WSTAT_MTIME))) {
723		utimbuf buffer;
724		buffer.actime = (mask & WSTAT_ATIME)
725			? request->nodeInfo.st.st_atime
726			: node->GetStat().st_atime;
727		buffer.modtime = (mask & WSTAT_MTIME)
728			? request->nodeInfo.st.st_mtime
729			: node->GetStat().st_mtime;
730		if (utime(path.GetPath(), &buffer) < 0)
731			result = errno;
732	}
733	// ignore WSTAT_CRTIME, WSTAT_UID, WSTAT_GID for the time being
734
735	// prepare the reply
736	WriteStatReply reply;
737	// update the node stat
738	reply.nodeInfoValid = false;
739	if (node) {
740		if (node->UpdateStat() == B_OK) {
741			_GetNodeInfo(node, &reply.nodeInfo);
742			reply.nodeInfoValid = true;
743		}
744	}
745
746	managerLocker.Unlock();
747
748	// send the reply
749	reply.error = result;
750	return GetChannel()->SendRequest(&reply);
751}
752
753// VisitCreateFileRequest
754status_t
755ClientConnection::VisitCreateFileRequest(CreateFileRequest* request)
756{
757	ConnectionReference connectionReference(this);
758	if (!connectionReference.IsValid())
759		return B_OK;
760
761	// get the volume
762	status_t result = B_OK;
763	ClientVolume* volume = _GetVolume(request->volumeID);
764	if (!volume)
765		result = B_BAD_VALUE;
766	ClientVolumePutter volumePutter(this, volume);
767
768	VolumeManagerLocker managerLocker;
769
770	// get the directory
771	Directory* directory = NULL;
772	if (result == B_OK) {
773		Node* node = volume->GetNode(request->directoryID);
774		if (node) {
775			directory = dynamic_cast<Directory*>(node);
776			if (!directory)
777				result = B_NOT_A_DIRECTORY;
778		} else
779			result = B_ENTRY_NOT_FOUND;
780	}
781
782	// check permissions
783	int openMode = request->openMode;
784	if (result == B_OK) {
785		if (!volume->GetNodePermissions(directory).ImpliesWritePermission())
786			result = B_PERMISSION_DENIED;
787	}
788
789	// get the path
790	Path path;
791	if (result == B_OK) {
792		result = directory->GetPath(&path);
793		if (result == B_OK)
794			result = path.Append(request->name.GetString());
795	}
796
797	// create the file
798	if (result == B_OK) {
799		int fd = -1;
800		result = FDManager::Open(path.GetPath(),
801			openMode | O_CREAT | O_NOTRAVERSE, request->mode, fd);
802		if (result == B_OK)
803			close(fd);
804	}
805
806	// load the new entry
807	Entry* entry = NULL;
808	if (result == B_OK) {
809		VolumeManager* volumeManager = VolumeManager::GetDefault();
810
811		// if there existed an entry before, we need to delete it, to avoid that
812		// we open the wrong node
813		entry = volumeManager->GetEntry(directory->GetVolumeID(),
814			directory->GetID(), request->name.GetString());
815		if (entry)
816			volumeManager->DeleteEntry(entry, false);
817
818		// load the new entry
819		entry = NULL;
820		result = volume->LoadEntry(directory, request->name.GetString(),
821			&entry);
822	}
823
824	// open the node
825	FileHandle* handle = NULL;
826	if (result == B_OK) {
827		openMode &= ~(O_CREAT | O_EXCL | O_TRUNC);
828		result = volume->Open(entry->GetNode(), openMode, &handle);
829	}
830	NodeHandleUnlocker handleUnlocker(volume, handle);
831
832	// prepare the reply
833	CreateFileReply reply;
834	if (result == B_OK) {
835		_GetEntryInfo(entry, &reply.entryInfo);
836		reply.cookie = handle->GetCookie();
837	}
838
839	managerLocker.Unlock();
840
841	// send the reply
842	reply.error = result;
843	status_t error = GetChannel()->SendRequest(&reply);
844
845	// close the handle, if a send error occurred
846	if (error != B_OK && result == B_OK)
847		volume->Close(handle);
848
849	return error;
850}
851
852// VisitOpenRequest
853status_t
854ClientConnection::VisitOpenRequest(OpenRequest* request)
855{
856	ConnectionReference connectionReference(this);
857	if (!connectionReference.IsValid())
858		return B_OK;
859
860	// get the volume
861	status_t result = B_OK;
862	ClientVolume* volume = _GetVolume(request->volumeID);
863	if (!volume)
864		result = B_BAD_VALUE;
865	ClientVolumePutter volumePutter(this, volume);
866
867	VolumeManagerLocker managerLocker;
868
869	// get the node
870	Node* node = NULL;
871	if (result == B_OK) {
872		node = volume->GetNode(request->nodeID);
873		if (!node)
874			result = B_ENTRY_NOT_FOUND;
875	}
876
877	// check permissions
878	int openMode = request->openMode;
879	if (result == B_OK) {
880		Permissions permissions = volume->GetNodePermissions(node);
881		if ((openMode & O_RWMASK) == O_RDWR) {
882			// read+write: fall back to read/write only, if the other permission
883			// is missing
884			if (!permissions.ImpliesReadPermission())
885				openMode = (openMode & ~O_RWMASK) | O_WRONLY;
886			else if (!permissions.ImpliesWritePermission())
887				openMode = (openMode & ~O_RWMASK) | O_RDONLY;
888		}
889		if ((openMode & O_RWMASK) == O_RDONLY) {
890			if (!permissions.ImpliesReadPermission())
891				result = B_PERMISSION_DENIED;
892		} else if ((openMode & O_RWMASK) == O_WRONLY) {
893			if (!permissions.ImpliesWritePermission())
894				result = B_PERMISSION_DENIED;
895		}
896	}
897
898	// open the node
899	FileHandle* handle = NULL;
900	if (result == B_OK)
901		result = volume->Open(node, openMode, &handle);
902	NodeHandleUnlocker handleUnlocker(volume, handle);
903
904	// prepare the reply
905	OpenReply reply;
906	if (result == B_OK) {
907		_GetNodeInfo(node, &reply.nodeInfo);
908		reply.cookie = handle->GetCookie();
909	}
910
911	managerLocker.Unlock();
912
913	// send the reply
914	reply.error = result;
915	status_t error = GetChannel()->SendRequest(&reply);
916
917	// close the handle, if a send error occurred
918	if (error != B_OK && result == B_OK)
919		volume->Close(handle);
920
921	return error;
922}
923
924// VisitCloseRequest
925status_t
926ClientConnection::VisitCloseRequest(CloseRequest* request)
927{
928	ConnectionReference connectionReference(this);
929	if (!connectionReference.IsValid())
930		return B_OK;
931
932	status_t result = B_OK;
933
934	if (request->volumeID >= 0) {
935		// get the volume
936		ClientVolume* volume = _GetVolume(request->volumeID);
937		if (!volume)
938			SET_ERROR(result, B_BAD_VALUE);
939		ClientVolumePutter volumePutter(this, volume);
940
941		// get the node handle
942		NodeHandle* handle = NULL;
943		if (result == B_OK)
944			SET_ERROR(result, volume->LockNodeHandle(request->cookie, &handle));
945		NodeHandleUnlocker handleUnlocker(volume, handle);
946
947		VolumeManagerLocker managerLocker;
948
949		// close it
950		if (result == B_OK)
951			SET_ERROR(result, volume->Close(handle));
952
953		managerLocker.Unlock();
954	} else {
955		// no volume ID given, so this is a query handle
956		// lock the handle
957		QueryHandle* handle = NULL;
958		SET_ERROR(result, _LockQueryHandle(request->cookie, &handle));
959		QueryHandleUnlocker handleUnlocker(this, handle);
960
961		// close it
962		if (result == B_OK)
963			SET_ERROR(result, _CloseQuery(handle));
964	}
965
966	// send the reply
967	CloseReply reply;
968	reply.error = result;
969	return GetChannel()->SendRequest(&reply);
970}
971
972// VisitReadRequest
973status_t
974ClientConnection::VisitReadRequest(ReadRequest* request)
975{
976	ConnectionReference connectionReference(this);
977	if (!connectionReference.IsValid())
978		return B_OK;
979
980	// get the volume
981	status_t result = B_OK;
982	ClientVolume* volume = _GetVolume(request->volumeID);
983	if (!volume)
984		result = B_BAD_VALUE;
985	ClientVolumePutter volumePutter(this, volume);
986
987	// get the node handle
988	NodeHandle* handle = NULL;
989	if (result == B_OK)
990		result = volume->LockNodeHandle(request->cookie, &handle);
991	NodeHandleUnlocker handleUnlocker(volume, handle);
992
993	// check if it is a file handle
994	FileHandle* fileHandle = NULL;
995	if (result == B_OK) {
996		fileHandle = dynamic_cast<FileHandle*>(handle);
997		if (!fileHandle)
998			result = B_BAD_VALUE;
999	}
1000
1001	VolumeManagerLocker managerLocker;
1002
1003	// check read permission
1004	if (result == B_OK) {
1005		Node* node = volume->GetNode(fileHandle->GetNodeRef());
1006		if (!node || !volume->GetNodePermissions(node).ImpliesReadPermission())
1007			result = B_PERMISSION_DENIED;
1008	}
1009
1010	managerLocker.Unlock();
1011
1012	off_t pos = request->pos;
1013	int32 size = request->size;
1014	int32 bufferSize = min(size, kMaxReadBufferSize);
1015	// allocate a buffer
1016	uint8* buffer = NULL;
1017	if (result == B_OK) {
1018		buffer = (uint8*)malloc(bufferSize);
1019		if (!buffer)
1020			result = B_NO_MEMORY;
1021	}
1022	MemoryDeleter bufferDeleter(buffer);
1023
1024	// read as long as there are bytes left to read or an error occurs
1025	bool moreToRead = true;
1026	do {
1027		int32 bytesToRead = min(size, bufferSize);
1028		size_t bytesRead = 0;
1029		if (result == B_OK)
1030			result = fileHandle->Read(pos, buffer, bytesToRead, &bytesRead);
1031		moreToRead = (result == B_OK && bytesRead > 0
1032			&& (int32)bytesRead < size);
1033
1034		// prepare the reply
1035		ReadReply reply;
1036		if (result == B_OK) {
1037			reply.pos = pos;
1038			reply.data.SetTo(buffer, bytesRead);
1039			reply.moreToCome = moreToRead;
1040			pos += bytesRead;
1041			size -= bytesRead;
1042		}
1043
1044		// send the reply
1045		reply.error = result;
1046		status_t error = GetChannel()->SendRequest(&reply);
1047		if (error != B_OK)
1048			return error;
1049	} while (moreToRead);
1050
1051	return B_OK;
1052}
1053
1054// VisitWriteRequest
1055status_t
1056ClientConnection::VisitWriteRequest(WriteRequest* request)
1057{
1058	ConnectionReference connectionReference(this);
1059	if (!connectionReference.IsValid())
1060		return B_OK;
1061
1062	// get the volume
1063	status_t result = B_OK;
1064	ClientVolume* volume = _GetVolume(request->volumeID);
1065	if (!volume)
1066		result = B_BAD_VALUE;
1067	ClientVolumePutter volumePutter(this, volume);
1068
1069	// get the node handle
1070	NodeHandle* handle = NULL;
1071	if (result == B_OK)
1072		result = volume->LockNodeHandle(request->cookie, &handle);
1073	NodeHandleUnlocker handleUnlocker(volume, handle);
1074
1075	// check if it is a file handle
1076	FileHandle* fileHandle = NULL;
1077	if (result == B_OK) {
1078		fileHandle = dynamic_cast<FileHandle*>(handle);
1079		if (!fileHandle)
1080			result = B_BAD_VALUE;
1081	}
1082
1083	VolumeManagerLocker managerLocker;
1084
1085	// check read permission
1086	if (result == B_OK) {
1087		Node* node = volume->GetNode(fileHandle->GetNodeRef());
1088		if (!node || !volume->GetNodePermissions(node).ImpliesWritePermission())
1089			result = B_PERMISSION_DENIED;
1090	}
1091
1092	managerLocker.Unlock();
1093
1094	// write until all has been written or an error occurs
1095	off_t pos = request->pos;
1096	int32 size = request->data.GetSize();
1097	const char* buffer = (const char*)request->data.GetData();
1098	while (result == B_OK && size > 0) {
1099		size_t bytesWritten;
1100		result = fileHandle->Write(pos, buffer, size, &bytesWritten);
1101		if (result == B_OK) {
1102			pos += bytesWritten;
1103			buffer += bytesWritten;
1104			size -= bytesWritten;
1105		}
1106	}
1107
1108	// prepare the reply
1109	WriteReply reply;
1110	// send the reply
1111	reply.error = result;
1112	return GetChannel()->SendRequest(&reply);
1113}
1114
1115// VisitCreateLinkRequest
1116status_t
1117ClientConnection::VisitCreateLinkRequest(CreateLinkRequest* request)
1118{
1119	ConnectionReference connectionReference(this);
1120	if (!connectionReference.IsValid())
1121		return B_OK;
1122
1123	// get the volume
1124	status_t result = B_OK;
1125	ClientVolume* volume = _GetVolume(request->volumeID);
1126	if (!volume)
1127		result = B_BAD_VALUE;
1128	ClientVolumePutter volumePutter(this, volume);
1129
1130	VolumeManagerLocker managerLocker;
1131
1132	// get the target node
1133	Node* node = NULL;
1134	if (result == B_OK) {
1135		node = volume->GetNode(request->nodeID);
1136		if (!node)
1137			result = B_ENTRY_NOT_FOUND;
1138	}
1139
1140	// get the target node path
1141	Path targetPath;
1142	if (result == B_OK)
1143		result = node->GetPath(&targetPath);
1144
1145	// get the directory
1146	Directory* directory = NULL;
1147	if (result == B_OK) {
1148		Node* node = volume->GetNode(request->directoryID);
1149		if (node) {
1150			directory = dynamic_cast<Directory*>(node);
1151			if (!directory)
1152				result = B_NOT_A_DIRECTORY;
1153		} else
1154			result = B_ENTRY_NOT_FOUND;
1155	}
1156
1157	// check permissions
1158	if (result == B_OK) {
1159		if (!volume->GetNodePermissions(directory).ImpliesWritePermission())
1160			result = B_PERMISSION_DENIED;
1161	}
1162
1163	// get the new entry's path
1164	Path path;
1165	if (result == B_OK) {
1166		result = directory->GetPath(&path);
1167		if (result == B_OK)
1168			result = path.Append(request->name.GetString());
1169	}
1170
1171	managerLocker.Unlock();
1172
1173	// create the link
1174	if (result == B_OK) {
1175		if (link(targetPath.GetPath(), path.GetPath()) < 0)
1176			result = errno;
1177	}
1178
1179	// prepare the reply
1180	CreateSymlinkReply reply;
1181	// send the reply
1182	reply.error = result;
1183	return GetChannel()->SendRequest(&reply);
1184}
1185
1186// VisitUnlinkRequest
1187status_t
1188ClientConnection::VisitUnlinkRequest(UnlinkRequest* request)
1189{
1190	ConnectionReference connectionReference(this);
1191	if (!connectionReference.IsValid())
1192		return B_OK;
1193
1194	// get the volume
1195	status_t result = B_OK;
1196	ClientVolume* volume = _GetVolume(request->volumeID);
1197	if (!volume)
1198		result = B_BAD_VALUE;
1199	ClientVolumePutter volumePutter(this, volume);
1200
1201	VolumeManagerLocker managerLocker;
1202
1203	// get the directory
1204	Directory* directory = NULL;
1205	if (result == B_OK) {
1206		Node* node = volume->GetNode(request->directoryID);
1207		if (node) {
1208			directory = dynamic_cast<Directory*>(node);
1209			if (!directory)
1210				result = B_NOT_A_DIRECTORY;
1211		} else
1212			result = B_ENTRY_NOT_FOUND;
1213	}
1214
1215	// check permissions
1216	if (result == B_OK) {
1217		if (!volume->GetNodePermissions(directory).ImpliesWritePermission())
1218			result = B_PERMISSION_DENIED;
1219	}
1220
1221	// get the entry's path
1222	Path path;
1223	if (result == B_OK) {
1224		result = directory->GetPath(&path);
1225		if (result == B_OK)
1226			result = path.Append(request->name.GetString());
1227	}
1228
1229	managerLocker.Unlock();
1230
1231	// remove the entry
1232	if (result == B_OK) {
1233		if (unlink(path.GetPath()) < 0)
1234			result = errno;
1235	}
1236
1237	// prepare the reply
1238	UnlinkReply reply;
1239	// send the reply
1240	reply.error = result;
1241	return GetChannel()->SendRequest(&reply);
1242}
1243
1244// VisitCreateSymlinkRequest
1245status_t
1246ClientConnection::VisitCreateSymlinkRequest(CreateSymlinkRequest* request)
1247{
1248	ConnectionReference connectionReference(this);
1249	if (!connectionReference.IsValid())
1250		return B_OK;
1251
1252	// get the volume
1253	status_t result = B_OK;
1254	ClientVolume* volume = _GetVolume(request->volumeID);
1255	if (!volume)
1256		result = B_BAD_VALUE;
1257	ClientVolumePutter volumePutter(this, volume);
1258
1259	VolumeManagerLocker managerLocker;
1260
1261	// get the directory
1262	Directory* directory = NULL;
1263	if (result == B_OK) {
1264		Node* node = volume->GetNode(request->directoryID);
1265		if (node) {
1266			directory = dynamic_cast<Directory*>(node);
1267			if (!directory)
1268				result = B_NOT_A_DIRECTORY;
1269		} else
1270			result = B_ENTRY_NOT_FOUND;
1271	}
1272
1273	// check permissions
1274	if (result == B_OK) {
1275		if (!volume->GetNodePermissions(directory).ImpliesWritePermission())
1276			result = B_PERMISSION_DENIED;
1277	}
1278
1279	// get the new entry's path
1280	Path path;
1281	if (result == B_OK) {
1282		result = directory->GetPath(&path);
1283		if (result == B_OK)
1284			result = path.Append(request->name.GetString());
1285	}
1286
1287	managerLocker.Unlock();
1288
1289	// create the symlink
1290	if (result == B_OK) {
1291		if (symlink(request->target.GetString(), path.GetPath()) < 0)
1292			result = errno;
1293	}
1294
1295	// prepare the reply
1296	CreateSymlinkReply reply;
1297	// send the reply
1298	reply.error = result;
1299	return GetChannel()->SendRequest(&reply);
1300}
1301
1302// VisitReadLinkRequest
1303status_t
1304ClientConnection::VisitReadLinkRequest(ReadLinkRequest* request)
1305{
1306	ConnectionReference connectionReference(this);
1307	if (!connectionReference.IsValid())
1308		return B_OK;
1309
1310	// get the volume
1311	status_t result = B_OK;
1312	ClientVolume* volume = _GetVolume(request->volumeID);
1313	if (!volume)
1314		result = B_BAD_VALUE;
1315	ClientVolumePutter volumePutter(this, volume);
1316
1317	VolumeManagerLocker managerLocker;
1318
1319	// get the node
1320	Node* node = NULL;
1321	if (result == B_OK) {
1322		node = volume->GetNode(request->nodeID);
1323		if (!node)
1324			result = B_ENTRY_NOT_FOUND;
1325	}
1326
1327	int32 bufferSize = request->maxSize;
1328
1329	// check read permission
1330	if (result == B_OK) {
1331		if (!volume->GetNodePermissions(node).ImpliesReadPermission())
1332			result = B_PERMISSION_DENIED;
1333	}
1334
1335	// allocate a buffer
1336	void* buffer = NULL;
1337	if (result == B_OK) {
1338		if (bufferSize < 0 || bufferSize > kMaxSaneReadLinkSize)
1339			result = B_BAD_DATA;
1340	}
1341	if (result == B_OK) {
1342		buffer = malloc(bufferSize);
1343		if (!buffer)
1344			result = B_NO_MEMORY;
1345	}
1346	MemoryDeleter bufferDeleter(buffer);
1347
1348	// read the link and prepare the reply
1349	ReadLinkReply reply;
1350	int32 bytesRead = 0;
1351	if (result == B_OK)
1352		result = node->ReadSymlink((char*)buffer, bufferSize, &bytesRead);
1353	if (result == B_OK) {
1354		reply.data.SetTo(buffer, bytesRead);
1355		_GetNodeInfo(node, &reply.nodeInfo);
1356	}
1357
1358	managerLocker.Unlock();
1359
1360	// send the reply
1361	reply.error = result;
1362	return GetChannel()->SendRequest(&reply);
1363}
1364
1365// VisitRenameRequest
1366status_t
1367ClientConnection::VisitRenameRequest(RenameRequest* request)
1368{
1369	ConnectionReference connectionReference(this);
1370	if (!connectionReference.IsValid())
1371		return B_OK;
1372
1373	// get the volume
1374	status_t result = B_OK;
1375	ClientVolume* volume = _GetVolume(request->volumeID);
1376	if (!volume)
1377		result = B_BAD_VALUE;
1378	ClientVolumePutter volumePutter(this, volume);
1379
1380	VolumeManagerLocker managerLocker;
1381
1382	// get the new directory
1383	Directory* newDirectory = NULL;
1384	if (result == B_OK) {
1385		Node* node = volume->GetNode(request->newDirectoryID);
1386		if (node) {
1387			newDirectory = dynamic_cast<Directory*>(node);
1388			if (!newDirectory)
1389				result = B_NOT_A_DIRECTORY;
1390		} else
1391			result = B_ENTRY_NOT_FOUND;
1392	}
1393
1394	// check permissions
1395	if (result == B_OK) {
1396		if (!volume->GetNodePermissions(newDirectory).ImpliesWritePermission())
1397			result = B_PERMISSION_DENIED;
1398	}
1399
1400	// get the new path
1401	Path newPath;
1402	if (result == B_OK) {
1403		result = newDirectory->GetPath(&newPath);
1404		if (result == B_OK)
1405			result = newPath.Append(request->newName.GetString());
1406	}
1407
1408	// get the old directory
1409	Directory* oldDirectory = NULL;
1410	if (result == B_OK) {
1411		Node* node = volume->GetNode(request->oldDirectoryID);
1412		if (node) {
1413			oldDirectory = dynamic_cast<Directory*>(node);
1414			if (!oldDirectory)
1415				result = B_NOT_A_DIRECTORY;
1416		} else
1417			result = B_ENTRY_NOT_FOUND;
1418	}
1419
1420	// check permissions
1421	if (result == B_OK) {
1422		if (!volume->GetNodePermissions(oldDirectory).ImpliesWritePermission())
1423			result = B_PERMISSION_DENIED;
1424	}
1425
1426	// get the new path
1427	Path oldPath;
1428	if (result == B_OK) {
1429		result = oldDirectory->GetPath(&oldPath);
1430		if (result == B_OK)
1431			result = oldPath.Append(request->oldName.GetString());
1432	}
1433
1434	managerLocker.Unlock();
1435
1436	// rename the entry
1437	if (result == B_OK) {
1438		if (rename(oldPath.GetPath(), newPath.GetPath()) < 0)
1439			result = errno;
1440	}
1441
1442	// prepare the reply
1443	RenameReply reply;
1444	// send the reply
1445	reply.error = result;
1446	return GetChannel()->SendRequest(&reply);
1447}
1448
1449// VisitMakeDirRequest
1450status_t
1451ClientConnection::VisitMakeDirRequest(MakeDirRequest* request)
1452{
1453	ConnectionReference connectionReference(this);
1454	if (!connectionReference.IsValid())
1455		return B_OK;
1456
1457	// get the volume
1458	status_t result = B_OK;
1459	ClientVolume* volume = _GetVolume(request->volumeID);
1460	if (!volume)
1461		result = B_BAD_VALUE;
1462	ClientVolumePutter volumePutter(this, volume);
1463
1464	VolumeManagerLocker managerLocker;
1465
1466	// get the directory
1467	Directory* directory = NULL;
1468	if (result == B_OK) {
1469		Node* node = volume->GetNode(request->directoryID);
1470		if (node) {
1471			directory = dynamic_cast<Directory*>(node);
1472			if (!directory)
1473				result = B_NOT_A_DIRECTORY;
1474		} else
1475			result = B_ENTRY_NOT_FOUND;
1476	}
1477
1478	// check permissions
1479	if (result == B_OK) {
1480		if (!volume->GetNodePermissions(directory).ImpliesWritePermission())
1481			result = B_PERMISSION_DENIED;
1482	}
1483
1484	// get the path
1485	Path path;
1486	if (result == B_OK) {
1487		result = directory->GetPath(&path);
1488		if (result == B_OK)
1489			result = path.Append(request->name.GetString());
1490	}
1491
1492	managerLocker.Unlock();
1493
1494	// create the directory
1495	if (result == B_OK) {
1496		if (mkdir(path.GetPath(), request->mode) < 0)
1497			result = errno;
1498	}
1499
1500	// prepare the reply
1501	MakeDirReply reply;
1502	// send the reply
1503	reply.error = result;
1504	return GetChannel()->SendRequest(&reply);
1505}
1506
1507// VisitRemoveDirRequest
1508status_t
1509ClientConnection::VisitRemoveDirRequest(RemoveDirRequest* request)
1510{
1511	ConnectionReference connectionReference(this);
1512	if (!connectionReference.IsValid())
1513		return B_OK;
1514
1515	// get the volume
1516	status_t result = B_OK;
1517	ClientVolume* volume = _GetVolume(request->volumeID);
1518	if (!volume)
1519		result = B_BAD_VALUE;
1520	ClientVolumePutter volumePutter(this, volume);
1521
1522	VolumeManagerLocker managerLocker;
1523
1524	// get the directory
1525	Directory* directory = NULL;
1526	if (result == B_OK) {
1527		Node* node = volume->GetNode(request->directoryID);
1528		if (node) {
1529			directory = dynamic_cast<Directory*>(node);
1530			if (!directory)
1531				result = B_NOT_A_DIRECTORY;
1532		} else
1533			result = B_ENTRY_NOT_FOUND;
1534	}
1535
1536	// check permissions
1537	if (result == B_OK) {
1538		if (!volume->GetNodePermissions(directory).ImpliesWritePermission())
1539			result = B_PERMISSION_DENIED;
1540	}
1541
1542	// get the path
1543	Path path;
1544	if (result == B_OK) {
1545		result = directory->GetPath(&path);
1546		if (result == B_OK)
1547			result = path.Append(request->name.GetString());
1548	}
1549
1550	managerLocker.Unlock();
1551
1552	// remove the directory
1553	if (result == B_OK) {
1554		if (rmdir(path.GetPath()) < 0)
1555			result = errno;
1556	}
1557
1558	// prepare the reply
1559	RemoveDirReply reply;
1560	// send the reply
1561	reply.error = result;
1562	return GetChannel()->SendRequest(&reply);
1563}
1564
1565// VisitOpenDirRequest
1566status_t
1567ClientConnection::VisitOpenDirRequest(OpenDirRequest* request)
1568{
1569	ConnectionReference connectionReference(this);
1570	if (!connectionReference.IsValid())
1571		return B_OK;
1572
1573	// get the volume
1574	status_t result = B_OK;
1575	ClientVolume* volume = _GetVolume(request->volumeID);
1576	if (!volume)
1577		result = B_BAD_VALUE;
1578	ClientVolumePutter volumePutter(this, volume);
1579
1580	VolumeManagerLocker managerLocker;
1581
1582	// get the directory
1583	Directory* directory = NULL;
1584	if (result == B_OK) {
1585		Node* node = volume->GetNode(request->nodeID);
1586		if (node) {
1587			directory = dynamic_cast<Directory*>(node);
1588			if (!directory)
1589				result = B_NOT_A_DIRECTORY;
1590		} else
1591			result = B_ENTRY_NOT_FOUND;
1592	}
1593
1594	// check permission
1595	if (result == B_OK) {
1596		if (!volume->GetNodePermissions(directory).ImpliesReadDirPermission())
1597			result = B_PERMISSION_DENIED;
1598	}
1599
1600	// open the directory
1601	DirIterator* handle = NULL;
1602	if (result == B_OK)
1603		result = volume->OpenDir(directory, &handle);
1604	NodeHandleUnlocker handleUnlocker(volume, handle);
1605
1606	// prepare the reply
1607	OpenDirReply reply;
1608	if (result == B_OK) {
1609		_GetNodeInfo(directory, &reply.nodeInfo);
1610		reply.cookie = handle->GetCookie();
1611	} else {
1612		if (directory != NULL) {
1613			PRINT("OpenDir() failed: client volume: %" B_PRId32 ", "
1614				"node: (%" B_PRIdDEV ", %" B_PRIdINO ")\n",
1615				volume->GetID(), directory->GetVolumeID(), directory->GetID());
1616		}
1617	}
1618
1619	managerLocker.Unlock();
1620
1621	// send the reply
1622	reply.error = result;
1623	status_t error = GetChannel()->SendRequest(&reply);
1624
1625	// close the handle, if a send error occurred
1626	if (error != B_OK && result == B_OK)
1627		volume->Close(handle);
1628
1629	return error;
1630}
1631
1632// VisitReadDirRequest
1633status_t
1634ClientConnection::VisitReadDirRequest(ReadDirRequest* request)
1635{
1636	ConnectionReference connectionReference(this);
1637	if (!connectionReference.IsValid())
1638		return B_OK;
1639
1640	// get the volume
1641	status_t result = B_OK;
1642	ClientVolume* volume = _GetVolume(request->volumeID);
1643	if (!volume)
1644		result = B_BAD_VALUE;
1645	ClientVolumePutter volumePutter(this, volume);
1646
1647	// get the node handle
1648	NodeHandle* handle = NULL;
1649	if (result == B_OK)
1650		result = volume->LockNodeHandle(request->cookie, &handle);
1651	NodeHandleUnlocker handleUnlocker(volume, handle);
1652
1653	// check if it is a directory iterator
1654	DirIterator* iterator = NULL;
1655	if (result == B_OK) {
1656		iterator = dynamic_cast<DirIterator*>(handle);
1657		if (!iterator)
1658			result = B_BAD_VALUE;
1659	}
1660
1661	VolumeManagerLocker managerLocker;
1662
1663	// get the directory
1664	Directory* directory = NULL;
1665	if (result == B_OK) {
1666		Node* node = volume->GetNode(iterator->GetNodeRef());
1667		if (node) {
1668			directory = dynamic_cast<Directory*>(node);
1669			if (!directory)
1670				result = B_NOT_A_DIRECTORY;
1671		} else
1672			result = B_ENTRY_NOT_FOUND;
1673	}
1674
1675	// check read permission
1676	if (result == B_OK) {
1677		if (!volume->GetNodePermissions(directory).ImpliesReadDirPermission())
1678			result = B_PERMISSION_DENIED;
1679	}
1680
1681	if (result == B_OK) {
1682		PRINT("ReadDir: (%" B_PRId32 ", %" B_PRIdINO ")\n", request->volumeID,
1683			directory->GetID());
1684	}
1685
1686	// rewind, if requested
1687	if (result == B_OK && request->rewind)
1688		iterator->Rewind();
1689
1690	// read the directory
1691	bool done = false;
1692	ReadDirReply reply;
1693	int32 toRead = request->count;
1694	while (result == B_OK && toRead > 0) {
1695		// get the next entry
1696		Entry* entry = iterator->NextEntry();
1697		if (!entry) {
1698			done = true;
1699			break;
1700		}
1701
1702		// get and add an entry info
1703		EntryInfo entryInfo;
1704		_GetEntryInfo(entry, &entryInfo);
1705		result = reply.entryInfos.Append(entryInfo);
1706
1707		toRead--;
1708	}
1709
1710	reply.revision = VolumeManager::GetDefault()->GetRevision();
1711
1712//PRINT(("ReadDir: (%lld) -> (%lx, %ld, dir: %lld, node: %lld, `%s')\n",
1713//directoryID, reply.error, reply.entryInfos.CountElements(),
1714//reply.entryInfo.directoryID,
1715//reply.entryInfo.nodeID, reply.entryInfo.name.GetString()));
1716	if (directory != NULL) {
1717		PRINT("ReadDir done: volume: %" B_PRId32 ", "
1718			"(%" B_PRIdDEV ", %" B_PRIdINO ") -> "
1719			"(%" B_PRIx32 ", %" B_PRId32 ")\n",
1720			volume->GetID(), directory->GetVolumeID(), directory->GetID(),
1721			result, reply.entryInfos.CountElements());
1722	}
1723
1724	managerLocker.Unlock();
1725
1726	// send the reply
1727	reply.error = result;
1728	reply.done = (result != B_OK || done);
1729	return GetChannel()->SendRequest(&reply);
1730}
1731
1732// VisitWalkRequest
1733status_t
1734ClientConnection::VisitWalkRequest(WalkRequest* request)
1735{
1736	ConnectionReference connectionReference(this);
1737	if (!connectionReference.IsValid())
1738		return B_OK;
1739
1740	// get the volume
1741	status_t result = B_OK;
1742	ClientVolume* volume = _GetVolume(request->volumeID);
1743	if (!volume)
1744		result = B_BAD_VALUE;
1745	ClientVolumePutter volumePutter(this, volume);
1746
1747	VolumeManagerLocker managerLocker;
1748
1749	// get the directory
1750	Directory* directory = NULL;
1751	if (result == B_OK) {
1752		Node* node = volume->GetNode(request->nodeID);
1753		if (node) {
1754			directory = dynamic_cast<Directory*>(node);
1755			if (!directory)
1756				result = B_NOT_A_DIRECTORY;
1757		} else
1758			result = B_ENTRY_NOT_FOUND;
1759	}
1760
1761	// check permission
1762	if (result == B_OK) {
1763		if (!volume->GetNodePermissions(directory)
1764				.ImpliesResolveDirEntryPermission()) {
1765			result = B_PERMISSION_DENIED;
1766		}
1767	}
1768
1769	WalkReply reply;
1770	char linkPath[B_PATH_NAME_LENGTH];
1771	if (result == B_OK) {
1772		// load the entry
1773		Entry* entry;
1774		result = volume->LoadEntry(directory, request->name.GetString(),
1775			&entry);
1776
1777		// fill in the reply
1778		if (result == B_OK) {
1779			_GetEntryInfo(entry, &reply.entryInfo);
1780
1781			// resolve a symlink, if desired
1782			Node* node = entry->GetNode();
1783			if (request->resolveLink && node->IsSymlink()) {
1784				result = node->ReadSymlink(linkPath, B_PATH_NAME_LENGTH);
1785				if (result == B_OK)
1786					reply.linkPath.SetTo(linkPath);
1787			}
1788		}
1789	}
1790
1791	managerLocker.Unlock();
1792
1793	// send the reply
1794	reply.error = result;
1795	PRINT("Walk: (%" B_PRIdDEV ", %" B_PRIdINO ", `%s') -> "
1796		"(%" B_PRIx32 ", (%" B_PRIdDEV ", %" B_PRIdINO "), `%s')\n",
1797		request->nodeID.volumeID, request->nodeID.nodeID,
1798		request->name.GetString(), result,
1799		reply.entryInfo.nodeInfo.st.st_dev,
1800		reply.entryInfo.nodeInfo.st.st_ino, reply.linkPath.GetString());
1801	return GetChannel()->SendRequest(&reply);
1802}
1803
1804// VisitMultiWalkRequest
1805status_t
1806ClientConnection::VisitMultiWalkRequest(MultiWalkRequest* request)
1807{
1808	ConnectionReference connectionReference(this);
1809	if (!connectionReference.IsValid())
1810		return B_OK;
1811
1812	// get the volume
1813	status_t result = B_OK;
1814	ClientVolume* volume = _GetVolume(request->volumeID);
1815	if (!volume)
1816		result = B_BAD_VALUE;
1817	ClientVolumePutter volumePutter(this, volume);
1818
1819	VolumeManagerLocker managerLocker;
1820
1821	// get the directory
1822	Directory* directory = NULL;
1823	if (result == B_OK) {
1824		Node* node = volume->GetNode(request->nodeID);
1825		if (node) {
1826			directory = dynamic_cast<Directory*>(node);
1827			if (!directory)
1828				result = B_NOT_A_DIRECTORY;
1829		} else
1830			result = B_ENTRY_NOT_FOUND;
1831	}
1832
1833	// check permission
1834	if (result == B_OK) {
1835		if (!volume->GetNodePermissions(directory)
1836				.ImpliesResolveDirEntryPermission()) {
1837			result = B_PERMISSION_DENIED;
1838		}
1839	}
1840
1841	MultiWalkReply reply;
1842	StringData* names = request->names.GetElements();
1843	int32 count = request->names.CountElements();
1844	for (int32 i = 0; result == B_OK && i < count; i++) {
1845		// load the entry
1846		Entry* entry;
1847		if (volume->LoadEntry(directory, names[i].GetString(), &entry)
1848				== B_OK) {
1849			// add an entry info
1850			EntryInfo entryInfo;
1851			_GetEntryInfo(entry, &entryInfo);
1852
1853			// append the info
1854			result = reply.entryInfos.Append(entryInfo);
1855		}
1856	}
1857
1858	managerLocker.Unlock();
1859
1860	// send the reply
1861	reply.error = result;
1862	PRINT("MultiWalk: (%" B_PRIdDEV ", %" B_PRIdINO ", %" B_PRId32 ") -> "
1863		"(%" B_PRIx32 ", %" B_PRId32 ")\n",
1864		request->nodeID.volumeID, request->nodeID.nodeID, count,
1865		result, reply.entryInfos.CountElements());
1866	return GetChannel()->SendRequest(&reply);
1867}
1868
1869// VisitOpenAttrDirRequest
1870status_t
1871ClientConnection::VisitOpenAttrDirRequest(OpenAttrDirRequest* request)
1872{
1873	ConnectionReference connectionReference(this);
1874	if (!connectionReference.IsValid())
1875		return B_OK;
1876
1877	// get the volume
1878	status_t result = B_OK;
1879	ClientVolume* volume = _GetVolume(request->volumeID);
1880	if (!volume)
1881		result = B_BAD_VALUE;
1882	ClientVolumePutter volumePutter(this, volume);
1883
1884	VolumeManagerLocker managerLocker;
1885
1886	// get the node
1887	Node* node = NULL;
1888	if (result == B_OK) {
1889		node = volume->GetNode(request->nodeID);
1890		if (!node)
1891			result = B_ENTRY_NOT_FOUND;
1892	}
1893
1894	// check permission
1895	if (result == B_OK) {
1896		if (!volume->GetNodePermissions(node).ImpliesReadPermission())
1897			result = B_PERMISSION_DENIED;
1898	}
1899
1900	// load/cache the attribute directory
1901	bool attrDirCached = (node->LoadAttrDir() == B_OK);
1902
1903	// open the attribute directory, if caching it failed
1904	AttrDirIterator* handle = NULL;
1905	if (result == B_OK && !attrDirCached)
1906		result = volume->OpenAttrDir(node, &handle);
1907	NodeHandleUnlocker handleUnlocker(volume, handle);
1908
1909	// prepare the reply
1910	OpenAttrDirReply reply;
1911	if (result == B_OK) {
1912		if (handle) {
1913			reply.cookie = handle->GetCookie();
1914		} else {
1915			// the attribute directory is cached
1916			reply.cookie = -1;
1917			result = _GetAttrDirInfo(request, node, &reply.attrDirInfo);
1918		}
1919	}
1920
1921	managerLocker.Unlock();
1922
1923	// send the reply
1924	reply.error = result;
1925	status_t error = GetChannel()->SendRequest(&reply);
1926
1927	// close the handle, if a send error occurred
1928	if (error != B_OK && result == B_OK && handle)
1929		volume->Close(handle);
1930
1931	return error;
1932}
1933
1934// VisitReadAttrDirRequest
1935status_t
1936ClientConnection::VisitReadAttrDirRequest(ReadAttrDirRequest* request)
1937{
1938	ConnectionReference connectionReference(this);
1939	if (!connectionReference.IsValid())
1940		return B_OK;
1941
1942	// get the volume
1943	status_t result = B_OK;
1944	ClientVolume* volume = _GetVolume(request->volumeID);
1945	if (!volume)
1946		result = B_BAD_VALUE;
1947	ClientVolumePutter volumePutter(this, volume);
1948
1949	// get the node handle
1950	NodeHandle* handle = NULL;
1951	if (result == B_OK)
1952		result = volume->LockNodeHandle(request->cookie, &handle);
1953	NodeHandleUnlocker handleUnlocker(volume, handle);
1954
1955	// check if it is a attribute directory iterator
1956	AttrDirIterator* iterator = NULL;
1957	if (result == B_OK) {
1958		iterator = dynamic_cast<AttrDirIterator*>(handle);
1959		if (!iterator)
1960			result = B_BAD_VALUE;
1961	}
1962
1963	VolumeManagerLocker managerLocker;
1964
1965	// get the node
1966	Node* node = NULL;
1967	if (result == B_OK) {
1968		node = volume->GetNode(iterator->GetNodeRef());
1969		if (!node)
1970			result = B_ENTRY_NOT_FOUND;
1971	}
1972
1973	// check read permission (we already checked when opening, but anyway...)
1974	if (result == B_OK) {
1975		if (!volume->GetNodePermissions(node).ImpliesReadPermission())
1976			result = B_PERMISSION_DENIED;
1977	}
1978
1979	managerLocker.Unlock();
1980
1981	// read the attribute directory
1982	uint8 buffer[offsetof(struct dirent, d_name) + B_FILE_NAME_LENGTH];
1983	struct dirent* dirEntry = (struct dirent*)buffer;
1984	int32 countRead = 0;
1985	bool done = true;
1986	if (result == B_OK) {
1987		if (request->rewind)
1988			result = iterator->RewindDir();
1989		if (result == B_OK) {
1990			result = iterator->ReadDir(dirEntry, sizeof(buffer), 1,
1991				&countRead, &done);
1992		}
1993	}
1994
1995	// prepare the reply
1996	ReadAttrDirReply reply;
1997	reply.name.SetTo(dirEntry->d_name);
1998
1999	// send the reply
2000	reply.error = result;
2001	reply.count = countRead;
2002	return GetChannel()->SendRequest(&reply);
2003}
2004
2005// VisitReadAttrRequest
2006status_t
2007ClientConnection::VisitReadAttrRequest(ReadAttrRequest* request)
2008{
2009	ConnectionReference connectionReference(this);
2010	if (!connectionReference.IsValid())
2011		return B_OK;
2012
2013	// get the volume
2014	status_t result = B_OK;
2015	ClientVolume* volume = _GetVolume(request->volumeID);
2016	if (!volume)
2017		result = B_BAD_VALUE;
2018	ClientVolumePutter volumePutter(this, volume);
2019
2020	VolumeManagerLocker managerLocker;
2021
2022	// get the node
2023	Node* node = NULL;
2024	if (result == B_OK) {
2025		node = volume->GetNode(request->nodeID);
2026		if (!node)
2027			result = B_ENTRY_NOT_FOUND;
2028	}
2029
2030	// check read permission
2031	if (result == B_OK) {
2032		if (!volume->GetNodePermissions(node).ImpliesReadPermission())
2033			result = B_PERMISSION_DENIED;
2034	}
2035
2036	// open the node
2037	FileHandle* handle = NULL;
2038	if (result == B_OK)
2039		result = volume->Open(node, O_RDONLY, &handle);
2040	NodeHandleUnlocker handleUnlocker(volume, handle);
2041
2042	managerLocker.Unlock();
2043
2044	// read the attribute
2045	if (result == B_OK) {
2046		// Due to a bug in BFS the `pos' is ignored. This means that the loop
2047		// below won't work if the attribute is non-empty and the buffer is
2048		// larger than the attribute, because the we would again and again
2049		// read the beginning of the attribute until the buffer is full.
2050		// Hence we first get an attr_info and don't try to read more than
2051		// the size of the attribute.
2052		attr_info info;
2053		result = handle->StatAttr(request->name.GetString(), &info);
2054		off_t originalPos = max(request->pos, (off_t)0);
2055		int32 originalSize = max(request->size, (int32)0);
2056		off_t pos = originalPos;
2057		int32 size = originalSize;
2058		type_code type = B_SWAP_INT32(request->type);
2059		bool convert = false;
2060
2061		if (result == B_OK) {
2062			originalSize = min((off_t)originalSize, max((off_t)0, info.size - pos));
2063			size = originalSize;
2064
2065			// deal with inverse endianess clients
2066			if (fInverseClientEndianess) {
2067				convert = _KnownAttributeType(info.type);
2068				if (convert) {
2069					// read the whole attribute
2070					pos = 0;
2071					size = info.size;
2072				} else
2073					type = B_SWAP_INT32(request->type);
2074			}
2075		}
2076		int32 bufferSize = min(size, kMaxReadBufferSize);
2077
2078		// allocate a buffer
2079		uint8* buffer = NULL;
2080		if (result == B_OK) {
2081			buffer = (uint8*)malloc(bufferSize);
2082			if (!buffer)
2083				result = B_NO_MEMORY;
2084		}
2085		MemoryDeleter bufferDeleter(buffer);
2086
2087		if (convert) {
2088			// read the whole attribute and convert it
2089			if (result == B_OK) {
2090				// read
2091				size_t bytesRead = 0;
2092				result = handle->ReadAttr(request->name.GetString(),
2093					type, 0, buffer, size, &bytesRead);
2094				if (result == B_OK && (int32)bytesRead != size)
2095					result = B_ERROR;
2096
2097				// convert
2098				if (result == B_OK)
2099					_ConvertAttribute(info, buffer);
2100			}
2101
2102			// prepare the reply
2103			ReadAttrReply reply;
2104			if (result == B_OK) {
2105				reply.pos = originalPos;
2106				reply.data.SetTo(buffer + originalPos, originalSize);
2107				reply.moreToCome = false;
2108			}
2109
2110			// send the reply
2111			reply.error = result;
2112			status_t error = GetChannel()->SendRequest(&reply);
2113			if (error != B_OK)
2114				return error;
2115		} else {
2116			// read as long as there are bytes left to read or an error occurs
2117			bool moreToRead = true;
2118			do {
2119				int32 bytesToRead = min(size, bufferSize);
2120				size_t bytesRead = 0;
2121				if (result == B_OK) {
2122					result = handle->ReadAttr(request->name.GetString(),
2123						request->type, pos, buffer, bytesToRead, &bytesRead);
2124				}
2125				moreToRead = (result == B_OK && bytesRead > 0
2126					&& (int32)bytesRead < size);
2127
2128				// prepare the reply
2129				ReadAttrReply reply;
2130				if (result == B_OK) {
2131					reply.pos = pos;
2132					reply.data.SetTo(buffer, bytesRead);
2133					reply.moreToCome = moreToRead;
2134					pos += bytesRead;
2135					size -= bytesRead;
2136				}
2137
2138				// send the reply
2139				reply.error = result;
2140				status_t error = GetChannel()->SendRequest(&reply);
2141				if (error != B_OK)
2142					return error;
2143			} while (moreToRead);
2144		}
2145
2146		// close the handle
2147		volume->Close(handle);
2148	} else {
2149		// opening the node failed (or something even earlier): send an error
2150		// reply
2151		ReadAttrReply reply;
2152		reply.error = result;
2153		status_t error = GetChannel()->SendRequest(&reply);
2154		if (error != B_OK)
2155			return error;
2156	}
2157
2158	return B_OK;
2159}
2160
2161// VisitWriteAttrRequest
2162status_t
2163ClientConnection::VisitWriteAttrRequest(WriteAttrRequest* request)
2164{
2165	ConnectionReference connectionReference(this);
2166	if (!connectionReference.IsValid())
2167		return B_OK;
2168
2169	// get the volume
2170	status_t result = B_OK;
2171	ClientVolume* volume = _GetVolume(request->volumeID);
2172	if (!volume)
2173		result = B_BAD_VALUE;
2174	ClientVolumePutter volumePutter(this, volume);
2175
2176	VolumeManagerLocker managerLocker;
2177
2178	// get the node
2179	Node* node = NULL;
2180	if (result == B_OK) {
2181		node = volume->GetNode(request->nodeID);
2182		if (!node)
2183			result = B_ENTRY_NOT_FOUND;
2184	}
2185
2186	// check read permission
2187	if (result == B_OK) {
2188		if (!volume->GetNodePermissions(node).ImpliesWritePermission())
2189			result = B_PERMISSION_DENIED;
2190	}
2191
2192	// open the node
2193	FileHandle* handle = NULL;
2194	if (result == B_OK)
2195		result = volume->Open(node, O_RDWR, &handle);
2196	NodeHandleUnlocker handleUnlocker(volume, handle);
2197
2198	managerLocker.Unlock();
2199
2200	if (result == B_OK) {
2201		off_t pos = max(request->pos, (off_t)0);
2202		int32 size = request->data.GetSize();
2203		type_code type = request->type;
2204		char* buffer = (char*)request->data.GetData();
2205
2206		// convert the data, if necessary
2207		if (fInverseClientEndianess) {
2208			if (_KnownAttributeType(type)) {
2209				if (pos != 0) {
2210					WARN("WriteAttr(): WARNING: Need to convert attribute "
2211						"endianess, but position is not 0: attribute: %s, "
2212						"pos: %" B_PRIdOFF ", size: %" B_PRId32 "\n",
2213						request->name.GetString(), pos, size);
2214				}
2215				swap_data(type, buffer, size, B_SWAP_ALWAYS);
2216			} else
2217				type = B_SWAP_INT32(type);
2218		}
2219
2220		// write the data
2221		while (result == B_OK && size > 0) {
2222			size_t bytesWritten;
2223			result = handle->WriteAttr(request->name.GetString(),
2224				type, pos, buffer, size, &bytesWritten);
2225			if (result == B_OK) {
2226				pos += bytesWritten;
2227				buffer += bytesWritten;
2228				size -= bytesWritten;
2229			}
2230		}
2231
2232		// close the handle
2233		volume->Close(handle);
2234	}
2235
2236	// prepare the reply
2237	WriteAttrReply reply;
2238	// send the reply
2239	reply.error = result;
2240	return GetChannel()->SendRequest(&reply);
2241}
2242
2243// VisitRemoveAttrRequest
2244status_t
2245ClientConnection::VisitRemoveAttrRequest(RemoveAttrRequest* request)
2246{
2247	ConnectionReference connectionReference(this);
2248	if (!connectionReference.IsValid())
2249		return B_OK;
2250
2251	// get the volume
2252	status_t result = B_OK;
2253	ClientVolume* volume = _GetVolume(request->volumeID);
2254	if (!volume)
2255		result = B_BAD_VALUE;
2256	ClientVolumePutter volumePutter(this, volume);
2257
2258	VolumeManagerLocker managerLocker;
2259
2260	// get the node
2261	Node* node = NULL;
2262	if (result == B_OK) {
2263		node = volume->GetNode(request->nodeID);
2264		if (!node)
2265			result = B_ENTRY_NOT_FOUND;
2266	}
2267
2268	// check read permission
2269	if (result == B_OK) {
2270		if (!volume->GetNodePermissions(node).ImpliesWritePermission())
2271			result = B_PERMISSION_DENIED;
2272	}
2273
2274	// open the node
2275	FileHandle* handle = NULL;
2276	if (result == B_OK)
2277		result = volume->Open(node, O_RDWR, &handle);
2278	NodeHandleUnlocker handleUnlocker(volume, handle);
2279
2280	managerLocker.Unlock();
2281
2282	// remove the attribute and close the node
2283	if (result == B_OK) {
2284		result = handle->RemoveAttr(request->name.GetString());
2285		volume->Close(handle);
2286	}
2287
2288	// send the reply
2289	RemoveAttrReply reply;
2290	reply.error = result;
2291	return GetChannel()->SendRequest(&reply);
2292}
2293
2294// VisitRenameAttrRequest
2295status_t
2296ClientConnection::VisitRenameAttrRequest(RenameAttrRequest* request)
2297{
2298	// Not supported, since there's no API function to rename an attribute.
2299	// send the reply
2300	RemoveAttrReply reply;
2301	reply.error = B_UNSUPPORTED;
2302	return GetChannel()->SendRequest(&reply);
2303}
2304
2305// VisitStatAttrRequest
2306status_t
2307ClientConnection::VisitStatAttrRequest(StatAttrRequest* request)
2308{
2309	ConnectionReference connectionReference(this);
2310	if (!connectionReference.IsValid())
2311		return B_OK;
2312
2313	// get the volume
2314	status_t result = B_OK;
2315	ClientVolume* volume = _GetVolume(request->volumeID);
2316	if (!volume)
2317		result = B_BAD_VALUE;
2318	ClientVolumePutter volumePutter(this, volume);
2319
2320	VolumeManagerLocker managerLocker;
2321
2322	// get the node
2323	Node* node = NULL;
2324	if (result == B_OK) {
2325		node = volume->GetNode(request->nodeID);
2326		if (!node)
2327			result = B_ENTRY_NOT_FOUND;
2328	}
2329
2330	// check read permission
2331	if (result == B_OK) {
2332		if (!volume->GetNodePermissions(node).ImpliesReadPermission())
2333			result = B_PERMISSION_DENIED;
2334	}
2335
2336	// open the node
2337	FileHandle* handle = NULL;
2338	if (result == B_OK)
2339		result = volume->Open(node, O_RDONLY, &handle);
2340	NodeHandleUnlocker handleUnlocker(volume, handle);
2341
2342	managerLocker.Unlock();
2343
2344	// stat the attribute and close the node
2345	attr_info attrInfo;
2346	StatAttrReply reply;
2347	if (result == B_OK) {
2348		result = handle->StatAttr(request->name.GetString(), &attrInfo);
2349		volume->Close(handle);
2350	}
2351
2352	// set the attribute info
2353	if (result == B_OK) {
2354		result = _GetAttrInfo(request, request->name.GetString(), attrInfo,
2355			NULL, &reply.attrInfo);
2356	}
2357
2358	// send the reply
2359	reply.error = result;
2360	return GetChannel()->SendRequest(&reply);
2361}
2362
2363// VisitOpenQueryRequest
2364status_t
2365ClientConnection::VisitOpenQueryRequest(OpenQueryRequest* request)
2366{
2367	ConnectionReference connectionReference(this);
2368	if (!connectionReference.IsValid())
2369		return B_OK;
2370
2371	VolumeManagerLocker managerLocker;
2372
2373	// open the query
2374	status_t result;
2375	QueryHandle* handle = NULL;
2376	result = _OpenQuery(request->queryString.GetString(),
2377			request->flags, request->port, request->token, &handle);
2378	QueryHandleUnlocker handleUnlocker(this, handle);
2379
2380	// prepare the reply
2381	OpenQueryReply reply;
2382	if (result == B_OK)
2383		reply.cookie = handle->GetCookie();
2384
2385	managerLocker.Unlock();
2386
2387	// send the reply
2388	reply.error = result;
2389	status_t error = GetChannel()->SendRequest(&reply);
2390
2391	// close the handle, if a send error occurred
2392	if (error != B_OK && result == B_OK)
2393		_CloseQuery(handle);
2394
2395	return error;
2396}
2397
2398// VisitReadQueryRequest
2399status_t
2400ClientConnection::VisitReadQueryRequest(ReadQueryRequest* request)
2401{
2402	ConnectionReference connectionReference(this);
2403	if (!connectionReference.IsValid())
2404		return B_OK;
2405
2406	// create an array for the IDs of the client volumes a found entry may
2407	// reside on
2408	status_t result = B_OK;
2409	int32 volumeCount = fVolumes->Size();
2410	int32* volumeIDs = new(std::nothrow) int32[volumeCount];
2411	if (!volumeIDs)
2412		result = B_NO_MEMORY;
2413
2414	ArrayDeleter<int32> volumeIDsDeleter(volumeIDs);
2415
2416	// get the query handle
2417	QueryHandle* handle = NULL;
2418	if (result == B_OK)
2419		result = _LockQueryHandle(request->cookie, &handle);
2420	QueryHandleUnlocker handleUnlocker(this, handle);
2421
2422	// check if it is a query handle
2423	QueryHandle* queryHandle = NULL;
2424	if (result == B_OK) {
2425		queryHandle = dynamic_cast<QueryHandle*>(handle);
2426		if (!queryHandle)
2427			result = B_BAD_VALUE;
2428	}
2429
2430	// read the query
2431	ReadQueryReply reply;
2432	int32 countRead = 0;
2433	while (result == B_OK) {
2434		uint8 buffer[offsetof(struct dirent, d_name) + B_FILE_NAME_LENGTH];
2435		struct dirent* dirEntry = (struct dirent*)buffer;
2436
2437		result = queryHandle->ReadDir(dirEntry, 1, &countRead);
2438		if (result != B_OK)
2439			break;
2440		if (countRead == 0)
2441			break;
2442		PRINT("  query entry: %" B_PRIdDEV ", %" B_PRIdINO ", \"%s\"\n",
2443			dirEntry->d_pdev, dirEntry->d_pino, dirEntry->d_name);
2444
2445		VolumeManagerLocker managerLocker;
2446		VolumeManager* volumeManager = VolumeManager::GetDefault();
2447
2448		// load the entry
2449		Entry* entry = NULL;
2450		result = volumeManager->LoadEntry(dirEntry->d_pdev,
2451			dirEntry->d_pino, dirEntry->d_name, true, &entry);
2452
2453		// if at least one client volume contains the entry, get an entry info
2454		if (result == B_OK) {
2455			HasQueryPermissionClientVolumeFilter filter;
2456			int32 entryVolumeCount = _GetContainingClientVolumes(
2457				entry->GetDirectory(), volumeIDs, volumeCount, &filter);
2458			if (entryVolumeCount > 0) {
2459				// store all the client volume IDs in the reply
2460				for (int32 i = 0; i < entryVolumeCount; i++) {
2461					result = reply.clientVolumeIDs.Append(volumeIDs[i]);
2462					if (result != B_OK)
2463						break;
2464				}
2465
2466				// get an entry info
2467				_GetNodeInfo(entry->GetDirectory(), &reply.dirInfo);
2468				_GetEntryInfo(entry, &reply.entryInfo);
2469				break;
2470			} else
2471				PRINT(("  -> no client volumes\n"));
2472		}
2473
2474		// entry is not in the volume: next round...
2475		result = B_OK;
2476	}
2477
2478	// send the reply
2479	reply.error = result;
2480	reply.count = countRead;
2481	PRINT("ReadQuery: (%" B_PRIx32 ", %" B_PRId32 ", "
2482		"dir: (%" B_PRIdDEV ", %" B_PRIdINO "), "
2483		"node: (%" B_PRIdDEV ", %" B_PRIdINO ", `%s')\n",
2484		reply.error, reply.count,
2485		reply.entryInfo.directoryID.volumeID,
2486		reply.entryInfo.directoryID.nodeID,
2487		reply.entryInfo.nodeInfo.st.st_dev,
2488		reply.entryInfo.nodeInfo.st.st_ino,
2489		reply.entryInfo.name.GetString());
2490	return GetChannel()->SendRequest(&reply);
2491}
2492
2493
2494// #pragma mark -
2495
2496// ProcessNodeMonitoringEvent
2497void
2498ClientConnection::ProcessNodeMonitoringEvent(int32 volumeID,
2499	NodeMonitoringEvent* event)
2500{
2501	// get a connection reference
2502	ConnectionReference connectionReference(this);
2503	if (!connectionReference.IsValid())
2504		return;
2505
2506	_PushNodeMonitoringEvent(volumeID, event);
2507}
2508
2509// CloseNodeMonitoringEventQueue
2510void
2511ClientConnection::CloseNodeMonitoringEventQueue()
2512{
2513	typedef Vector<NodeMonitoringRequest*> RequestVector;
2514	const RequestVector* requests = NULL;
2515	if (fNodeMonitoringEvents->Close(false, &requests) == B_OK) {
2516		for (RequestVector::ConstIterator it = requests->Begin();
2517			 it != requests->End();
2518			 it++) {
2519			delete *it;
2520		}
2521	}
2522}
2523
2524
2525// #pragma mark -
2526
2527// QueryDomainIntersectsWith
2528bool
2529ClientConnection::QueryDomainIntersectsWith(Volume* volume)
2530{
2531	// Iterate through the the client volumes and check whether any one contains
2532	// the supplied volume or its root dir is on the volume. We don't check
2533	// directory inclusion for the latter, since we don't need to query the
2534	// volume, if the client volume is located on a volume mounted somewhere
2535	// under the supplied volume (e.g. the root FS contains everything, but does
2536	// seldomly need to be queried).
2537	VolumeManager* volumeManager = VolumeManager::GetDefault();
2538	AutoLocker<VolumeMap> volumesLocker(fVolumes);
2539	for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) {
2540		ClientVolume* clientVolume = it.Next().value;
2541		Directory* volumeRoot = volume->GetRootDirectory();
2542		Directory* clientVolumeRoot = clientVolume->GetRootDirectory();
2543		if (volumeManager->DirectoryContains(clientVolumeRoot, volumeRoot, true)
2544			|| volumeRoot->GetVolumeID() == clientVolumeRoot->GetVolumeID()) {
2545			return true;
2546		}
2547	}
2548
2549	return false;
2550}
2551
2552// ProcessQueryEvent
2553void
2554ClientConnection::ProcessQueryEvent(NodeMonitoringEvent* event)
2555{
2556	dev_t volumeID;
2557	ino_t directoryID;
2558	if (event->opcode == B_ENTRY_CREATED) {
2559		// "entry created" event
2560		EntryCreatedEvent* createdEvent
2561			= dynamic_cast<EntryCreatedEvent*>(event);
2562		if (!createdEvent)
2563			return;
2564		volumeID = createdEvent->volumeID;
2565		directoryID = createdEvent->directoryID;
2566
2567	} else if (event->opcode == B_ENTRY_REMOVED) {
2568		// "entry removed" event
2569		EntryRemovedEvent* removedEvent
2570			= dynamic_cast<EntryRemovedEvent*>(event);
2571		if (!removedEvent)
2572			return;
2573		volumeID = removedEvent->volumeID;
2574		directoryID = removedEvent->directoryID;
2575
2576	} else {
2577		// We only support "entry created" and "entry removed" query events.
2578		// "entry moved" is split by the volume manager into those.
2579		ERROR("Ignoring unexpected query event: opcode: 0x%" B_PRIx32 "\n",
2580			event->opcode);
2581		return;
2582	}
2583	PRINT("ClientConnection::ProcessQueryEvent(): event: %p, type: %s:"
2584		" directory: (%" B_PRIdDEV ", %" B_PRIdINO ")\n",
2585		event, typeid(event).name(), volumeID, directoryID);
2586
2587	// create an array for the IDs of the client volumes a found entry may
2588	// reside on
2589	int32 volumeCount = fVolumes->Size();
2590	int32* volumeIDs = new(std::nothrow) int32[volumeCount];
2591	if (!volumeIDs)
2592		return;
2593
2594	ArrayDeleter<int32> volumeIDsDeleter(volumeIDs);
2595
2596	HasQueryPermissionClientVolumeFilter filter;
2597
2598	// load the directory the concerned entry belongs/belonged to
2599	Directory* directory;
2600	int32 concernedVolumes = 0;
2601	if (VolumeManager::GetDefault()->LoadDirectory(volumeID, directoryID,
2602			&directory) == B_OK) {
2603		// find out, which client volumes the directory is located in
2604		concernedVolumes = _GetContainingClientVolumes(directory, volumeIDs,
2605			volumeCount, &filter);
2606	} else {
2607		// Failed to load the directory, so maybe it has already been
2608		// deleted. For "entry removed" events, we consider all client
2609		// volumes to be notified -- those that don't know the entry will
2610		// ignore the event.
2611		if (event->opcode == B_ENTRY_REMOVED) {
2612			concernedVolumes = _GetAllClientVolumeIDs(volumeIDs, volumeCount,
2613				&filter);
2614		}
2615	}
2616
2617	// now push the event for each concerned client volume
2618	for (int32 i = 0; i < concernedVolumes; i++)
2619		_PushNodeMonitoringEvent(volumeIDs[i], event);
2620	// TODO: More than one volume will usually only be concerned in case of
2621	// nested client volumes. We could optimize the case by having an array of
2622	// volume IDs in the respective requests sent over the net (just as in the
2623	// ReadQueryReply).
2624}
2625
2626
2627// #pragma mark -
2628
2629// _Close
2630void
2631ClientConnection::_Close()
2632{
2633	// terminate node monitoring processor
2634	CloseNodeMonitoringEventQueue();
2635	if (fNodeMonitoringProcessor >= 0
2636		&& find_thread(NULL) != fNodeMonitoringProcessor) {
2637		int32 result;
2638		wait_for_thread(fNodeMonitoringProcessor, &result);
2639		// The variable is not unset, when this is the node monitoring
2640		// processor thread -- which is good, since the destructor will
2641		// wait for the thread in this case.
2642		fNodeMonitoringProcessor = -1;
2643	}
2644	if (fConnection)
2645		fConnection->Close();
2646	// notify the listener
2647	ClientConnectionListener* listener = fListener;
2648	fListener = NULL;
2649	if (listener)
2650		listener->ClientConnectionClosed(this, fError);
2651}
2652
2653// _MarkClosed
2654void
2655ClientConnection::_MarkClosed(bool error)
2656{
2657	AutoLocker<Locker> _(fLock);
2658	if (!fClosed) {
2659		fClosed = true;
2660		fError = error;
2661	}
2662}
2663
2664// _GetNodeInfo
2665void
2666ClientConnection::_GetNodeInfo(Node* node, NodeInfo* info)
2667{
2668	if (node && info) {
2669		info->st = node->GetStat();
2670		info->revision = VolumeManager::GetDefault()->GetRevision();
2671	}
2672}
2673
2674// _GetEntryInfo
2675void
2676ClientConnection::_GetEntryInfo(Entry* entry, EntryInfo* info)
2677{
2678	if (entry && info) {
2679		info->directoryID.volumeID = entry->GetVolumeID();
2680		info->directoryID.nodeID = entry->GetDirectoryID();
2681		info->name.SetTo(entry->GetName());
2682		_GetNodeInfo(entry->GetNode(), &info->nodeInfo);
2683	}
2684}
2685
2686// _GetAttrInfo
2687status_t
2688ClientConnection::_GetAttrInfo(Request* request, const char* name,
2689	const attr_info& attrInfo, const void* data, AttributeInfo* info)
2690{
2691	if (!request || !name || !info)
2692		return B_BAD_VALUE;
2693
2694	info->name.SetTo(name);
2695	info->info = attrInfo;
2696	data = (attrInfo.size > 0 ? data : NULL);
2697	int32 dataSize = (data ? attrInfo.size : 0);
2698	info->data.SetTo(data, dataSize);
2699
2700	// if the client has inverse endianess, swap the type, if we don't know it
2701	if (fInverseClientEndianess) {
2702		if (_KnownAttributeType(info->info.type)) {
2703			// we need to convert the data, if supplied
2704			if (data) {
2705				// allocate a buffer
2706				RequestBuffer* requestBuffer = RequestBuffer::Create(dataSize);
2707				if (!requestBuffer)
2708					return B_NO_MEMORY;
2709
2710				// convert the data
2711				memcpy(requestBuffer->GetData(), data, dataSize);
2712				_ConvertAttribute(info->info, requestBuffer->GetData());
2713			}
2714		} else
2715			info->info.type = B_SWAP_INT32(info->info.type);
2716	}
2717
2718	return B_OK;
2719}
2720
2721// _GetAttrDirInfo
2722status_t
2723ClientConnection::_GetAttrDirInfo(Request* request, AttributeDirectory* attrDir,
2724	AttrDirInfo* info)
2725{
2726	if (!request || !attrDir || !info || !attrDir->IsAttrDirValid())
2727		return B_BAD_VALUE;
2728
2729	// add the attribute infos
2730	for (Attribute* attribute = attrDir->GetFirstAttribute();
2731		 attribute;
2732		 attribute = attrDir->GetNextAttribute(attribute)) {
2733		// get the attribute info
2734		AttributeInfo attrInfo;
2735		attr_info bAttrInfo;
2736		attribute->GetInfo(&bAttrInfo);
2737		status_t error = _GetAttrInfo(request, attribute->GetName(), bAttrInfo,
2738			attribute->GetData(), &attrInfo);
2739
2740		// append it
2741		if (error == B_OK)
2742			error = info->attributeInfos.Append(attrInfo);
2743		if (error != B_OK)
2744			return error;
2745	}
2746
2747	info->revision = VolumeManager::GetDefault()->GetRevision();
2748	info->isValid = true;
2749
2750	return B_OK;
2751}
2752
2753// _CreateVolume
2754status_t
2755ClientConnection::_CreateVolume(ClientVolume** _volume)
2756{
2757	// create and init the volume
2758	ClientVolume* volume = new(std::nothrow) ClientVolume(fSecurityContextLock,
2759		this);
2760	if (!volume)
2761		return B_NO_MEMORY;
2762	status_t error = volume->Init();
2763	if (error != B_OK) {
2764		delete volume;
2765		return error;
2766	}
2767
2768	// add it to the volume map
2769	AutoLocker<VolumeMap> locker(fVolumes);
2770	error = fVolumes->Put(volume->GetID(), volume);
2771	locker.Unlock();
2772
2773	if (error == B_OK)
2774		*_volume = volume;
2775	else
2776		delete volume;
2777
2778	return error;
2779}
2780
2781// _GetVolume
2782ClientVolume*
2783ClientConnection::_GetVolume(int32 id)
2784{
2785	AutoLocker<VolumeMap> _(fVolumes);
2786	ClientVolume* volume = fVolumes->Get(id);
2787	if (!volume || volume->IsRemoved())
2788		return NULL;
2789	volume->AcquireReference();
2790	return volume;
2791}
2792
2793// _PutVolume
2794//
2795// The VolumeManager may be locked, but no other lock must be held.
2796void
2797ClientConnection::_PutVolume(ClientVolume* volume)
2798{
2799	if (!volume)
2800		return;
2801
2802	// decrement reference counter and remove the volume, if 0
2803	AutoLocker<VolumeMap> locker(fVolumes);
2804	bool removed = (volume->ReleaseReference() == 1 && volume->IsRemoved());
2805	if (removed)
2806		fVolumes->Remove(volume->GetID());
2807	locker.Unlock();
2808
2809	if (removed) {
2810		VolumeManagerLocker managerLocker;
2811		delete volume;
2812	}
2813}
2814
2815// _UnmountVolume
2816//
2817// The caller must have a reference to the volume.
2818void
2819ClientConnection::_UnmountVolume(ClientVolume* volume)
2820{
2821	if (!volume)
2822		return;
2823	AutoLocker<VolumeMap> locker(fVolumes);
2824	volume->MarkRemoved();
2825	locker.Unlock();
2826
2827	// push a notification event
2828	if (VolumeUnmountedEvent* event = new(std::nothrow) VolumeUnmountedEvent) {
2829		VolumeManagerLocker managerLocker;
2830
2831		event->opcode = B_DEVICE_UNMOUNTED;
2832		_PushNodeMonitoringEvent(volume->GetID(), event);
2833		event->ReleaseReference();
2834	}
2835}
2836
2837// _UnmountAllVolumes
2838void
2839ClientConnection::_UnmountAllVolumes()
2840{
2841	while (true) {
2842		// To avoid heap allocation (which can fail) we unmount the volumes
2843		// chunkwise.
2844		// get the volumes
2845		const int32 volumeChunkSize = 32;
2846		ClientVolume* volumes[volumeChunkSize];
2847		int32 volumeCount = 0;
2848		AutoLocker<VolumeMap> volumesLocker(fVolumes);
2849		for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) {
2850			if (ClientVolume* volume = _GetVolume(it.Next().value->GetID())) {
2851				volumes[volumeCount++] = volume;
2852			}
2853			if (volumeCount == volumeChunkSize)
2854				break;
2855		}
2856		volumesLocker.Unlock();
2857
2858		// unmount and put the volumes
2859		for (int32 i = 0; i < volumeCount; i++) {
2860			ClientVolume* volume = volumes[i];
2861			_UnmountVolume(volume);
2862			_PutVolume(volume);
2863		}
2864
2865		if (volumeCount < volumeChunkSize)
2866			break;
2867	}
2868}
2869
2870// _NodeMonitoringProcessorEntry
2871int32
2872ClientConnection::_NodeMonitoringProcessorEntry(void* data)
2873{
2874	return ((ClientConnection*)data)->_NodeMonitoringProcessor();
2875}
2876
2877
2878// _NodeMonitoringProcessor
2879int32
2880ClientConnection::_NodeMonitoringProcessor()
2881{
2882	while (!fClosed) {
2883		// get the next request
2884		NodeMonitoringRequest* request = NULL;
2885		status_t error = fNodeMonitoringEvents->Pop(&request);
2886
2887		// get a client connection reference
2888		ConnectionReference connectionReference(this);
2889		if (!connectionReference.IsValid())
2890			return B_OK;
2891
2892		// No request? Next round...
2893		if (error != B_OK)
2894			continue;
2895		ObjectDeleter<NodeMonitoringRequest> requestDeleter(request);
2896
2897		// send the request
2898		error = fConnection->SendRequest(request);
2899		if (error != B_OK) {
2900			ERROR(("ClientConnection::_NodeMonitoringProcessor(): "
2901				"Failed to send request.\n"));
2902		}
2903	}
2904	return 0;
2905}
2906
2907
2908// _PushNodeMonitoringEvent
2909//
2910// The caller must have a connection reference. Moreover the VolumeManager
2911// must be locked.
2912status_t
2913ClientConnection::_PushNodeMonitoringEvent(int32 volumeID,
2914	NodeMonitoringEvent* event)
2915{
2916	if (!event)
2917		return B_BAD_VALUE;
2918
2919	// get the volume
2920	ClientVolume* volume = _GetVolume(volumeID);
2921	if (!volume && event->opcode != B_DEVICE_UNMOUNTED)
2922		return B_BAD_VALUE;
2923	ClientVolumePutter volumePutter(this, volume);
2924
2925	// create a node monitoring request
2926	NodeMonitoringRequest* request = NULL;
2927	status_t error = B_ERROR;
2928	switch (event->opcode) {
2929		case B_ENTRY_CREATED:
2930			error = _EntryCreated(volume,
2931				dynamic_cast<EntryCreatedEvent*>(event), request);
2932			break;
2933		case B_ENTRY_REMOVED:
2934			error = _EntryRemoved(volume,
2935				dynamic_cast<EntryRemovedEvent*>(event), request);
2936			break;
2937		case B_ENTRY_MOVED:
2938			error = _EntryMoved(volume,
2939				dynamic_cast<EntryMovedEvent*>(event), request);
2940			break;
2941		case B_STAT_CHANGED:
2942			error = _NodeStatChanged(volume,
2943				dynamic_cast<StatChangedEvent*>(event), request);
2944			break;
2945		case B_ATTR_CHANGED:
2946			error = _NodeAttributeChanged(volume,
2947				dynamic_cast<AttributeChangedEvent*>(event), request);
2948			break;
2949		case B_DEVICE_UNMOUNTED:
2950			error = B_OK;
2951			break;
2952	}
2953
2954	// replace all data buffers -- when the request is actually sent, they
2955	// might no longer exist
2956	if (error == B_OK)
2957		error = RequestBufferReplacer().ReplaceBuffer(request);
2958
2959	if (error == B_OK) {
2960		// common initialization
2961		request->volumeID = volumeID;
2962		request->opcode = event->opcode;
2963		request->revision = VolumeManager::GetDefault()->GetRevision();
2964
2965		// push the request
2966		error = fNodeMonitoringEvents->Push(request);
2967		if (error != B_OK)
2968			delete request;
2969	}
2970
2971	return error;
2972}
2973
2974// _EntryCreated
2975status_t
2976ClientConnection::_EntryCreated(ClientVolume* volume, EntryCreatedEvent* event,
2977	NodeMonitoringRequest*& _request)
2978{
2979	// allocate the request
2980	EntryCreatedRequest* request = new(std::nothrow) EntryCreatedRequest;
2981	if (!request)
2982		return B_NO_MEMORY;
2983	ObjectDeleter<NodeMonitoringRequest> requestDeleter(request);
2984
2985	// get the name
2986	const char* name = event->name.GetString();
2987
2988	// set the request fields
2989	request->directoryID = NodeID(event->volumeID, event->directoryID);
2990	request->nodeID = NodeID(event->volumeID, event->nodeID);
2991	request->name.SetTo(name);
2992	if (event->queryHandler) {
2993		request->port = event->remotePort;
2994		request->token = event->remoteToken;
2995		request->queryUpdate = true;
2996	} else
2997		request->queryUpdate = false;
2998
2999	// try to get an entry info
3000	Entry* entry;
3001	if (VolumeManager::GetDefault()->LoadEntry(event->volumeID,
3002			event->directoryID, name, true, &entry) == B_OK
3003		&& entry->GetNode()->GetVolumeID() == event->volumeID
3004		&& entry->GetNode()->GetID() == event->nodeID) {
3005		_GetEntryInfo(entry, &request->entryInfo);
3006		request->entryInfoValid = true;
3007	} else
3008		request->entryInfoValid = false;
3009
3010	requestDeleter.Detach();
3011	_request = request;
3012	return B_OK;
3013}
3014
3015// _EntryRemoved
3016status_t
3017ClientConnection::_EntryRemoved(ClientVolume* volume, EntryRemovedEvent* event,
3018	NodeMonitoringRequest*& _request)
3019{
3020	// special handling, if it is the root node of the client volume that has
3021	// been removed
3022	if (!event->queryHandler
3023		&& NodeRef(event->nodeVolumeID, event->nodeID)
3024			== volume->GetRootNodeRef()) {
3025		NoAllocEntryRef ref(event->nodeVolumeID, event->nodeID, ".");
3026		BEntry entry;
3027		if (FDManager::SetEntry(&entry, &ref) != B_OK || !entry.Exists())
3028			_UnmountVolume(volume);
3029
3030		// don't send the "entry removed" event
3031		return B_ERROR;
3032	}
3033
3034	// allocate the request
3035	EntryRemovedRequest* request = new(std::nothrow) EntryRemovedRequest;
3036	if (!request)
3037		return B_NO_MEMORY;
3038	ObjectDeleter<NodeMonitoringRequest> requestDeleter(request);
3039
3040	// get the name
3041	const char* name = event->name.GetString();
3042
3043	// set the request fields
3044	request->directoryID = NodeID(event->volumeID, event->directoryID);
3045	request->nodeID = NodeID(event->nodeVolumeID, event->nodeID);
3046	request->name.SetTo(name);
3047	if (event->queryHandler) {
3048		request->port = event->remotePort;
3049		request->token = event->remoteToken;
3050		request->queryUpdate = true;
3051	} else
3052		request->queryUpdate = false;
3053
3054	requestDeleter.Detach();
3055	_request = request;
3056	return B_OK;
3057}
3058
3059// _EntryMoved
3060status_t
3061ClientConnection::_EntryMoved(ClientVolume* volume, EntryMovedEvent* event,
3062	NodeMonitoringRequest*& _request)
3063{
3064	// allocate the request
3065	EntryMovedRequest* request = new(std::nothrow) EntryMovedRequest;
3066	if (!request)
3067		return B_NO_MEMORY;
3068	ObjectDeleter<NodeMonitoringRequest> requestDeleter(request);
3069
3070	// allocate memory for the names
3071	int32 fromNameLen = event->fromName.GetLength();
3072	const char* fromName
3073		= (fromNameLen > 0 ? event->fromName.GetString() : NULL);
3074	const char* toName = event->toName.GetString();
3075
3076	// set the request fields
3077	request->fromDirectoryID = NodeID(event->volumeID, event->fromDirectoryID);
3078	request->toDirectoryID = NodeID(event->volumeID, event->toDirectoryID);
3079	request->nodeID = NodeID(event->nodeVolumeID, event->nodeID);
3080	request->fromName.SetTo(fromName);
3081	request->toName.SetTo(toName);
3082	request->queryUpdate = false;
3083
3084	// try to get an entry info
3085	Entry* entry;
3086	if (VolumeManager::GetDefault()->LoadEntry(event->volumeID,
3087			event->toDirectoryID, toName, true, &entry) == B_OK
3088		&& entry->GetNode()->GetVolumeID() == event->nodeVolumeID
3089		&& entry->GetNode()->GetID() == event->nodeID) {
3090		_GetEntryInfo(entry, &request->entryInfo);
3091		request->entryInfoValid = true;
3092	} else
3093		request->entryInfoValid = false;
3094
3095	requestDeleter.Detach();
3096	_request = request;
3097	return B_OK;
3098}
3099
3100// _NodeStatChanged
3101status_t
3102ClientConnection::_NodeStatChanged(ClientVolume* volume,
3103	StatChangedEvent* event, NodeMonitoringRequest*& _request)
3104{
3105	// get the node
3106	Node* node = volume->GetNode(event->volumeID, event->nodeID);
3107	if (!node)
3108		return B_ENTRY_NOT_FOUND;
3109
3110	// allocate the request
3111	StatChangedRequest* request = new(std::nothrow) StatChangedRequest;
3112	if (!request)
3113		return B_NO_MEMORY;
3114	ObjectDeleter<NodeMonitoringRequest> requestDeleter(request);
3115
3116	// set the request fields
3117	request->nodeID = NodeID(event->volumeID, event->nodeID);
3118	_GetNodeInfo(node, &request->nodeInfo);
3119	request->queryUpdate = false;
3120
3121	requestDeleter.Detach();
3122	_request = request;
3123	return B_OK;
3124}
3125
3126// _NodeAttributeChanged
3127status_t
3128ClientConnection::_NodeAttributeChanged(ClientVolume* volume,
3129	AttributeChangedEvent* event, NodeMonitoringRequest*& _request)
3130{
3131	// get the node
3132	Node* node = volume->GetNode(event->volumeID, event->nodeID);
3133	if (!node)
3134		return B_ENTRY_NOT_FOUND;
3135
3136	// update the attribute directory
3137	bool removed = false;
3138	bool valid = false;
3139	attr_info info;
3140	const void* data = NULL;
3141	status_t error = node->UpdateAttribute(event->attribute.GetString(),
3142		&removed, &info, &data);
3143	valid = (error == B_OK);
3144
3145	// allocate the request
3146	AttributeChangedRequest* request = new(std::nothrow) AttributeChangedRequest;
3147	if (!request)
3148		return B_NO_MEMORY;
3149	ObjectDeleter<NodeMonitoringRequest> requestDeleter(request);
3150
3151	// get an attr dir info, if the directory is valid
3152	if (node->IsAttrDirValid()) {
3153		status_t error = _GetAttrDirInfo(request, node, &request->attrDirInfo);
3154		if (error != B_OK)
3155			return error;
3156	}
3157
3158	// get name and the data size
3159	int32 dataSize = (data ? info.size : 0);
3160	const char* name = event->attribute.GetString();
3161
3162	// set the request fields
3163	request->nodeID = NodeID(event->volumeID, event->nodeID);
3164	request->attrInfo.name.SetTo(name);
3165	request->valid = valid;
3166	request->removed = removed;
3167	if (!removed && valid) {
3168		request->attrInfo.info = info;
3169		request->attrInfo.data.SetTo(data, dataSize);
3170	}
3171	request->queryUpdate = false;
3172
3173	requestDeleter.Detach();
3174	_request = request;
3175	return B_OK;
3176}
3177
3178// _KnownAttributeType
3179bool
3180ClientConnection::_KnownAttributeType(type_code type)
3181{
3182	if (!fInverseClientEndianess)
3183		return false;
3184
3185	switch (type) {
3186		case B_BOOL_TYPE:
3187		case B_CHAR_TYPE:
3188		case B_COLOR_8_BIT_TYPE:
3189		case B_DOUBLE_TYPE:
3190		case B_FLOAT_TYPE:
3191		case B_GRAYSCALE_8_BIT_TYPE:
3192		case B_INT64_TYPE:
3193		case B_INT32_TYPE:
3194		case B_INT16_TYPE:
3195		case B_INT8_TYPE:
3196		case B_MESSAGE_TYPE:
3197		case B_MESSENGER_TYPE:
3198		case B_MIME_TYPE:
3199		case B_MONOCHROME_1_BIT_TYPE:
3200		case B_OFF_T_TYPE:
3201		case B_POINTER_TYPE:
3202		case B_POINT_TYPE:
3203		case B_RECT_TYPE:
3204		case B_REF_TYPE:
3205		case B_RGB_COLOR_TYPE:
3206		case B_SIZE_T_TYPE:
3207		case B_SSIZE_T_TYPE:
3208		case B_STRING_TYPE:
3209		case B_TIME_TYPE:
3210		case B_UINT64_TYPE:
3211		case B_UINT32_TYPE:
3212		case B_UINT16_TYPE:
3213		case B_UINT8_TYPE:
3214		case B_ASCII_TYPE:
3215		case B_MIME_STRING_TYPE:
3216			return true;
3217
3218		//B_RGB_32_BIT_TYPE: We could translate it, but it's heavy...
3219	}
3220
3221	return false;
3222}
3223
3224// _ConvertAttribute
3225void
3226ClientConnection::_ConvertAttribute(const attr_info& info, void* buffer)
3227{
3228	swap_data(info.type, buffer, info.size, B_SWAP_ALWAYS);
3229}
3230
3231
3232// #pragma mark -
3233
3234// _OpenQuery
3235status_t
3236ClientConnection::_OpenQuery(const char* queryString, uint32 flags,
3237	port_id remotePort, int32 remoteToken, QueryHandle** _handle)
3238{
3239	if (!queryString || !_handle)
3240		return B_BAD_VALUE;
3241
3242	// open query
3243	QueryHandle* queryHandle;
3244	status_t error = VolumeManager::GetDefault()->OpenQuery(this, queryString,
3245		flags, remotePort, remoteToken, &queryHandle);
3246	if (error != B_OK)
3247		return error;
3248	BReference<QueryHandle> handleReference(queryHandle, true);
3249
3250	// lock the handle
3251	queryHandle->Lock();
3252
3253	// add the handle
3254	error = fQueryHandles->AddNodeHandle(queryHandle);
3255	if (error != B_OK)
3256		return error;
3257
3258	handleReference.Detach();
3259	*_handle = queryHandle;
3260	return B_OK;
3261}
3262
3263// _CloseQuery
3264status_t
3265ClientConnection::_CloseQuery(QueryHandle* handle)
3266{
3267	if (!handle || !fQueryHandles->RemoveNodeHandle(handle))
3268		return B_BAD_VALUE;
3269
3270	return B_OK;
3271}
3272
3273// _LockQueryHandle
3274//
3275// VolumeManager must NOT be locked.
3276status_t
3277ClientConnection::_LockQueryHandle(int32 cookie, QueryHandle** _handle)
3278{
3279	NodeHandle* handle;
3280	status_t error = fQueryHandles->LockNodeHandle(cookie, &handle);
3281	if (error == B_OK)
3282		*_handle = static_cast<QueryHandle*>(handle);
3283	return error;
3284}
3285
3286// _UnlockQueryHandle
3287//
3288// VolumeManager may or may not be locked.
3289void
3290ClientConnection::_UnlockQueryHandle(NodeHandle* nodeHandle)
3291{
3292	fQueryHandles->UnlockNodeHandle(nodeHandle);
3293}
3294
3295
3296// #pragma mark -
3297
3298// _GetAllClientVolumeIDs
3299int32
3300ClientConnection::_GetAllClientVolumeIDs(int32* volumeIDs, int32 arraySize,
3301	ClientVolumeFilter* filter)
3302{
3303	int32 count = 0;
3304	AutoLocker<VolumeMap> volumesLocker(fVolumes);
3305	for (VolumeMap::Iterator it = fVolumes->GetIterator();
3306		 it.HasNext() && arraySize > count;) {
3307		ClientVolume* clientVolume = it.Next().value;
3308		if (!filter || filter->FilterVolume(this, clientVolume))
3309			volumeIDs[count++] = clientVolume->GetID();
3310	}
3311
3312	return count;
3313}
3314
3315// _GetContainingClientVolumes
3316int32
3317ClientConnection::_GetContainingClientVolumes(Directory* directory,
3318	int32* volumeIDs, int32 arraySize, ClientVolumeFilter* filter)
3319{
3320	int32 count = 0;
3321	VolumeManager* volumeManager = VolumeManager::GetDefault();
3322	AutoLocker<VolumeMap> volumesLocker(fVolumes);
3323	for (VolumeMap::Iterator it = fVolumes->GetIterator();
3324		 it.HasNext() && arraySize > count;) {
3325		ClientVolume* clientVolume = it.Next().value;
3326		Directory* clientVolumeRoot = clientVolume->GetRootDirectory();
3327		if (volumeManager->DirectoryContains(clientVolumeRoot, directory, true)
3328			&& (!filter || filter->FilterVolume(this, clientVolume))) {
3329			volumeIDs[count++] = clientVolume->GetID();
3330		}
3331	}
3332
3333	return count;
3334}
3335
3336
3337// #pragma mark -
3338// #pragma mark ----- ClientConnectionListener -----
3339
3340// constructor
3341ClientConnectionListener::ClientConnectionListener()
3342{
3343}
3344
3345// destructor
3346ClientConnectionListener::~ClientConnectionListener()
3347{
3348}
3349
3350