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 "Cookie.h" 11 #include "FileSystem.h" 12 #include "NFS4Object.h" 13 #include "OpenState.h" 14 #include "Request.h" 15 16 17 static inline bigtime_t 18 RetryDelay(uint32 attempt, uint32 leaseTime = 0) 19 { 20 attempt = min_c(attempt, 10); 21 22 bigtime_t delay = (bigtime_t(1) << (attempt - 1)) * 100000; 23 if (leaseTime != 0) 24 delay = min_c(delay, sSecToBigTime(leaseTime)); 25 return delay; 26 } 27 28 29 bool 30 NFS4Object::HandleErrors(uint32& attempt, uint32 nfs4Error, RPC::Server* server, 31 OpenStateCookie* cookie, OpenState* state, uint32* sequence) 32 { 33 // No request send by the client should cause any of the following errors. 34 ASSERT(nfs4Error != NFS4ERR_CLID_INUSE); 35 ASSERT(nfs4Error != NFS4ERR_BAD_STATEID); 36 ASSERT(nfs4Error != NFS4ERR_RESTOREFH); 37 ASSERT(nfs4Error != NFS4ERR_LOCKS_HELD); 38 ASSERT(nfs4Error != NFS4ERR_OP_ILLEGAL); 39 40 attempt++; 41 42 if (cookie != NULL) 43 state = cookie->fOpenState; 44 45 uint32 leaseTime; 46 status_t result; 47 switch (nfs4Error) { 48 case NFS4_OK: 49 return false; 50 51 // retransmission of CLOSE caused seqid to fall back 52 case NFS4ERR_BAD_SEQID: 53 if (attempt == 1) { 54 ASSERT(sequence != NULL); 55 (*sequence)++; 56 return true; 57 } 58 return false; 59 60 // resource is locked, we need to wait 61 case NFS4ERR_DENIED: 62 if (cookie == NULL) 63 return false; 64 65 if (sequence != NULL) 66 fFileSystem->OpenOwnerSequenceUnlock(*sequence); 67 68 result = acquire_sem_etc(cookie->fSnoozeCancel, 1, 69 B_RELATIVE_TIMEOUT, RetryDelay(attempt)); 70 71 if (sequence != NULL) 72 *sequence = fFileSystem->OpenOwnerSequenceLock(); 73 74 if (result != B_TIMED_OUT) { 75 if (result == B_OK) 76 release_sem(cookie->fSnoozeCancel); 77 return false; 78 } 79 return true; 80 81 // server needs more time, we need to wait 82 case NFS4ERR_LOCKED: 83 case NFS4ERR_DELAY: 84 if (sequence != NULL) 85 fFileSystem->OpenOwnerSequenceUnlock(*sequence); 86 87 if (cookie == NULL) { 88 snooze_etc(RetryDelay(attempt), B_SYSTEM_TIMEBASE, 89 B_RELATIVE_TIMEOUT); 90 91 92 if (sequence != NULL) 93 *sequence = fFileSystem->OpenOwnerSequenceLock(); 94 95 return true; 96 } 97 98 if ((cookie->fMode & O_NONBLOCK) == 0) { 99 result = acquire_sem_etc(cookie->fSnoozeCancel, 1, 100 B_RELATIVE_TIMEOUT, RetryDelay(attempt)); 101 102 if (sequence != NULL) 103 *sequence = fFileSystem->OpenOwnerSequenceLock(); 104 105 if (result != B_TIMED_OUT) { 106 if (result == B_OK) 107 release_sem(cookie->fSnoozeCancel); 108 return false; 109 } 110 return true; 111 } 112 113 if (sequence != NULL) 114 *sequence = fFileSystem->OpenOwnerSequenceLock(); 115 return false; 116 117 // server is in grace period, we need to wait 118 case NFS4ERR_GRACE: 119 leaseTime = fFileSystem->NFSServer()->LeaseTime(); 120 if (sequence != NULL) 121 fFileSystem->OpenOwnerSequenceUnlock(*sequence); 122 123 if (cookie == NULL) { 124 snooze_etc(RetryDelay(attempt, leaseTime), B_SYSTEM_TIMEBASE, 125 B_RELATIVE_TIMEOUT); 126 if (sequence != NULL) 127 *sequence = fFileSystem->OpenOwnerSequenceLock(); 128 return true; 129 } 130 131 if ((cookie->fMode & O_NONBLOCK) == 0) { 132 result = acquire_sem_etc(cookie->fSnoozeCancel, 1, 133 B_RELATIVE_TIMEOUT, RetryDelay(attempt, leaseTime)); 134 135 if (sequence != NULL) 136 *sequence = fFileSystem->OpenOwnerSequenceLock(); 137 138 if (result != B_TIMED_OUT) { 139 if (result == B_OK) 140 release_sem(cookie->fSnoozeCancel); 141 return false; 142 } 143 return true; 144 } 145 146 if (sequence != NULL) 147 *sequence = fFileSystem->OpenOwnerSequenceLock(); 148 return false; 149 150 // server has rebooted, reclaim share and try again 151 case NFS4ERR_STALE_CLIENTID: 152 case NFS4ERR_STALE_STATEID: 153 if (state != NULL) { 154 if (sequence != NULL) 155 fFileSystem->OpenOwnerSequenceUnlock(*sequence); 156 157 fFileSystem->NFSServer()->ServerRebooted(state->fClientID); 158 if (sequence != NULL) 159 *sequence = fFileSystem->OpenOwnerSequenceLock(); 160 161 return true; 162 } 163 return false; 164 165 // File Handle has expired, is invalid or the node has been deleted 166 case NFS4ERR_BADHANDLE: 167 case NFS4ERR_FHEXPIRED: 168 case NFS4ERR_STALE: 169 if (fInfo.UpdateFileHandles(fFileSystem) == B_OK) 170 return true; 171 return false; 172 173 // filesystem has been moved 174 case NFS4ERR_LEASE_MOVED: 175 case NFS4ERR_MOVED: 176 fFileSystem->Migrate(server); 177 return true; 178 179 // lease has expired 180 case NFS4ERR_EXPIRED: 181 if (state != NULL) { 182 fFileSystem->NFSServer()->ClientId(state->fClientID, true); 183 return true; 184 } 185 return false; 186 187 default: 188 return false; 189 } 190 } 191 192 193 status_t 194 NFS4Object::ConfirmOpen(const FileHandle& fh, OpenState* state, 195 uint32* sequence) 196 { 197 ASSERT(state != NULL); 198 ASSERT(sequence != NULL); 199 200 uint32 attempt = 0; 201 do { 202 RPC::Server* serv = fFileSystem->Server(); 203 Request request(serv, fFileSystem); 204 205 RequestBuilder& req = request.Builder(); 206 207 req.PutFH(fh); 208 req.OpenConfirm(*sequence, state->fStateID, state->fStateSeq); 209 210 status_t result = request.Send(); 211 if (result != B_OK) 212 return result; 213 214 ReplyInterpreter& reply = request.Reply(); 215 216 result = reply.PutFH(); 217 if (result == B_OK) 218 *sequence += IncrementSequence(reply.NFS4Error()); 219 220 if (HandleErrors(attempt, reply.NFS4Error(), serv, NULL, state)) 221 continue; 222 223 result = reply.OpenConfirm(&state->fStateSeq); 224 if (result != B_OK) 225 return result; 226 227 return B_OK; 228 } while (true); 229 } 230 231 232 uint32 233 NFS4Object::IncrementSequence(uint32 error) 234 { 235 if (error != NFS4ERR_STALE_CLIENTID && error != NFS4ERR_STALE_STATEID 236 && error != NFS4ERR_BAD_STATEID && error != NFS4ERR_BAD_SEQID 237 && error != NFS4ERR_BADXDR && error != NFS4ERR_RESOURCE 238 && error != NFS4ERR_NOFILEHANDLE) 239 return 1; 240 241 return 0; 242 } 243 244