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
NFS4Server(RPC::Server * serv)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
~NFS4Server()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
ServerRebooted(uint64 clientId)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
AddFileSystem(FileSystem * fs)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
RemoveFileSystem(FileSystem * fs)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
ClientId(uint64 prevId,bool forceNew)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
FileSystemMigrated()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
_GetLeaseTime()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
_StartRenewing()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
_Renewal()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
_RenewalThreadStart(void * ptr)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
ProcessCallback(RPC::CallbackRequest * request,Connection * connection)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
CallbackRecall(RequestInterpreter * request,ReplyBuilder * reply)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_ENTRY_NOT_FOUND);
345 return B_ENTRY_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
CallbackGetAttr(RequestInterpreter * request,ReplyBuilder * reply)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_ENTRY_NOT_FOUND, 0, 0, 0);
387 return B_ENTRY_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
RecallAll()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