1// ServerVolume.cpp
2
3#include "ServerVolume.h"
4
5#include <new>
6
7#include <AutoDeleter.h>
8#include <AutoLocker.h>
9
10#include "Compatibility.h"
11#include "DebugSupport.h"
12#include "ExtendedServerInfo.h"
13#include "QueryManager.h"
14#include "SendReceiveRequest.h"
15#include "ServerConnection.h"
16#include "ServerConnectionProvider.h"
17#include "ServerQueryIterator.h"
18#include "ShareVolume.h"
19#include "VolumeEvent.h"
20#include "VolumeManager.h"
21#include "VolumeSupport.h"
22
23// constructor
24ServerVolume::ServerVolume(VolumeManager* volumeManager,
25	ExtendedServerInfo* serverInfo)
26	: VirtualVolume(volumeManager),
27	  fServerInfo(serverInfo),
28	  fConnectionProvider(NULL)
29{
30	fServerInfo->AcquireReference();
31}
32
33// destructor
34ServerVolume::~ServerVolume()
35{
36	if (fConnectionProvider)
37		fConnectionProvider->ReleaseReference();
38	if (fServerInfo)
39		fServerInfo->ReleaseReference();
40}
41
42// GetServerAddress
43NetAddress
44ServerVolume::GetServerAddress()
45{
46	AutoLocker<Locker> _(fLock);
47	return fServerInfo->GetAddress();
48}
49
50// SetServerInfo
51void
52ServerVolume::SetServerInfo(ExtendedServerInfo* serverInfo)
53{
54	if (!serverInfo)
55		return;
56
57	// set the new info
58	fLock.Lock();
59	fServerInfo->ReleaseReference();
60	fServerInfo = serverInfo;
61	fServerInfo->AcquireReference();
62	BReference<ExtendedServerInfo> newReference(fServerInfo);
63
64	// remove shares, that are no longer there
65
66	// init a directory iterator
67	VirtualDirIterator iterator;
68	iterator.SetDirectory(fRootNode, true);
69
70	// iterate through the directory
71	const char* name;
72	Node* node;
73	while (iterator.GetCurrentEntry(&name, &node)) {
74		iterator.NextEntry();
75		// TODO: Searching by name is currently O(n).
76		bool remove = (!serverInfo->GetShareInfo(name));
77		fLock.Unlock();
78
79		if (remove) {
80			PRINT("  removing share: %s\n", name);
81			if (Volume* volume = GetChildVolume(name)) {
82				volume->SetUnmounting(true);
83				volume->PutVolume();
84			}
85		}
86
87		fLock.Lock();
88	}
89
90	// uninit the directory iterator
91	iterator.SetDirectory(NULL);
92	fLock.Unlock();
93
94	// add new shares
95	int32 count = serverInfo->CountShares();
96	for (int32 i = 0; i < count; i++) {
97		ExtendedShareInfo* shareInfo = serverInfo->ShareInfoAt(i);
98		const char* shareName = shareInfo->GetShareName();
99
100		Volume* volume = GetChildVolume(shareName);
101		if (volume) {
102			volume->PutVolume();
103		} else {
104			PRINT("  adding share: %s\n",
105				shareInfo->GetShareName());
106			status_t error = _AddShare(shareInfo);
107			if (error != B_OK) {
108				ERROR("ServerVolume::SetServerInfo(): ERROR: Failed to add "
109					"share `%s': %s\n", shareName, strerror(error));
110			}
111		}
112	}
113}
114
115// Init
116status_t
117ServerVolume::Init(const char* name)
118{
119	status_t error = VirtualVolume::Init(name);
120	if (error != B_OK)
121		return error;
122
123	// create the server connection provider
124	fConnectionProvider = new ServerConnectionProvider(fVolumeManager,
125		fServerInfo, GetRootID());
126	if (!fConnectionProvider) {
127		Uninit();
128		return B_NO_MEMORY;
129	}
130	error = fConnectionProvider->Init();
131	if (error != B_OK) {
132		Uninit();
133		return error;
134	}
135
136	// add share volumes
137	int32 count = fServerInfo->CountShares();
138	for (int32 i = 0; i < count; i++) {
139		ExtendedShareInfo* shareInfo = fServerInfo->ShareInfoAt(i);
140
141		error = _AddShare(shareInfo);
142		if (error != B_OK) {
143			ERROR("ServerVolume::Init(): ERROR: Failed to add share `%s': "
144				"%s\n", shareInfo->GetShareName(), strerror(error));
145		}
146	}
147
148	return B_OK;
149}
150
151// Uninit
152void
153ServerVolume::Uninit()
154{
155	if (fConnectionProvider)
156		fConnectionProvider->CloseServerConnection();
157
158	VirtualVolume::Uninit();
159}
160
161// PrepareToUnmount
162void
163ServerVolume::PrepareToUnmount()
164{
165	VirtualVolume::PrepareToUnmount();
166}
167
168// HandleEvent
169void
170ServerVolume::HandleEvent(VolumeEvent* event)
171{
172	if (event->GetType() == CONNECTION_BROKEN_EVENT) {
173		// tell all share volumes that they have been disconnected
174
175		// init a directory iterator
176		fLock.Lock();
177		VirtualDirIterator iterator;
178		iterator.SetDirectory(fRootNode, true);
179
180		// iterate through the directory
181		const char* name;
182		Node* node;
183		while (iterator.GetCurrentEntry(&name, &node)) {
184			iterator.NextEntry();
185			Volume* volume = fVolumeManager->GetVolume(node->GetID());
186			fLock.Unlock();
187			if (ShareVolume* shareVolume = dynamic_cast<ShareVolume*>(volume))
188				shareVolume->ConnectionClosed();
189			if (volume)
190				volume->PutVolume();
191			fLock.Lock();
192		}
193
194		// uninit the directory iterator
195		iterator.SetDirectory(NULL);
196
197		// mark ourselves unmounting
198		SetUnmounting(true);
199		fLock.Unlock();
200	}
201}
202
203
204// #pragma mark -
205// #pragma mark ----- FS -----
206
207// Unmount
208status_t
209ServerVolume::Unmount()
210{
211	return B_OK;
212}
213
214
215// #pragma mark -
216// #pragma mark ----- queries -----
217
218// OpenQuery
219status_t
220ServerVolume::OpenQuery(const char* queryString, uint32 flags, port_id port,
221	int32 token, QueryIterator** _iterator)
222{
223// TODO: Do nothing when there are no (mounted) shares.
224	// get connection
225	ServerConnection* serverConnection
226		= fConnectionProvider->GetExistingServerConnection();
227	if (!serverConnection)
228		return ERROR_NOT_CONNECTED;
229	RequestConnection* connection = serverConnection->GetRequestConnection();
230
231	// create a query iterator and add it to the query manager
232	ServerQueryIterator* iterator = new(std::nothrow) ServerQueryIterator(this);
233	if (!iterator)
234		return B_NO_MEMORY;
235	QueryManager* queryManager = fVolumeManager->GetQueryManager();
236	status_t error = queryManager->AddIterator(iterator);
237	if (error != B_OK) {
238		delete iterator;
239		return error;
240	}
241	QueryIteratorPutter iteratorPutter(queryManager, iterator);
242
243	// prepare the request
244	OpenQueryRequest request;
245	request.queryString.SetTo(queryString);
246	request.flags = flags;
247	request.port = port;
248	request.token = token;
249
250	// send the request
251	OpenQueryReply* reply;
252	error = SendRequest(connection, &request, &reply);
253	if (error != B_OK)
254		RETURN_ERROR(error);
255	ObjectDeleter<Request> replyDeleter(reply);
256	if (reply->error != B_OK)
257		RETURN_ERROR(reply->error);
258
259	// set the result
260	iterator->SetRemoteCookie(reply->cookie);
261	*_iterator = iterator;
262	iteratorPutter.Detach();
263	return B_OK;
264}
265
266// FreeQueryIterator
267void
268ServerVolume::FreeQueryIterator(QueryIterator* _iterator)
269{
270	ServerQueryIterator* iterator
271		= dynamic_cast<ServerQueryIterator*>(_iterator);
272
273	int32 cookie = iterator->GetRemoteCookie();
274	if (cookie >= 0) {
275		// prepare the close request
276		CloseRequest request;
277		request.volumeID = -1;
278		request.cookie = cookie;
279
280		// send the request
281		ServerConnection* serverConnection
282			= fConnectionProvider->GetExistingServerConnection();
283		if (serverConnection && serverConnection->IsConnected()) {
284			CloseReply* reply;
285			status_t error = SendRequest(
286				serverConnection->GetRequestConnection(), &request, &reply);
287			if (error == B_OK)
288				delete reply;
289		}
290	}
291
292	delete iterator;
293}
294
295// ReadQuery
296status_t
297ServerVolume::ReadQuery(QueryIterator* _iterator, struct dirent* buffer,
298	size_t bufferSize, int32 count, int32* countRead)
299{
300	// get connection
301	ServerConnection* serverConnection
302		= fConnectionProvider->GetExistingServerConnection();
303	if (!serverConnection)
304		return ERROR_NOT_CONNECTED;
305	RequestConnection* connection = serverConnection->GetRequestConnection();
306
307	ServerQueryIterator* iterator
308		= dynamic_cast<ServerQueryIterator*>(_iterator);
309
310	*countRead = 0;
311
312	for (;;) {
313		// if the iterator hasn't cached any more share volume IDs, we need to
314		// ask the server for the next entry
315		if (!iterator->HasNextShareVolumeID()) {
316			// prepare the request
317			ReadQueryRequest request;
318			request.cookie = iterator->GetRemoteCookie();
319			request.count = 1;
320
321			// send the request
322			ReadQueryReply* reply;
323			status_t error = SendRequest(connection, &request, &reply);
324			if (error != B_OK)
325				RETURN_ERROR(error);
326			ObjectDeleter<Request> replyDeleter(reply);
327			if (reply->error != B_OK)
328				RETURN_ERROR(reply->error);
329
330			// check, if anything has been read at all
331			if (reply->count == 0) {
332				*countRead = 0;
333				return B_OK;
334			}
335
336			// update the iterator
337			error = iterator->SetEntry(reply->clientVolumeIDs.GetElements(),
338				reply->clientVolumeIDs.CountElements(), reply->dirInfo,
339				reply->entryInfo);
340			if (error != B_OK)
341				return error;
342		}
343
344		// get the next concerned share volume and delegate the rest of the work
345		int32 volumeID = iterator->NextShareVolumeID();
346		ShareVolume* shareVolume = _GetShareVolume(volumeID);
347		if (!shareVolume)
348			continue;
349		VolumePutter volumePutter(shareVolume);
350
351		return shareVolume->GetQueryEntry(iterator->GetEntryInfo(),
352			iterator->GetDirectoryInfo(), buffer, bufferSize, countRead);
353	}
354}
355
356
357// #pragma mark -
358// #pragma mark ----- private -----
359
360// _AddShare
361status_t
362ServerVolume::_AddShare(ExtendedShareInfo* shareInfo)
363{
364	// create the share volume
365	ShareVolume* shareVolume = new(std::nothrow) ShareVolume(fVolumeManager,
366		fConnectionProvider, fServerInfo, shareInfo);
367	if (!shareVolume)
368		return B_NO_MEMORY;
369	status_t error = shareVolume->Init(shareInfo->GetShareName());
370	if (error != B_OK) {
371		delete shareVolume;
372		return error;
373	}
374
375	// add the volume to the volume manager
376	error = fVolumeManager->AddVolume(shareVolume);
377	if (error != B_OK) {
378		delete shareVolume;
379		return error;
380	}
381	VolumePutter volumePutter(shareVolume);
382
383	// add the volume to us
384	error = AddChildVolume(shareVolume);
385	if (error != B_OK) {
386		shareVolume->SetUnmounting(true);
387		return error;
388	}
389
390	return B_OK;
391}
392
393// _GetShareVolume
394ShareVolume*
395ServerVolume::_GetShareVolume(int32 volumeID)
396{
397	AutoLocker<Locker> locker(fLock);
398	VirtualDirIterator dirIterator;
399	dirIterator.SetDirectory(fRootNode, true);
400
401	// iterate through the directory
402	const char* name;
403	Node* node;
404	while (dirIterator.GetCurrentEntry(&name, &node)) {
405		Volume* volume = fVolumeManager->GetVolume(node->GetID());
406		ShareVolume* shareVolume = dynamic_cast<ShareVolume*>(volume);
407		if (shareVolume && shareVolume->GetID() == volumeID)
408			return shareVolume;
409
410		volume->PutVolume();
411		dirIterator.NextEntry();
412	}
413
414	return NULL;
415}
416
417