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