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_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 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 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