2 * Copyright 2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Paweł Dziepak, pdziepak@quarnos.org
10 #include "NFS4Server.h"
12 #include <AutoDeleter.h>
14 #include "FileSystem.h"
17 #include "WorkQueue.h"
20 NFS4Server::NFS4Server(RPC::Server
* serv
)
23 fWaitCancel(create_sem(0, NULL
)),
31 mutex_init(&fClientIdLock
, NULL
);
32 mutex_init(&fFSLock
, NULL
);
33 mutex_init(&fThreadStartLock
, NULL
);
38 NFS4Server::~NFS4Server()
42 release_sem(fWaitCancel
);
44 wait_for_thread(fThread
, &result
);
46 delete_sem(fWaitCancel
);
47 mutex_destroy(&fClientIdLock
);
48 mutex_destroy(&fFSLock
);
49 mutex_destroy(&fThreadStartLock
);
54 NFS4Server::ServerRebooted(uint64 clientId
)
56 if (clientId
!= fClientId
)
59 fClientId
= ClientId(clientId
, true);
61 // reclaim all opened files and held locks from all filesystems
62 MutexLocker
_(fFSLock
);
63 FileSystem
* fs
= fFileSystems
.Head();
65 DoublyLinkedList
<OpenState
>::Iterator iterator
66 = fs
->OpenFilesLock().GetIterator();
67 OpenState
* current
= iterator
.Next();
68 while (current
!= NULL
) {
69 current
->Reclaim(fClientId
);
71 current
= iterator
.Next();
73 fs
->OpenFilesUnlock();
75 fs
= fFileSystems
.GetNext(fs
);
83 NFS4Server::AddFileSystem(FileSystem
* fs
)
87 MutexLocker
_(fFSLock
);
90 fUseCount
+= fs
->OpenFilesCount();
91 if (fs
->OpenFilesCount() > 0)
97 NFS4Server::RemoveFileSystem(FileSystem
* fs
)
101 MutexLocker
_(fFSLock
);
102 fFileSystems
.Remove(fs
);
103 fUseCount
-= fs
->OpenFilesCount();
108 NFS4Server::ClientId(uint64 prevId
, bool forceNew
)
110 MutexLocker
_(fClientIdLock
);
111 if ((fUseCount
== 0 && fClientIdLastUse
+ (time_t)LeaseTime() < time(NULL
))
112 || (forceNew
&& fClientId
== prevId
)) {
114 Request
request(fServer
, NULL
);
115 request
.Builder().SetClientID(fServer
);
117 status_t result
= request
.Send();
122 result
= request
.Reply().SetClientID(&fClientId
, &ver
);
127 request
.Builder().SetClientIDConfirm(fClientId
, ver
);
129 result
= request
.Send();
133 result
= request
.Reply().SetClientIDConfirm();
138 fClientIdLastUse
= time(NULL
);
144 NFS4Server::FileSystemMigrated()
146 // reclaim all opened files and held locks from all filesystems
147 MutexLocker
_(fFSLock
);
148 FileSystem
* fs
= fFileSystems
.Head();
150 fs
->Migrate(fServer
);
151 fs
= fFileSystems
.GetNext(fs
);
159 NFS4Server::_GetLeaseTime()
161 Request
request(fServer
, NULL
);
162 request
.Builder().PutRootFH();
163 Attribute attr
[] = { FATTR4_LEASE_TIME
};
164 request
.Builder().GetAttr(attr
, sizeof(attr
) / sizeof(Attribute
));
166 status_t result
= request
.Send();
170 ReplyInterpreter
& reply
= request
.Reply();
176 result
= reply
.GetAttr(&values
, &count
);
179 ArrayDeleter
<AttrValue
> valuesDeleter(values
);
181 // FATTR4_LEASE_TIME is mandatory
182 if (count
< 1 || values
[0].fAttribute
!= FATTR4_LEASE_TIME
)
185 fLeaseTime
= values
[0].fData
.fValue32
;
192 NFS4Server::_StartRenewing()
197 MutexLocker
_(fThreadStartLock
);
202 if (fLeaseTime
== 0) {
203 status_t result
= _GetLeaseTime();
208 fThreadCancel
= false;
209 fThread
= spawn_kernel_thread(&NFS4Server::_RenewalThreadStart
,
210 "NFSv4 Renewal", B_NORMAL_PRIORITY
, this);
214 status_t result
= resume_thread(fThread
);
215 if (result
!= B_OK
) {
216 kill_thread(fThread
);
225 NFS4Server::_Renewal()
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
) {
233 release_sem(fWaitCancel
);
237 uint64 clientId
= fClientId
;
239 if (fUseCount
== 0) {
240 MutexLocker
_(fFSLock
);
241 if (fUseCount
== 0) {
242 fThreadCancel
= true;
247 Request
request(fServer
, NULL
);
248 request
.Builder().Renew(clientId
);
249 result
= request
.Send();
253 switch (request
.Reply().NFS4Error()) {
254 case NFS4ERR_CB_PATH_DOWN
:
257 case NFS4ERR_STALE_CLIENTID
:
258 ServerRebooted(clientId
);
260 case NFS4ERR_LEASE_MOVED
:
261 FileSystemMigrated();
271 NFS4Server::_RenewalThreadStart(void* ptr
)
274 NFS4Server
* server
= reinterpret_cast<NFS4Server
*>(ptr
);
275 return server
->_Renewal();
280 NFS4Server::ProcessCallback(RPC::CallbackRequest
* request
,
281 Connection
* connection
)
283 ASSERT(request
!= NULL
);
284 ASSERT(connection
!= NULL
);
286 RequestInterpreter
req(request
);
287 ReplyBuilder
reply(request
->XID());
290 uint32 count
= req
.OperationCount();
292 for (uint32 i
= 0; i
< count
; i
++) {
293 switch (req
.Operation()) {
294 case OpCallbackGetAttr
:
295 result
= CallbackGetAttr(&req
, &reply
);
297 case OpCallbackRecall
:
298 result
= CallbackRecall(&req
, &reply
);
301 result
= B_NOT_SUPPORTED
;
308 XDR::WriteStream
& stream
= reply
.Reply()->Stream();
309 connection
->Send(stream
.Buffer(), stream
.Size());
316 NFS4Server::CallbackRecall(RequestInterpreter
* request
, ReplyBuilder
* reply
)
318 ASSERT(request
!= NULL
);
319 ASSERT(reply
!= NULL
);
326 status_t result
= request
->Recall(&handle
, truncate
, &stateSeq
, stateID
);
330 MutexLocker
locker(fFSLock
);
332 Delegation
* delegation
= NULL
;
333 FileSystem
* current
= fFileSystems
.Head();
334 while (current
!= NULL
) {
335 delegation
= current
->GetDelegation(handle
);
336 if (delegation
!= NULL
)
339 current
= fFileSystems
.GetNext(current
);
343 if (delegation
== NULL
) {
344 reply
->Recall(B_FILE_NOT_FOUND
);
345 return B_FILE_NOT_FOUND
;
348 DelegationRecallArgs
* args
= new(std::nothrow
) DelegationRecallArgs
;
349 args
->fDelegation
= delegation
;
350 args
->fTruncate
= truncate
;
351 gWorkQueue
->EnqueueJob(DelegationRecall
, args
);
360 NFS4Server::CallbackGetAttr(RequestInterpreter
* request
, ReplyBuilder
* reply
)
362 ASSERT(request
!= NULL
);
363 ASSERT(reply
!= NULL
);
368 status_t result
= request
->GetAttr(&handle
, &mask
);
372 MutexLocker
locker(fFSLock
);
374 Delegation
* delegation
= NULL
;
375 FileSystem
* current
= fFileSystems
.Head();
376 while (current
!= NULL
) {
377 delegation
= current
->GetDelegation(handle
);
378 if (delegation
!= NULL
)
381 current
= fFileSystems
.GetNext(current
);
385 if (delegation
== NULL
) {
386 reply
->GetAttr(B_FILE_NOT_FOUND
, 0, 0, 0);
387 return B_FILE_NOT_FOUND
;
391 delegation
->GetInode()->Stat(&st
);
394 change
= delegation
->GetInode()->Change();
395 if (delegation
->GetInode()->Dirty())
397 reply
->GetAttr(B_OK
, mask
, st
.st_size
, change
);
404 NFS4Server::RecallAll()
406 MutexLocker
_(fFSLock
);
407 FileSystem
* fs
= fFileSystems
.Head();
409 DoublyLinkedList
<Delegation
>& list
= fs
->DelegationsLock();
410 DoublyLinkedList
<Delegation
>::Iterator iterator
= list
.GetIterator();
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
);
419 current
= iterator
.Next();
421 fs
->DelegationsUnlock();
423 fs
= fFileSystems
.GetNext(fs
);