xref: /haiku/src/add-ons/kernel/file_systems/netfs/client/ServerVolume.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
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->AcquireReference();
31 }
32 
33 // destructor
34 ServerVolume::~ServerVolume()
35 {
36 	if (fConnectionProvider)
37 		fConnectionProvider->ReleaseReference();
38 	if (fServerInfo)
39 		fServerInfo->ReleaseReference();
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->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
116 status_t
117 ServerVolume::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
152 void
153 ServerVolume::Uninit()
154 {
155 	if (fConnectionProvider)
156 		fConnectionProvider->CloseServerConnection();
157 
158 	VirtualVolume::Uninit();
159 }
160 
161 // PrepareToUnmount
162 void
163 ServerVolume::PrepareToUnmount()
164 {
165 	VirtualVolume::PrepareToUnmount();
166 }
167 
168 // HandleEvent
169 void
170 ServerVolume::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
208 status_t
209 ServerVolume::Unmount()
210 {
211 	return B_OK;
212 }
213 
214 
215 // #pragma mark -
216 // #pragma mark ----- queries -----
217 
218 // OpenQuery
219 status_t
220 ServerVolume::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
267 void
268 ServerVolume::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
296 status_t
297 ServerVolume::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
361 status_t
362 ServerVolume::_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
394 ShareVolume*
395 ServerVolume::_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