BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / file_systems / nfs4 / NFS4Server.cpp
blob726cbad3546bc737f37d14234822a630f6b1725e
1 /*
2 * Copyright 2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Paweł Dziepak, pdziepak@quarnos.org
7 */
10 #include "NFS4Server.h"
12 #include <AutoDeleter.h>
14 #include "FileSystem.h"
15 #include "Inode.h"
16 #include "Request.h"
17 #include "WorkQueue.h"
20 NFS4Server::NFS4Server(RPC::Server* serv)
22 fThreadCancel(true),
23 fWaitCancel(create_sem(0, NULL)),
24 fLeaseTime(0),
25 fClientIdLastUse(0),
26 fUseCount(0),
27 fServer(serv)
29 ASSERT(serv != NULL);
31 mutex_init(&fClientIdLock, NULL);
32 mutex_init(&fFSLock, NULL);
33 mutex_init(&fThreadStartLock, NULL);
38 NFS4Server::~NFS4Server()
40 fThreadCancel = true;
41 fUseCount = 0;
42 release_sem(fWaitCancel);
43 status_t result;
44 wait_for_thread(fThread, &result);
46 delete_sem(fWaitCancel);
47 mutex_destroy(&fClientIdLock);
48 mutex_destroy(&fFSLock);
49 mutex_destroy(&fThreadStartLock);
53 uint64
54 NFS4Server::ServerRebooted(uint64 clientId)
56 if (clientId != fClientId)
57 return 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();
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);
71 current = iterator.Next();
73 fs->OpenFilesUnlock();
75 fs = fFileSystems.GetNext(fs);
78 return fClientId;
82 void
83 NFS4Server::AddFileSystem(FileSystem* fs)
85 ASSERT(fs != NULL);
87 MutexLocker _(fFSLock);
88 fFileSystems.Add(fs);
90 fUseCount += fs->OpenFilesCount();
91 if (fs->OpenFilesCount() > 0)
92 _StartRenewing();
96 void
97 NFS4Server::RemoveFileSystem(FileSystem* fs)
99 ASSERT(fs != NULL);
101 MutexLocker _(fFSLock);
102 fFileSystems.Remove(fs);
103 fUseCount -= fs->OpenFilesCount();
107 uint64
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();
118 if (result != B_OK)
119 return fClientId;
121 uint64 ver;
122 result = request.Reply().SetClientID(&fClientId, &ver);
123 if (result != B_OK)
124 return fClientId;
126 request.Reset();
127 request.Builder().SetClientIDConfirm(fClientId, ver);
129 result = request.Send();
130 if (result != B_OK)
131 return fClientId;
133 result = request.Reply().SetClientIDConfirm();
134 if (result != B_OK)
135 return fClientId;
138 fClientIdLastUse = time(NULL);
139 return fClientId;
143 status_t
144 NFS4Server::FileSystemMigrated()
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);
154 return B_OK;
158 status_t
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();
167 if (result != B_OK)
168 return result;
170 ReplyInterpreter& reply = request.Reply();
172 reply.PutRootFH();
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);
181 // FATTR4_LEASE_TIME is mandatory
182 if (count < 1 || values[0].fAttribute != FATTR4_LEASE_TIME)
183 return B_BAD_VALUE;
185 fLeaseTime = values[0].fData.fValue32;
187 return B_OK;
191 status_t
192 NFS4Server::_StartRenewing()
194 if (!fThreadCancel)
195 return B_OK;
197 MutexLocker _(fThreadStartLock);
199 if (!fThreadCancel)
200 return B_OK;
202 if (fLeaseTime == 0) {
203 status_t result = _GetLeaseTime();
204 if (result != B_OK)
205 return result;
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;
214 status_t result = resume_thread(fThread);
215 if (result != B_OK) {
216 kill_thread(fThread);
217 return result;
220 return B_OK;
224 status_t
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) {
232 if (result == B_OK)
233 release_sem(fWaitCancel);
234 return result;
237 uint64 clientId = fClientId;
239 if (fUseCount == 0) {
240 MutexLocker _(fFSLock);
241 if (fUseCount == 0) {
242 fThreadCancel = true;
243 return B_OK;
247 Request request(fServer, NULL);
248 request.Builder().Renew(clientId);
249 result = request.Send();
250 if (result != B_OK)
251 continue;
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;
266 return B_OK;
270 status_t
271 NFS4Server::_RenewalThreadStart(void* ptr)
273 ASSERT(ptr != NULL);
274 NFS4Server* server = reinterpret_cast<NFS4Server*>(ptr);
275 return server->_Renewal();
279 status_t
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());
289 status_t result;
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);
296 break;
297 case OpCallbackRecall:
298 result = CallbackRecall(&req, &reply);
299 break;
300 default:
301 result = B_NOT_SUPPORTED;
304 if (result != B_OK)
305 break;
308 XDR::WriteStream& stream = reply.Reply()->Stream();
309 connection->Send(stream.Buffer(), stream.Size());
311 return B_OK;
315 status_t
316 NFS4Server::CallbackRecall(RequestInterpreter* request, ReplyBuilder* reply)
318 ASSERT(request != NULL);
319 ASSERT(reply != NULL);
321 uint32 stateID[3];
322 uint32 stateSeq;
323 bool truncate;
324 FileHandle handle;
326 status_t result = request->Recall(&handle, truncate, &stateSeq, stateID);
327 if (result != B_OK)
328 return result;
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)
337 break;
339 current = fFileSystems.GetNext(current);
341 locker.Unlock();
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);
353 reply->Recall(B_OK);
355 return B_OK;
359 status_t
360 NFS4Server::CallbackGetAttr(RequestInterpreter* request, ReplyBuilder* reply)
362 ASSERT(request != NULL);
363 ASSERT(reply != NULL);
365 FileHandle handle;
366 int mask;
368 status_t result = request->GetAttr(&handle, &mask);
369 if (result != B_OK)
370 return result;
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)
379 break;
381 current = fFileSystems.GetNext(current);
383 locker.Unlock();
385 if (delegation == NULL) {
386 reply->GetAttr(B_FILE_NOT_FOUND, 0, 0, 0);
387 return B_FILE_NOT_FOUND;
390 struct stat st;
391 delegation->GetInode()->Stat(&st);
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);
399 return B_OK;
403 status_t
404 NFS4Server::RecallAll()
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();
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);
426 return B_OK;