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