xref: /haiku/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp (revision 002f37b0cca92e4cf72857c72ac95db5a8b09615)
1 /*
2  * Copyright 2012 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Paweł Dziepak, pdziepak@quarnos.org
7  */
8 
9 
10 #include "NFS4Server.h"
11 
12 #include <AutoDeleter.h>
13 
14 #include "FileSystem.h"
15 #include "Inode.h"
16 #include "Request.h"
17 #include "WorkQueue.h"
18 
19 
20 NFS4Server::NFS4Server(RPC::Server* serv)
21 	:
22 	fThreadCancel(true),
23 	fWaitCancel(create_sem(0, NULL)),
24 	fLeaseTime(0),
25 	fClientIdLastUse(0),
26 	fUseCount(0),
27 	fServer(serv)
28 {
29 	ASSERT(serv != NULL);
30 
31 	mutex_init(&fClientIdLock, NULL);
32 	mutex_init(&fFSLock, NULL);
33 	mutex_init(&fThreadStartLock, NULL);
34 
35 }
36 
37 
38 NFS4Server::~NFS4Server()
39 {
40 	fThreadCancel = true;
41 	fUseCount = 0;
42 	release_sem(fWaitCancel);
43 	status_t result;
44 	wait_for_thread(fThread, &result);
45 
46 	delete_sem(fWaitCancel);
47 	mutex_destroy(&fClientIdLock);
48 	mutex_destroy(&fFSLock);
49 	mutex_destroy(&fThreadStartLock);
50 }
51 
52 
53 uint64
54 NFS4Server::ServerRebooted(uint64 clientId)
55 {
56 	if (clientId != fClientId)
57 		return fClientId;
58 
59 	fClientId = ClientId(clientId, true);
60 
61 	// reclaim all opened files and held locks from all filesystems
62 	MutexLocker _(fFSLock);
63 	FileSystem* fs = fFileSystems.Head();
64 	while (fs != NULL) {
65 		DoublyLinkedList<OpenState>::Iterator iterator
66 			= fs->OpenFilesLock().GetIterator();
67 		OpenState* current = iterator.Next();
68 		while (current != NULL) {
69 			current->Reclaim(fClientId);
70 
71 			current = iterator.Next();
72 		}
73 		fs->OpenFilesUnlock();
74 
75 		fs = fFileSystems.GetNext(fs);
76 	}
77 
78 	return fClientId;
79 }
80 
81 
82 void
83 NFS4Server::AddFileSystem(FileSystem* fs)
84 {
85 	ASSERT(fs != NULL);
86 
87 	MutexLocker _(fFSLock);
88 	fFileSystems.Add(fs);
89 
90 	fUseCount += fs->OpenFilesCount();
91 	if (fs->OpenFilesCount() > 0)
92 		_StartRenewing();
93 }
94 
95 
96 void
97 NFS4Server::RemoveFileSystem(FileSystem* fs)
98 {
99 	ASSERT(fs != NULL);
100 
101 	MutexLocker _(fFSLock);
102 	fFileSystems.Remove(fs);
103 	fUseCount -= fs->OpenFilesCount();
104 }
105 
106 
107 uint64
108 NFS4Server::ClientId(uint64 prevId, bool forceNew)
109 {
110 	MutexLocker _(fClientIdLock);
111 	if ((fUseCount == 0 && fClientIdLastUse + (time_t)LeaseTime() < time(NULL))
112 		|| (forceNew && fClientId == prevId)) {
113 
114 		Request request(fServer, NULL);
115 		request.Builder().SetClientID(fServer);
116 
117 		status_t result = request.Send();
118 		if (result != B_OK)
119 			return fClientId;
120 
121 		uint64 ver;
122 		result = request.Reply().SetClientID(&fClientId, &ver);
123 		if (result != B_OK)
124 			return fClientId;
125 
126 		request.Reset();
127 		request.Builder().SetClientIDConfirm(fClientId, ver);
128 
129 		result = request.Send();
130 		if (result != B_OK)
131 			return fClientId;
132 
133 		result = request.Reply().SetClientIDConfirm();
134 		if (result != B_OK)
135 			return fClientId;
136 	}
137 
138 	fClientIdLastUse = time(NULL);
139 	return fClientId;
140 }
141 
142 
143 status_t
144 NFS4Server::FileSystemMigrated()
145 {
146 	// reclaim all opened files and held locks from all filesystems
147 	MutexLocker _(fFSLock);
148 	FileSystem* fs = fFileSystems.Head();
149 	while (fs != NULL) {
150 		fs->Migrate(fServer);
151 		fs = fFileSystems.GetNext(fs);
152 	}
153 
154 	return B_OK;
155 }
156 
157 
158 status_t
159 NFS4Server::_GetLeaseTime()
160 {
161 	Request request(fServer, NULL);
162 	request.Builder().PutRootFH();
163 	Attribute attr[] = { FATTR4_LEASE_TIME };
164 	request.Builder().GetAttr(attr, sizeof(attr) / sizeof(Attribute));
165 
166 	status_t result = request.Send();
167 	if (result != B_OK)
168 		return result;
169 
170 	ReplyInterpreter& reply = request.Reply();
171 
172 	reply.PutRootFH();
173 
174 	AttrValue* values;
175 	uint32 count;
176 	result = reply.GetAttr(&values, &count);
177 	if (result != B_OK)
178 		return result;
179 	ArrayDeleter<AttrValue> valuesDeleter(values);
180 
181 	// FATTR4_LEASE_TIME is mandatory
182 	if (count < 1 || values[0].fAttribute != FATTR4_LEASE_TIME)
183 		return B_BAD_VALUE;
184 
185 	fLeaseTime = values[0].fData.fValue32;
186 
187 	return B_OK;
188 }
189 
190 
191 status_t
192 NFS4Server::_StartRenewing()
193 {
194 	if (!fThreadCancel)
195 		return B_OK;
196 
197 	MutexLocker _(fThreadStartLock);
198 
199 	if (!fThreadCancel)
200 		return B_OK;
201 
202 	if (fLeaseTime == 0) {
203 		status_t result = _GetLeaseTime();
204 		if (result != B_OK)
205 			return result;
206 	}
207 
208 	fThreadCancel = false;
209 	fThread = spawn_kernel_thread(&NFS4Server::_RenewalThreadStart,
210 		"NFSv4 Renewal", B_NORMAL_PRIORITY, this);
211 	if (fThread < B_OK)
212 		return fThread;
213 
214 	status_t result = resume_thread(fThread);
215 	if (result != B_OK) {
216 		kill_thread(fThread);
217 		return result;
218 	}
219 
220 	return B_OK;
221 }
222 
223 
224 status_t
225 NFS4Server::_Renewal()
226 {
227 	while (!fThreadCancel) {
228 		// TODO: operations like OPEN, READ, CLOSE, etc also renew leases
229 		status_t result = acquire_sem_etc(fWaitCancel, 1,
230 			B_RELATIVE_TIMEOUT, sSecToBigTime(fLeaseTime - 2));
231 		if (result != B_TIMED_OUT) {
232 			if (result == B_OK)
233 				release_sem(fWaitCancel);
234 			return result;
235 		}
236 
237 		uint64 clientId = fClientId;
238 
239 		if (fUseCount == 0) {
240 			MutexLocker _(fFSLock);
241 			if (fUseCount == 0) {
242 				fThreadCancel = true;
243 				return B_OK;
244 			}
245 		}
246 
247 		Request request(fServer, NULL);
248 		request.Builder().Renew(clientId);
249 		result = request.Send();
250 		if (result != B_OK)
251 			continue;
252 
253 		switch (request.Reply().NFS4Error()) {
254 			case NFS4ERR_CB_PATH_DOWN:
255 				RecallAll();
256 				break;
257 			case NFS4ERR_STALE_CLIENTID:
258 				ServerRebooted(clientId);
259 				break;
260 			case NFS4ERR_LEASE_MOVED:
261 				FileSystemMigrated();
262 				break;
263 		}
264 	}
265 
266 	return B_OK;
267 }
268 
269 
270 status_t
271 NFS4Server::_RenewalThreadStart(void* ptr)
272 {
273 	ASSERT(ptr != NULL);
274 	NFS4Server* server = reinterpret_cast<NFS4Server*>(ptr);
275 	return server->_Renewal();
276 }
277 
278 
279 status_t
280 NFS4Server::ProcessCallback(RPC::CallbackRequest* request,
281 	Connection* connection)
282 {
283 	ASSERT(request != NULL);
284 	ASSERT(connection != NULL);
285 
286 	RequestInterpreter req(request);
287 	ReplyBuilder reply(request->XID());
288 
289 	status_t result;
290 	uint32 count = req.OperationCount();
291 
292 	for (uint32 i = 0; i < count; i++) {
293 		switch (req.Operation()) {
294 			case OpCallbackGetAttr:
295 				result = CallbackGetAttr(&req, &reply);
296 				break;
297 			case OpCallbackRecall:
298 				result = CallbackRecall(&req, &reply);
299 				break;
300 			default:
301 				result = B_NOT_SUPPORTED;
302 		}
303 
304 		if (result != B_OK)
305 			break;
306 	}
307 
308 	XDR::WriteStream& stream = reply.Reply()->Stream();
309 	connection->Send(stream.Buffer(), stream.Size());
310 
311 	return B_OK;
312 }
313 
314 
315 status_t
316 NFS4Server::CallbackRecall(RequestInterpreter* request, ReplyBuilder* reply)
317 {
318 	ASSERT(request != NULL);
319 	ASSERT(reply != NULL);
320 
321 	uint32 stateID[3];
322 	uint32 stateSeq;
323 	bool truncate;
324 	FileHandle handle;
325 
326 	status_t result = request->Recall(&handle, truncate, &stateSeq, stateID);
327 	if (result != B_OK)
328 		return result;
329 
330 	MutexLocker locker(fFSLock);
331 
332 	Delegation* delegation = NULL;
333 	FileSystem* current = fFileSystems.Head();
334 	while (current != NULL) {
335 		delegation = current->GetDelegation(handle);
336 		if (delegation != NULL)
337 			break;
338 
339 		current = fFileSystems.GetNext(current);
340 	}
341 	locker.Unlock();
342 
343 	if (delegation == NULL) {
344 		reply->Recall(B_FILE_NOT_FOUND);
345 		return B_FILE_NOT_FOUND;
346 	}
347 
348 	DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
349 	args->fDelegation = delegation;
350 	args->fTruncate = truncate;
351 	gWorkQueue->EnqueueJob(DelegationRecall, args);
352 
353 	reply->Recall(B_OK);
354 
355 	return B_OK;
356 }
357 
358 
359 status_t
360 NFS4Server::CallbackGetAttr(RequestInterpreter* request, ReplyBuilder* reply)
361 {
362 	ASSERT(request != NULL);
363 	ASSERT(reply != NULL);
364 
365 	FileHandle handle;
366 	int mask;
367 
368 	status_t result = request->GetAttr(&handle, &mask);
369 	if (result != B_OK)
370 		return result;
371 
372 	MutexLocker locker(fFSLock);
373 
374 	Delegation* delegation = NULL;
375 	FileSystem* current = fFileSystems.Head();
376 	while (current != NULL) {
377 		delegation = current->GetDelegation(handle);
378 		if (delegation != NULL)
379 			break;
380 
381 		current = fFileSystems.GetNext(current);
382 	}
383 	locker.Unlock();
384 
385 	if (delegation == NULL) {
386 		reply->GetAttr(B_FILE_NOT_FOUND, 0, 0, 0);
387 		return B_FILE_NOT_FOUND;
388 	}
389 
390 	struct stat st;
391 	delegation->GetInode()->Stat(&st);
392 
393 	uint64 change;
394 	change = delegation->GetInode()->Change();
395 	if (delegation->GetInode()->Dirty())
396 		change++;
397 	reply->GetAttr(B_OK, mask, st.st_size, change);
398 
399 	return B_OK;
400 }
401 
402 
403 status_t
404 NFS4Server::RecallAll()
405 {
406 	MutexLocker _(fFSLock);
407 	FileSystem* fs = fFileSystems.Head();
408 	while (fs != NULL) {
409 		DoublyLinkedList<Delegation>& list = fs->DelegationsLock();
410 		DoublyLinkedList<Delegation>::Iterator iterator = list.GetIterator();
411 
412 		Delegation* current = iterator.Next();
413 		while (current != NULL) {
414 			DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
415 			args->fDelegation = current;
416 			args->fTruncate = false;
417 			gWorkQueue->EnqueueJob(DelegationRecall, args);
418 
419 			current = iterator.Next();
420 		}
421 		fs->DelegationsUnlock();
422 
423 		fs = fFileSystems.GetNext(fs);
424 	}
425 
426 	return B_OK;
427 }
428 
429