nfs4: Improve delegation handling

* Use the delegation stateid instead of the open stateid when sending
  a read, write, or write stat request.
* Ensure that the uid and gid of a request reflect the user who opened
  the file in question.
* Add measures to avoid deadlocks when a delegation is recalled.
* Fix a race condition in which OpenState can be deleted before an IO
  job is done with it.
* Possible fix for #19694.

The NFS 4.0 RFC specifies that the if a delegation is held, the
delegation stateid should be used for IO requests, and for SETATTR
(write stat) requests that set file size.

Change-Id: I9604ef58e3232f64d1e02ab180c603220e967e1d
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9587
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Jim906
2025-09-18 19:06:20 +00:00
committed by waddlesplash
parent 771bccb4b9
commit dc64aa7e6d
15 changed files with 386 additions and 49 deletions
@@ -147,7 +147,9 @@ Cookie::CancelAll()
OpenStateCookie::OpenStateCookie(FileSystem* fileSystem)
:
Cookie(fileSystem)
Cookie(fileSystem),
fUid(geteuid()),
fGid(getegid())
{
}
@@ -78,6 +78,9 @@ struct OpenStateCookie : public Cookie {
OpenState* fOpenState;
uint32 fMode;
uid_t fUid;
gid_t fGid;
OpenStateCookie(FileSystem* fileSystem);
};
@@ -20,22 +20,55 @@ Delegation::Delegation(const OpenDelegationData& data, Inode* inode,
fData(data),
fInode(inode),
fAttribute(attribute),
fStateSeq(data.fStateSeq),
fUid(geteuid()),
fGid(getegid())
fGid(getegid()),
fRecalled(false)
{
ASSERT(inode != NULL);
memcpy(fStateID, data.fStateID, sizeof(fStateID));
}
status_t
Delegation::GiveUp(bool truncate)
{
PrepareGiveUp(truncate);
return DoGiveUp(truncate);
}
status_t
Delegation::PrepareGiveUp(bool truncate)
{
status_t status = B_OK;
if (!fAttribute && !truncate)
fInode->SyncAndCommit(true);
status = fInode->Sync(true, false);
ReturnDelegation();
return status;
}
return B_OK;
status_t
Delegation::DoGiveUp(bool truncate, bool wait)
{
if (!fAttribute && !truncate && wait)
fInode->WaitAIOComplete();
status_t status = ReturnDelegation();
fInode->Commit(fUid, fGid);
return status;
}
void
Delegation::GetStateIDandSeq(uint32* stateID, uint32& stateSeq) const
{
memcpy(stateID, fStateID, sizeof(uint32) * 3);
stateSeq = fStateSeq;
}
@@ -24,10 +24,15 @@ public:
uint64 clientID, bool attr = false);
status_t GiveUp(bool truncate = false);
status_t PrepareGiveUp(bool truncate);
status_t DoGiveUp(bool truncate, bool wait = true);
inline void SetData(const OpenDelegationData& data);
inline Inode* GetInode() const;
void GetStateIDandSeq(uint32* stateID, uint32& stateSeq) const;
inline OpenDelegation Type();
inline void MarkRecalled();
inline bool RecallInitiated() const;
void Dump(void (*xprintf)(const char*, ...) = dprintf) const;
@@ -39,8 +44,11 @@ private:
OpenDelegationData fData;
Inode* fInode;
bool fAttribute;
uint32 fStateID[3];
uint32 fStateSeq;
uid_t fUid;
gid_t fGid;
bool fRecalled;
};
@@ -65,5 +73,19 @@ Delegation::Type()
}
inline void
Delegation::MarkRecalled()
{
fRecalled = true;
}
inline bool
Delegation::RecallInitiated() const
{
return fRecalled;
}
#endif // DELEGATION_H
+148 -13
View File
@@ -34,6 +34,7 @@ Inode::Inode()
fWriteDirty(false),
fAIOWait(create_sem(1, NULL)),
fAIOCount(0),
fOpenStateReleasesPending(0),
fStale(false)
{
rw_lock_init(&fDelegationLock, "nfs4 Inode::fDelegationLock");
@@ -704,12 +705,15 @@ Inode::WriteStat(const struct stat* st, uint32 mask, OpenAttrCookie* cookie)
i++;
}
ReadLocker delegationLocker(fDelegationLock);
if (cookie == NULL) {
MutexLocker stateLocker(fStateLock);
ASSERT(fOpenState != NULL || !(mask & B_STAT_SIZE));
result = NFS4Inode::WriteStat(fOpenState, attr, i);
} else
result = NFS4Inode::WriteStat(cookie->fOpenState, attr, i);
result = NFS4Inode::WriteStat(fOpenState, fDelegation, attr, i);
} else {
ASSERT(cookie->fOpenState->fDelegation == fDelegation);
result = NFS4Inode::WriteStat(cookie->fOpenState, NULL, attr, i);
}
fMetaCache.InvalidateStat();
@@ -974,6 +978,52 @@ Inode::RecallDelegation(bool truncate)
}
/*! Flush write data to the server if needed before returning the delegation.
@post If data needs to be flushed, an IO job is enqueued but may not be complete.
*/
void
Inode::PrepareDelegationRecall(bool truncate)
{
rw_lock_write_lock(&fDelegationLock);
fDelegation->MarkRecalled();
rw_lock_write_unlock(&fDelegationLock);
ReadLocker _(fDelegationLock);
if (fDelegation == NULL)
return;
fDelegation->PrepareGiveUp(truncate);
return;
}
/*! Return the delegation after data has been flushed to the server.
@pre RecallDelegationAsyncPrep has been called.
*/
void
Inode::RecallDelegationAsync(bool truncate)
{
WriteLocker _(fDelegationLock);
if (fDelegation == NULL)
return;
fDelegation->DoGiveUp(truncate, false);
fMetaCache.UnlockValid();
fFileSystem->RemoveDelegation(fDelegation);
MutexLocker stateLocker(fStateLock);
fOpenState->fDelegation = NULL;
ReleaseOpenState();
delete fDelegation;
fDelegation = NULL;
return;
}
void
Inode::RecallReadDelegation()
{
@@ -1003,27 +1053,90 @@ Inode::ReturnDelegation(bool truncate)
}
/*! Temporarily unlock the locks that need to be acquired by the WorkQueue when a delegation
is recalled.
@pre fStateLock is locked and fDelegation lock is read-locked.
*/
void
Inode::UnlockAndRelockStateLocks()
{
rw_lock_read_unlock(&fDelegationLock);
mutex_unlock(&fStateLock);
rw_lock_read_lock(&fDelegationLock);
mutex_lock(&fStateLock);
}
/*! Temporarily unlock fWriteLock. Useful for allowing an IO job to complete.
@pre fWriteLock is write-locked and fDelegationLock is read-locked.
*/
void
Inode::UnlockAndRelockWriteLock()
{
rw_lock_write_unlock(&fWriteLock);
rw_lock_write_lock(&fWriteLock);
}
/*! Release a reference to fOpenState or set up later release if IO is not complete.
@pre fStateLock is locked.
*/
void
Inode::ReleaseOpenState()
{
ASSERT(fOpenState != NULL);
if (fOpenState->ReleaseReference() == 1) {
ASSERT(fAIOCount == 0);
fOpenState = NULL;
// If IO is pending, wait until it is finished to release the (possibly last) reference.
// It would be simpler to call WaitAIOComplete here, but that won't work if this
// is the WorkQueue thread.
MutexLocker _(fAIOLock);
if (fAIOCount > 0) {
++fOpenStateReleasesPending;
} else {
if (fOpenState->ReleaseReference() == 1)
fOpenState = NULL;
}
return;
}
/*! Sync file cache data to server.
@param wait If true, the function returns only after the sync is finished.
*/
status_t
Inode::Sync(bool force, bool wait)
{
ReadLocker locker(fDelegationLock);
if (!force && fDelegation != NULL && fDelegation->Type() == OPEN_DELEGATE_WRITE
&& !fDelegation->RecallInitiated()) {
locker.Unlock();
if (wait == true) {
// Wait for any IO jobs that may already be enqueued.
WaitAIOComplete();
}
return B_OK;
}
locker.Unlock();
status_t status = file_cache_sync(fFileCache);
if (wait == true)
WaitAIOComplete();
return status;
}
status_t
Inode::SyncAndCommit(bool force)
Inode::SyncAndCommit(bool force, OpenStateCookie* cookie)
{
if (!force && fDelegation != NULL)
return B_OK;
Sync(force, true);
file_cache_sync(fFileCache);
WaitAIOComplete();
return Commit();
// The server is liable to deny a commit request that does not come from a user who
// opened the file.
uid_t uid = cookie != NULL ? cookie->fUid : geteuid();
gid_t gid = cookie != NULL ? cookie->fGid : getegid();
return Commit(uid, gid);
}
@@ -1040,11 +1153,33 @@ Inode::BeginAIOOp()
void
Inode::EndAIOOp()
{
MutexLocker _(fAIOLock);
MutexLocker AIOLocker(fAIOLock);
ASSERT(fAIOCount > 0);
fAIOCount--;
if (fAIOCount == 0)
release_sem(fAIOWait);
if (fOpenStateReleasesPending > 0) {
MutexLocker stateLocker(fStateLock);
--fOpenStateReleasesPending;
if (fOpenState->ReleaseReference() == 1) {
ASSERT(fAIOCount == 0);
ASSERT(fOpenStateReleasesPending == 0);
fOpenState = NULL;
}
}
}
bool
Inode::AIOIncomplete()
{
MutexLocker _(fAIOLock);
if (fAIOCount > 0)
return true;
return false;
}
+17 -2
View File
@@ -40,16 +40,22 @@ public:
inline OpenState* GetOpenState();
inline Delegation* GetDelegation() const;
void SetDelegation(Delegation* delegation);
void RecallDelegation(bool truncate = false);
void PrepareDelegationRecall(bool truncate = false);
void RecallDelegationAsync(bool truncate = false);
void RecallReadDelegation();
void UnlockAndRelockStateLocks();
void UnlockAndRelockWriteLock();
status_t LookUp(const char* name, ino_t* id);
status_t Access(int mode);
status_t Commit();
status_t SyncAndCommit(bool force = false);
status_t Sync(bool force = false, bool wait = true);
status_t Commit(uid_t uid, gid_t gid);
status_t SyncAndCommit(bool force = false, OpenStateCookie* cookie = NULL);
status_t CreateObject(const char* name, const char* path,
int mode, FileType type, ino_t* id);
@@ -117,6 +123,7 @@ public:
void BeginAIOOp();
void EndAIOOp();
bool AIOIncomplete();
inline void WaitAIOComplete();
inline void SetStale(bool stale = true);
@@ -177,6 +184,7 @@ private:
sem_id fAIOWait;
uint32 fAIOCount;
mutex fAIOLock;
uint32 fOpenStateReleasesPending;
bool fStale;
};
@@ -266,6 +274,13 @@ Inode::GetOpenState()
}
inline Delegation*
Inode::GetDelegation() const
{
return fDelegation;
}
inline void
Inode::SetStale(bool stale)
{
@@ -115,6 +115,7 @@ Inode::Open(int mode, OpenFileCookie* cookie)
state->fInfo = fInfo;
state->fFileSystem = fFileSystem;
state->fMode = mode & O_RWMASK;
state->fDelegation = fDelegation;
status_t result = OpenFile(state, mode, &data);
if (result != B_OK) {
delete state;
@@ -125,8 +126,8 @@ Inode::Open(int mode, OpenFileCookie* cookie)
fOpenState = state;
cookie->fOpenState = state;
locker.Unlock();
RevalidateFileCache();
if (fDelegation == NULL)
RevalidateFileCache();
} else {
fOpenState->AcquireReference();
cookie->fOpenState = fOpenState;
@@ -138,7 +139,7 @@ Inode::Open(int mode, OpenFileCookie* cookie)
if (oldMode == O_RDONLY)
RecallReadDelegation();
status_t result = OpenFile(fOpenState, O_RDWR, &data);
status_t result = OpenFile(fOpenState, newMode, &data);
if (result != B_OK) {
locker.Lock();
ReleaseOpenState();
@@ -197,7 +198,7 @@ Inode::Close(OpenFileCookie* cookie)
int mode = cookie->fMode & O_RWMASK;
if (mode == O_RDWR || mode == O_WRONLY)
SyncAndCommit();
SyncAndCommit(false, cookie);
MutexLocker _(fStateLock);
ReleaseOpenState();
@@ -323,6 +324,8 @@ Inode::ReadDirect(OpenStateCookie* cookie, off_t pos, void* buffer,
status_t result;
OpenState* state = cookie != NULL ? cookie->fOpenState : fOpenState;
ReadLocker delegationLocker(fDelegationLock);
ASSERT(state->fDelegation == fDelegation);
while (size < *_length && !*eof) {
uint32 len = *_length - size;
result = ReadFile(cookie, state, pos + size, &len,
@@ -363,7 +366,7 @@ status_t
Inode::WriteDirect(OpenStateCookie* cookie, off_t pos, const void* _buffer,
size_t* _length)
{
ASSERT(cookie != NULL || fOpenState != NULL);
ASSERT_WITH_DUMP(cookie != NULL || fOpenState != NULL, this);
ASSERT(_buffer != NULL);
ASSERT(_length != NULL);
@@ -385,6 +388,8 @@ Inode::WriteDirect(OpenStateCookie* cookie, off_t pos, const void* _buffer,
fWriteDirty = true;
}
ReadLocker delegationLocker(fDelegationLock);
ASSERT(state->fDelegation == fDelegation);
while (size < *_length) {
uint32 len = *_length - size;
status_t result = WriteFile(cookie, state, pos + size, &len,
@@ -436,7 +441,8 @@ Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
// We will let the server zero out the rest of the hole.
fMaxFileSize = pos;
size_t pageOffset = pos % B_PAGE_SIZE;
file_cache_write(fFileCache, cookie, pos - pageOffset, NULL, &pageOffset);
if (pageOffset != 0)
file_cache_write(fFileCache, cookie, pos - pageOffset, NULL, &pageOffset);
}
fMaxFileSize = fileSize;
fMetaCache.GrowFile(fMaxFileSize);
@@ -444,7 +450,7 @@ Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
if ((cookie->fMode & O_NOCACHE) != 0) {
WriteDirect(cookie, pos, _buffer, _length);
Commit();
Commit(cookie->fUid, cookie->fGid);
}
return file_cache_write(fFileCache, cookie, pos, _buffer, _length);
@@ -452,13 +458,13 @@ Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
status_t
Inode::Commit()
Inode::Commit(uid_t uid, gid_t gid)
{
if (!fWriteDirty)
return B_OK;
WriteLocker _(fWriteLock);
status_t result = CommitWrites();
status_t result = CommitWrites(fDelegation != NULL, uid, gid);
if (result != B_OK)
return result;
fWriteDirty = false;
@@ -61,20 +61,38 @@ NFS4Inode::GetChangeInfo(uint64* change, bool attrDir)
status_t
NFS4Inode::CommitWrites()
NFS4Inode::CommitWrites(bool unlockBetweenAttempts, uid_t uid, gid_t gid)
{
uint32 attempt = 0;
uint32 retryLimit = 0;
bool hard = true;
if (fFileSystem != NULL) {
retryLimit = fFileSystem->GetConfiguration().fRetryLimit;
hard = fFileSystem->GetConfiguration().fHard;
}
do {
RPC::Server* serv = fFileSystem->Server();
Request request(serv, fFileSystem, geteuid(), getegid());
Request request(serv, fFileSystem, uid, gid, unlockBetweenAttempts);
RequestBuilder& req = request.Builder();
req.PutFH(fInfo.fHandle);
req.Commit(0, 0);
status_t result = request.Send();
if (result != B_OK)
return result;
if (result != B_OK) {
if (unlockBetweenAttempts && (hard || attempt < retryLimit)) {
// The server won't reply to a commit request while a delegation recall is in
// progress. To avoid a deadlock, we respond to a failure to reply by releasing
// fWriteLock, allowing the CallbackServer thread to proceed with CallbackRecall()
// if it is blocked, and trying the commit request again.
static_cast<Inode*>(this)->UnlockAndRelockWriteLock();
++attempt;
continue;
} else {
return result;
}
}
ReplyInterpreter& reply = request.Reply();
@@ -318,22 +336,40 @@ NFS4Inode::GetStat(AttrValue** values, uint32* count, OpenAttrCookie* cookie)
status_t
NFS4Inode::WriteStat(OpenState* state, AttrValue* attrs, uint32 attrCount)
NFS4Inode::WriteStat(OpenState* state, Delegation* delegation, AttrValue* attrs, uint32 attrCount)
{
ASSERT(attrs != NULL);
uint32 attempt = 0;
Inode* inode = delegation != NULL ? delegation->GetInode() : NULL;
bool useDelegationStateID = true;
do {
RPC::Server* serv = fFileSystem->Server();
Request request(serv, fFileSystem, geteuid(), getegid());
RequestBuilder& req = request.Builder();
uint32 stateID[3];
memset(stateID, 0, sizeof(stateID));
uint32 stateSeq = 0;
if (state != NULL) {
if (state->fDelegation != NULL && useDelegationStateID) {
delegation = state->fDelegation;
delegation->GetStateIDandSeq(stateID, stateSeq);
} else {
memcpy(stateID, state->fStateID, sizeof(stateID));
stateSeq = state->fStateSeq;
}
req.PutFH(state->fInfo.fHandle);
req.SetAttr(state->fStateID, state->fStateSeq, attrs, attrCount);
req.SetAttr(stateID, stateSeq, attrs, attrCount);
} else {
if (delegation != NULL && useDelegationStateID) {
// We might have a delegation regardless of whether the file is open.
delegation->GetStateIDandSeq(stateID, stateSeq);
}
req.PutFH(fInfo.fHandle);
req.SetAttr(NULL, 0, attrs, attrCount);
req.SetAttr(stateID, stateSeq, attrs, attrCount);
}
status_t result = request.Send();
@@ -342,8 +378,19 @@ NFS4Inode::WriteStat(OpenState* state, AttrValue* attrs, uint32 attrCount)
ReplyInterpreter& reply = request.Reply();
if (HandleErrors(attempt, reply.NFS4Error(), serv))
if (HandleErrors(attempt, reply.NFS4Error(), serv, NULL, state)) {
if (reply.NFS4Error() == NFS4ERR_DELAY && delegation != NULL) {
delegation->GetInode()->UnlockAndRelockStateLocks();
if (inode != NULL) {
// The OpenState and Delegation pointers might be invalid now.
state = inode->GetOpenState();
delegation = inode->GetDelegation();
}
} else if (reply.NFS4Error() == NFS4ERR_OPENMODE && state->fDelegation != NULL) {
useDelegationStateID = false;
}
continue;
}
reply.PutFH();
result = reply.SetAttr();
@@ -726,13 +773,24 @@ NFS4Inode::ReadFile(OpenStateCookie* cookie, OpenState* state, uint64 position,
ASSERT(eof != NULL);
uint32 attempt = 0;
bool useDelegationStateID = true;
do {
RPC::Server* serv = fFileSystem->Server();
Request request(serv, fFileSystem, geteuid(), getegid());
RequestBuilder& req = request.Builder();
uint32 stateID[3];
memset(stateID, 0, sizeof(stateID));
uint32 stateSeq = 0;
if (state->fDelegation != NULL && useDelegationStateID) {
state->fDelegation->GetStateIDandSeq(stateID, stateSeq);
} else {
memcpy(stateID, state->fStateID, sizeof(stateID));
stateSeq = state->fStateSeq;
}
req.PutFH(state->fInfo.fHandle);
req.Read(state->fStateID, state->fStateSeq, position, *length);
req.Read(stateID, stateSeq, position, *length);
status_t result = request.Send(cookie);
if (result != B_OK)
@@ -740,8 +798,13 @@ NFS4Inode::ReadFile(OpenStateCookie* cookie, OpenState* state, uint64 position,
ReplyInterpreter& reply = request.Reply();
if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, state))
if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, state)) {
if (reply.NFS4Error() == NFS4ERR_DELAY && state->fDelegation != NULL)
state->fDelegation->GetInode()->UnlockAndRelockStateLocks();
else if (reply.NFS4Error() == NFS4ERR_OPENMODE && state->fDelegation != NULL)
useDelegationStateID = false;
continue;
}
reply.PutFH();
result = reply.Read(buffer, length, eof);
@@ -762,14 +825,25 @@ NFS4Inode::WriteFile(OpenStateCookie* cookie, OpenState* state, uint64 position,
ASSERT(buffer != NULL);
uint32 attempt = 0;
bool useDelegationStateID = true;
do {
RPC::Server* serv = fFileSystem->Server();
Request request(serv, fFileSystem, state->fUid, state->fGid);
RequestBuilder& req = request.Builder();
uint32 stateID[3];
memset(stateID, 0, sizeof(stateID));
uint32 stateSeq = 0;
if (state->fDelegation != NULL && useDelegationStateID) {
state->fDelegation->GetStateIDandSeq(stateID, stateSeq);
} else {
memcpy(stateID, state->fStateID, sizeof(stateID));
stateSeq = state->fStateSeq;
}
req.PutFH(state->fInfo.fHandle);
req.Write(state->fStateID, state->fStateSeq, buffer, position, *length,
req.Write(stateID, stateSeq, buffer, position, *length,
commit);
status_t result = request.Send(cookie);
@@ -778,8 +852,13 @@ NFS4Inode::WriteFile(OpenStateCookie* cookie, OpenState* state, uint64 position,
ReplyInterpreter& reply = request.Reply();
if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, state))
if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, state)) {
if (reply.NFS4Error() == NFS4ERR_DELAY && state->fDelegation != NULL)
state->fDelegation->GetInode()->UnlockAndRelockStateLocks();
else if (reply.NFS4Error() == NFS4ERR_OPENMODE && state->fDelegation != NULL)
useDelegationStateID = false;
continue;
}
reply.PutFH();
@@ -28,7 +28,7 @@ public:
protected:
status_t Access(uint32* allowed);
status_t CommitWrites();
status_t CommitWrites(bool unlockBetweenAttempts, uid_t uid, gid_t gid);
status_t LookUp(const char* name, uint64* change, uint64* fileID,
FileHandle* handle, bool parent = false);
@@ -43,8 +43,8 @@ protected:
status_t GetStat(AttrValue** values, uint32* count,
OpenAttrCookie* attr = NULL);
status_t WriteStat(OpenState* state, AttrValue* attrs,
uint32 attrCount);
status_t WriteStat(OpenState* state, Delegation* delegation,
AttrValue* attrs, uint32 attrCount);
status_t CreateFile(const char* name, int mode, int perms,
OpenState* state, ChangeInfo* changeInfo,
@@ -184,6 +184,12 @@ NFS4Object::HandleErrors(uint32& attempt, uint32 nfs4Error, RPC::Server* server,
}
return false;
// delegation might have just been recalled, or is about to be recalled
case NFS4ERR_OPENMODE:
if (state->fDelegation != NULL)
return true;
return false;
default:
return false;
}
@@ -348,6 +348,11 @@ NFS4Server::CallbackRecall(RequestInterpreter* request, ReplyBuilder* reply)
DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
args->fDelegation = delegation;
args->fTruncate = truncate;
// If an IORequest job is needed, we should enqueue it before enqueueing
// the DelegationRecall job.
delegation->GetInode()->PrepareDelegationRecall(truncate);
gWorkQueue->EnqueueJob(DelegationRecall, args);
reply->Recall(B_OK);
@@ -48,6 +48,11 @@ Request::_SendUDP(Cookie* cookie)
hard = fFileSystem->GetConfiguration().fHard;
}
if (fSingleAttempt) {
requestTimeout /= 10;
retryLimit = 0;
}
result = fServer->WaitCall(rpc, requestTimeout);
if (result != B_OK) {
int attempts = 1;
@@ -107,6 +112,11 @@ Request::_SendTCP(Cookie* cookie)
hard = fFileSystem->GetConfiguration().fHard;
}
if (fSingleAttempt) {
requestTimeout /= 10;
retryLimit = 0;
}
do {
result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc);
if (result == B_NO_MEMORY)
@@ -21,7 +21,7 @@ class Request {
public:
inline Request(RPC::Server* server,
FileSystem* fileSystem,
uid_t uid, gid_t gid);
uid_t uid, gid_t gid, bool singleAttempt = false);
inline RequestBuilder& Builder();
inline ReplyInterpreter& Reply();
@@ -38,15 +38,19 @@ private:
RequestBuilder fBuilder;
ReplyInterpreter fReply;
bool fSingleAttempt;
};
inline
Request::Request(RPC::Server* server, FileSystem* fileSystem, uid_t uid, gid_t gid)
Request::Request(RPC::Server* server, FileSystem* fileSystem, uid_t uid, gid_t gid,
bool singleAttempt)
:
fServer(server),
fFileSystem(fileSystem),
fBuilder(uid, gid)
fBuilder(uid, gid),
fSingleAttempt(singleAttempt)
{
ASSERT(server != NULL);
}
@@ -186,7 +186,24 @@ void
WorkQueue::JobRecall(DelegationRecallArgs* args)
{
ASSERT(args != NULL);
args->fDelegation->GetInode()->RecallDelegation(args->fTruncate);
Inode* inode = args->fDelegation->GetInode();
if (inode->AIOIncomplete()) {
// Re-queue and try again later.
WorkQueueEntry* entry = new(std::nothrow) WorkQueueEntry;
if (entry == NULL)
return;
entry->fType = DelegationRecall;
entry->fArguments = args;
// The queue is already locked.
fQueue.InsertAfter(fQueue.Tail(), entry);
} else {
args->fDelegation->GetInode()->RecallDelegationAsync(args->fTruncate);
}
return;
}
@@ -349,7 +349,7 @@ nfs4_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
// Unless the file was deleted by someone else, verify that all known names have been
// unlinked.
if (node->IsStale() == false) {
if (node != NULL && node->IsStale() == false) {
FileInfo fileInfo;
ASSERT(fs->InoIdMap()->GetFileInfo(&fileInfo, vti->ID()) == B_ENTRY_NOT_FOUND);
}