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