/*
* Copyright 2012-2016 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "Inode.h"
#include <ctype.h>
#include <string.h>
#include <AutoDeleter.h>
#include <fs_cache.h>
#include <NodeMonitor.h>
#include "IdMap.h"
#include "Request.h"
#include "RootInode.h"
Inode::Inode()
:
fMetaCache(this),
fCache(NULL),
fAttrCache(NULL),
fDelegation(NULL),
fFileCache(NULL),
fMaxFileSize(0),
fOpenState(NULL),
fWriteDirty(false),
fAIOWait(create_sem(1, NULL)),
fAIOCount(0)
{
rw_lock_init(&fDelegationLock, NULL);
mutex_init(&fStateLock, NULL);
mutex_init(&fFileCacheLock, NULL);
rw_lock_init(&fWriteLock, NULL);
mutex_init(&fAIOLock, NULL);
}
status_t
Inode::CreateInode(FileSystem* fs, const FileInfo& fi, Inode** _inode)
{
ASSERT(fs != NULL);
ASSERT(_inode != NULL);
Inode* inode = NULL;
if (fs->Root() == NULL)
inode = new(std::nothrow) RootInode;
else
inode = new(std::nothrow) Inode;
if (inode == NULL)
return B_NO_MEMORY;
inode->fInfo = fi;
inode->fFileSystem = fs;
uint32 attempt = 0;
uint64 size;
do {
RPC::Server* serv = fs->Server();
Request request(serv, fs);
RequestBuilder& req = request.Builder();
req.PutFH(inode->fInfo.fHandle);
Attribute attr[] = { FATTR4_TYPE, FATTR4_CHANGE, FATTR4_SIZE,
FATTR4_FSID, FATTR4_FILEID };
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (inode->HandleErrors(attempt, reply.NFS4Error(), serv))
continue;
reply.PutFH();
AttrValue* values;
uint32 count;
result = reply.GetAttr(&values, &count);
if (result != B_OK)
return result;
if (fi.fFileId == 0) {
if (count < 5 || values[4].fAttribute != FATTR4_FILEID)
inode->fInfo.fFileId = fs->AllocFileId();
else
inode->fInfo.fFileId = values[4].fData.fValue64;
} else
inode->fInfo.fFileId = fi.fFileId;
// FATTR4_TYPE is mandatory
inode->fType = values[0].fData.fValue32;
if (inode->fType == NF4DIR)
inode->fCache = new DirectoryCache(inode);
inode->fAttrCache = new DirectoryCache(inode, true);
// FATTR4_CHANGE is mandatory
inode->fChange = values[1].fData.fValue64;
// FATTR4_SIZE is mandatory
size = values[2].fData.fValue64;
inode->fMaxFileSize = size;
// FATTR4_FSID is mandatory
FileSystemId* fsid
= reinterpret_cast<FileSystemId*>(values[3].fData.fPointer);
if (*fsid != fs->FsId()) {
delete[] values;
return B_ENTRY_NOT_FOUND;
}
delete[] values;
*_inode = inode;
break;
} while (true);
if (inode->fType == NF4REG)
inode->fFileCache = file_cache_create(fs->DevId(), inode->ID(), size);
return B_OK;
}
Inode::~Inode()
{
if (fDelegation != NULL)
RecallDelegation();
if (fFileCache != NULL)
file_cache_delete(fFileCache);
delete fCache;
delete fAttrCache;
delete_sem(fAIOWait);
mutex_destroy(&fAIOLock);
mutex_destroy(&fStateLock);
mutex_destroy(&fFileCacheLock);
rw_lock_destroy(&fDelegationLock);
rw_lock_destroy(&fWriteLock);
ASSERT(fAIOCount == 0);
}
status_t
Inode::RevalidateFileCache()
{
if (fDelegation != NULL)
return B_OK;
uint64 change;
status_t result = GetChangeInfo(&change);
if (result != B_OK)
return result;
MutexLocker _(fFileCacheLock);
if (change == fChange)
return B_OK;
SyncAndCommit(true);
file_cache_delete(fFileCache);
struct stat st;
fMetaCache.InvalidateStat();
result = Stat(&st);
if (result == B_OK)
fMaxFileSize = st.st_size;
fFileCache = file_cache_create(fFileSystem->DevId(), ID(), fMaxFileSize);
change = fChange;
return B_OK;
}
status_t
Inode::LookUp(const char* name, ino_t* id)
{
ASSERT(name != NULL);
ASSERT(id != NULL);
if (fType != NF4DIR)
return B_NOT_A_DIRECTORY;
uint64 change;
uint64 fileID;
FileHandle handle;
status_t result = NFS4Inode::LookUp(name, &change, &fileID, &handle);
if (result != B_OK)
return result;
*id = FileIdToInoT(fileID);
result = ChildAdded(name, fileID, handle);
if (result != B_OK)
return result;
fCache->Lock();
if (!fCache->Valid()) {
fCache->Reset();
fCache->SetChangeInfo(change);
} else
fCache->ValidateChangeInfo(change);
fCache->AddEntry(name, *id);
fCache->Unlock();
return B_OK;
}
status_t
Inode::Link(Inode* dir, const char* name)
{
ASSERT(dir != NULL);
ASSERT(name != NULL);
ChangeInfo changeInfo;
status_t result = NFS4Inode::Link(dir, name, &changeInfo);
if (result != B_OK)
return result;
fFileSystem->Root()->MakeInfoInvalid();
fInfo.fNames->AddName(dir->fInfo.fNames, name);
dir->fCache->Lock();
if (dir->fCache->Valid()) {
if (changeInfo.fAtomic
&& dir->fCache->ChangeInfo() == changeInfo.fBefore) {
dir->fCache->AddEntry(name, fInfo.fFileId, true);
dir->fCache->SetChangeInfo(changeInfo.fAfter);
} else
dir->fCache->Trash();
}
dir->fCache->Unlock();
notify_entry_created(fFileSystem->DevId(), dir->ID(), name, ID());
return B_OK;
}
status_t
Inode::Remove(const char* name, FileType type, ino_t* id)
{
ASSERT(name != NULL);
MemoryDeleter nameDeleter;
if (type == NF4NAMEDATTR) {
status_t result = LoadAttrDirHandle();
if (result != B_OK)
return result;
name = AttrToFileName(name);
if (name == NULL)
return B_NO_MEMORY;
nameDeleter.SetTo(const_cast<char*>(name));
}
ChangeInfo changeInfo;
uint64 fileID;
status_t result = NFS4Inode::RemoveObject(name, type, &changeInfo, &fileID);
if (result != B_OK)
return result;
DirectoryCache* cache = type != NF4NAMEDATTR ? fCache : fAttrCache;
cache->Lock();
if (cache->Valid()) {
if (changeInfo.fAtomic
&& fCache->ChangeInfo() == changeInfo.fBefore) {
cache->RemoveEntry(name);
cache->SetChangeInfo(changeInfo.fAfter);
} else if (cache->ChangeInfo() != changeInfo.fBefore)
cache->Trash();
}
cache->Unlock();
fFileSystem->Root()->MakeInfoInvalid();
if (id != NULL)
*id = FileIdToInoT(fileID);
if (type == NF4NAMEDATTR) {
notify_attribute_changed(fFileSystem->DevId(), -1, ID(), name,
B_ATTR_REMOVED);
} else {
notify_entry_removed(fFileSystem->DevId(), ID(), name,
FileIdToInoT(fileID));
}
return B_OK;
}
status_t
Inode::Rename(Inode* from, Inode* to, const char* fromName, const char* toName,
bool attribute, ino_t* id, ino_t* oldID)
{
ASSERT(from != NULL);
ASSERT(fromName != NULL);
ASSERT(to != NULL);
ASSERT(toName != NULL);
if (from->fFileSystem != to->fFileSystem)
return B_DONT_DO_THAT;
MemoryDeleter fromNameDeleter;
MemoryDeleter toNameDeleter;
if (attribute) {
status_t result = from->LoadAttrDirHandle();
if (result != B_OK)
return result;
result = to->LoadAttrDirHandle();
if (result != B_OK)
return result;
fromName = from->AttrToFileName(fromName);
toName = to->AttrToFileName(toName);
fromNameDeleter.SetTo(const_cast<char*>(fromName));
toNameDeleter.SetTo(const_cast<char*>(toName));
if (fromName == NULL || toName == NULL)
return B_NO_MEMORY;
}
uint64 oldFileID = 0;
if (!attribute)
to->NFS4Inode::LookUp(toName, NULL, &oldFileID, NULL);
uint64 fileID;
ChangeInfo fromChange, toChange;
status_t result = NFS4Inode::RenameNode(from, to, fromName, toName,
&fromChange, &toChange, &fileID, attribute);
if (result != B_OK)
return result;
from->fFileSystem->Root()->MakeInfoInvalid();
if (id != NULL)
*id = FileIdToInoT(fileID);
if (oldID != NULL)
*oldID = FileIdToInoT(oldFileID);
DirectoryCache* cache = attribute ? from->fAttrCache : from->fCache;
cache->Lock();
if (cache->Valid()) {
if (fromChange.fAtomic && cache->ChangeInfo() == fromChange.fBefore) {
cache->RemoveEntry(fromName);
if (to == from)
cache->AddEntry(toName, fileID, true);
cache->SetChangeInfo(fromChange.fAfter);
} else
cache->Trash();
}
cache->Unlock();
if (to != from) {
cache = attribute ? to->fAttrCache : to->fCache;
cache->Lock();
if (cache->Valid()) {
if (toChange.fAtomic
&& (cache->ChangeInfo() == toChange.fBefore)) {
cache->AddEntry(toName, fileID, true);
cache->SetChangeInfo(toChange.fAfter);
} else
cache->Trash();
}
cache->Unlock();
}
if (attribute) {
notify_attribute_changed(from->fFileSystem->DevId(), -1, from->ID(),
fromName, B_ATTR_REMOVED);
notify_attribute_changed(to->fFileSystem->DevId(), -1, to->ID(), toName,
B_ATTR_CREATED);
} else {
notify_entry_moved(from->fFileSystem->DevId(), from->ID(), fromName,
to->ID(), toName, FileIdToInoT(fileID));
}
return B_OK;
}
status_t
Inode::CreateLink(const char* name, const char* path, int mode, ino_t* id)
{
return CreateObject(name, path, mode, NF4LNK, id);
}
status_t
Inode::CreateObject(const char* name, const char* path, int mode, FileType type,
ino_t* id)
{
ASSERT(name != NULL);
ASSERT(type != NF4LNK || path != NULL);
ChangeInfo changeInfo;
uint64 fileID;
FileHandle handle;
status_t result = NFS4Inode::CreateObject(name, path, mode, type,
&changeInfo, &fileID, &handle);
if (result != B_OK)
return result;
fFileSystem->Root()->MakeInfoInvalid();
result = ChildAdded(name, fileID, handle);
if (result != B_OK)
return result;
fCache->Lock();
if (fCache->Valid()) {
if (changeInfo.fAtomic && fCache->ChangeInfo() == changeInfo.fBefore) {
fCache->AddEntry(name, fileID, true);
fCache->SetChangeInfo(changeInfo.fAfter);
} else
fCache->Trash();
}
fCache->Unlock();
notify_entry_created(fFileSystem->DevId(), ID(), name,
FileIdToInoT(fileID));
*id = FileIdToInoT(fileID);
return B_OK;
}
status_t
Inode::Access(int mode)
{
int acc = 0;
uint32 allowed;
bool cache = fFileSystem->GetConfiguration().fCacheMetadata;
status_t result = fMetaCache.GetAccess(geteuid(), &allowed);
if (result != B_OK || !cache) {
result = NFS4Inode::Access(&allowed);
if (result != B_OK)
return result;
fMetaCache.SetAccess(geteuid(), allowed);
}
if ((allowed & ACCESS4_READ) != 0)
acc |= R_OK;
if ((allowed & ACCESS4_LOOKUP) != 0)
acc |= X_OK | R_OK;
if ((allowed & ACCESS4_EXECUTE) != 0)
acc |= X_OK;
if ((allowed & ACCESS4_MODIFY) != 0)
acc |= W_OK;
if ((mode & acc) != mode)
return B_NOT_ALLOWED;
return B_OK;
}
status_t
Inode::Stat(struct stat* st, OpenAttrCookie* attr)
{
ASSERT(st != NULL);
if (attr != NULL)
return GetStat(st, attr);
bool cache = fFileSystem->GetConfiguration().fCacheMetadata;
if (!cache)
return GetStat(st, NULL);
status_t result = fMetaCache.GetStat(st);
if (result != B_OK) {
struct stat temp;
result = GetStat(&temp);
if (result != B_OK)
return result;
fMetaCache.SetStat(temp);
fMetaCache.GetStat(st);
}
return B_OK;
}
status_t
Inode::GetStat(struct stat* st, OpenAttrCookie* attr)
{
ASSERT(st != NULL);
AttrValue* values;
uint32 count;
status_t result = NFS4Inode::GetStat(&values, &count, attr);
if (result != B_OK)
return result;
// FATTR4_SIZE is mandatory
if (count < 1 || values[0].fAttribute != FATTR4_SIZE) {
delete[] values;
return B_BAD_VALUE;
}
st->st_size = values[0].fData.fValue64;
uint32 next = 1;
st->st_mode = Type();
if (count >= next && values[next].fAttribute == FATTR4_MODE) {
st->st_mode |= values[next].fData.fValue32;
next++;
} else
st->st_mode = 777;
if (count >= next && values[next].fAttribute == FATTR4_NUMLINKS) {
st->st_nlink = values[next].fData.fValue32;
next++;
} else
st->st_nlink = 1;
if (count >= next && values[next].fAttribute == FATTR4_OWNER) {
char* owner = reinterpret_cast<char*>(values[next].fData.fPointer);
if (owner != NULL && isdigit(owner[0]))
st->st_uid = atoi(owner);
else
st->st_uid = gIdMapper->GetUserId(owner);
next++;
} else
st->st_uid = 0;
if (count >= next && values[next].fAttribute == FATTR4_OWNER_GROUP) {
char* group = reinterpret_cast<char*>(values[next].fData.fPointer);
if (group != NULL && isdigit(group[0]))
st->st_gid = atoi(group);
else
st->st_gid = gIdMapper->GetGroupId(group);
next++;
} else
st->st_gid = 0;
if (count >= next && values[next].fAttribute == FATTR4_TIME_ACCESS) {
memcpy(&st->st_atim, values[next].fData.fPointer,
sizeof(timespec));
next++;
} else
memset(&st->st_atim, 0, sizeof(timespec));
if (count >= next && values[next].fAttribute == FATTR4_TIME_CREATE) {
memcpy(&st->st_crtim, values[next].fData.fPointer,
sizeof(timespec));
next++;
} else
memset(&st->st_crtim, 0, sizeof(timespec));
if (count >= next && values[next].fAttribute == FATTR4_TIME_METADATA) {
memcpy(&st->st_ctim, values[next].fData.fPointer,
sizeof(timespec));
next++;
} else
memset(&st->st_ctim, 0, sizeof(timespec));
if (count >= next && values[next].fAttribute == FATTR4_TIME_MODIFY) {
memcpy(&st->st_mtim, values[next].fData.fPointer,
sizeof(timespec));
next++;
} else
memset(&st->st_mtim, 0, sizeof(timespec));
delete[] values;
st->st_blksize = fFileSystem->Root()->IOSize();
st->st_blocks = st->st_size / st->st_blksize;
st->st_blocks += st->st_size % st->st_blksize == 0 ? 0 : 1;
return B_OK;
}
status_t
Inode::WriteStat(const struct stat* st, uint32 mask, OpenAttrCookie* cookie)
{
ASSERT(st != NULL);
status_t result;
AttrValue attr[6];
uint32 i = 0;
if ((mask & B_STAT_SIZE) != 0) {
fMaxFileSize = st->st_size;
file_cache_set_size(fFileCache, st->st_size);
attr[i].fAttribute = FATTR4_SIZE;
attr[i].fFreePointer = false;
attr[i].fData.fValue64 = st->st_size;
i++;
}
if ((mask & B_STAT_MODE) != 0) {
attr[i].fAttribute = FATTR4_MODE;
attr[i].fFreePointer = false;
attr[i].fData.fValue32 = st->st_mode;
i++;
}
if ((mask & B_STAT_UID) != 0) {
attr[i].fAttribute = FATTR4_OWNER;
attr[i].fFreePointer = true;
attr[i].fData.fPointer = gIdMapper->GetOwner(st->st_uid);
i++;
}
if ((mask & B_STAT_GID) != 0) {
attr[i].fAttribute = FATTR4_OWNER_GROUP;
attr[i].fFreePointer = true;
attr[i].fData.fPointer = gIdMapper->GetOwnerGroup(st->st_gid);
i++;
}
if ((mask & B_STAT_ACCESS_TIME) != 0) {
attr[i].fAttribute = FATTR4_TIME_ACCESS_SET;
attr[i].fFreePointer = true;
attr[i].fData.fPointer = malloc(sizeof(st->st_atim));
memcpy(attr[i].fData.fPointer, &st->st_atim, sizeof(st->st_atim));
i++;
}
if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
attr[i].fAttribute = FATTR4_TIME_MODIFY_SET;
attr[i].fFreePointer = true;
attr[i].fData.fPointer = malloc(sizeof(st->st_mtim));
memcpy(attr[i].fData.fPointer, &st->st_mtim, sizeof(st->st_mtim));
i++;
}
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);
fMetaCache.InvalidateStat();
const uint32 kAccessMask = B_STAT_MODE | B_STAT_UID | B_STAT_GID;
if ((mask & kAccessMask) != 0)
fMetaCache.InvalidateAccess();
return result;
}
inline status_t
Inode::CheckLockType(short ltype, uint32 mode)
{
switch (ltype) {
case F_UNLCK:
return B_OK;
case F_RDLCK:
if ((mode & O_RDONLY) == 0 && (mode & O_RDWR) == 0)
return EBADF;
return B_OK;
case F_WRLCK:
if ((mode & O_WRONLY) == 0 && (mode & O_RDWR) == 0)
return EBADF;
return B_OK;
default:
return B_BAD_VALUE;
}
}
status_t
Inode::TestLock(OpenFileCookie* cookie, struct flock* lock)
{
ASSERT(cookie != NULL);
ASSERT(lock != NULL);
if (lock->l_type == F_UNLCK)
return B_OK;
status_t result = CheckLockType(lock->l_type, cookie->fMode);
if (result != B_OK)
return result;
LockType ltype = sGetLockType(lock->l_type, false);
uint64 position = lock->l_start;
uint64 length;
if (lock->l_len + lock->l_start == OFF_MAX)
length = UINT64_MAX;
else
length = lock->l_len;
bool conflict;
result = NFS4Inode::TestLock(cookie, <ype, &position, &length, conflict);
if (result != B_OK)
return result;
if (conflict) {
lock->l_type = sLockTypeToHaiku(ltype);
lock->l_start = static_cast<off_t>(position);
if (length >= OFF_MAX)
lock->l_len = OFF_MAX;
else
lock->l_len = static_cast<off_t>(length);
} else
lock->l_type = F_UNLCK;
return B_OK;
}
status_t
Inode::AcquireLock(OpenFileCookie* cookie, const struct flock* lock,
bool wait)
{
ASSERT(cookie != NULL);
ASSERT(lock != NULL);
OpenState* state = cookie->fOpenState;
status_t result = CheckLockType(lock->l_type, cookie->fMode);
if (result != B_OK)
return result;
thread_info info;
get_thread_info(find_thread(NULL), &info);
MutexLocker locker(state->fOwnerLock);
LockOwner* owner = state->GetLockOwner(info.team);
if (owner == NULL)
return B_NO_MEMORY;
LockInfo* linfo = new(std::nothrow) LockInfo(owner);
if (linfo == NULL)
return B_NO_MEMORY;
locker.Unlock();
linfo->fStart = lock->l_start;
if (lock->l_len + lock->l_start == OFF_MAX)
linfo->fLength = UINT64_MAX;
else
linfo->fLength = lock->l_len;
linfo->fType = sGetLockType(lock->l_type, wait);
result = NFS4Inode::AcquireLock(cookie, linfo, wait);
if (result != B_OK) {
delete linfo;
return result;
}
MutexLocker _(state->fLocksLock);
state->AddLock(linfo);
cookie->AddLock(linfo);
return B_OK;
}
status_t
Inode::ReleaseLock(OpenFileCookie* cookie, const struct flock* lock)
{
ASSERT(cookie != NULL);
ASSERT(lock != NULL);
SyncAndCommit();
LockInfo* prev = NULL;
thread_info info;
get_thread_info(find_thread(NULL), &info);
uint32 owner = info.team;
OpenState* state = cookie->fOpenState;
MutexLocker locker(state->fLocksLock);
LockInfo* linfo = state->fLocks;
while (linfo != NULL) {
if (linfo->fOwner->fOwner == owner && *linfo == *lock) {
state->RemoveLock(linfo, prev);
break;
}
prev = linfo;
linfo = linfo->fNext;
}
prev = NULL;
linfo = cookie->fLocks;
while (linfo != NULL) {
if (linfo->fOwner->fOwner == owner && *linfo == *lock) {
cookie->RemoveLock(linfo, prev);
break;
}
prev = linfo;
linfo = linfo->fCookieNext;
}
locker.Unlock();
if (linfo == NULL)
return B_BAD_VALUE;
status_t result = NFS4Inode::ReleaseLock(cookie, linfo);
if (result != B_OK)
return result;
state->DeleteLock(linfo);
return B_OK;
}
status_t
Inode::ReleaseAllLocks(OpenFileCookie* cookie)
{
ASSERT(cookie != NULL);
if (cookie->fLocks)
SyncAndCommit();
OpenState* state = cookie->fOpenState;
MutexLocker _(state->fLocksLock);
LockInfo* linfo = cookie->fLocks;
while (linfo != NULL) {
cookie->RemoveLock(linfo, NULL);
LockInfo* prev = NULL;
LockInfo* stateLock = state->fLocks;
while (stateLock != NULL) {
if (*linfo == *stateLock) {
state->RemoveLock(stateLock, prev);
break;
}
prev = stateLock;
stateLock = stateLock->fNext;
}
NFS4Inode::ReleaseLock(cookie, linfo);
state->DeleteLock(linfo);
linfo = cookie->fLocks;
}
return B_OK;
}
status_t
Inode::ChildAdded(const char* name, uint64 fileID,
const FileHandle& fileHandle)
{
ASSERT(name != NULL);
fFileSystem->Root()->MakeInfoInvalid();
FileInfo fi;
fi.fFileId = fileID;
fi.fHandle = fileHandle;
return fFileSystem->InoIdMap()->AddName(fi, fInfo.fNames, name,
FileIdToInoT(fileID));
}
const char*
Inode::Name() const
{
ASSERT(fInfo.fNames->fNames.Head() != NULL);
return fInfo.fNames->fNames.Head()->fName;
}
void
Inode::SetDelegation(Delegation* delegation)
{
ASSERT(delegation != NULL);
WriteLocker _(fDelegationLock);
fMetaCache.InvalidateStat();
struct stat st;
Stat(&st);
fMetaCache.LockValid();
fDelegation = delegation;
fOpenState->AcquireReference();
fOpenState->fDelegation = delegation;
fFileSystem->AddDelegation(delegation);
}
void
Inode::RecallDelegation(bool truncate)
{
WriteLocker _(fDelegationLock);
if (fDelegation == NULL)
return;
ReturnDelegation(truncate);
}
void
Inode::RecallReadDelegation()
{
WriteLocker _(fDelegationLock);
if (fDelegation == NULL || fDelegation->Type() != OPEN_DELEGATE_READ)
return;
ReturnDelegation(false);
}
void
Inode::ReturnDelegation(bool truncate)
{
ASSERT(fDelegation != NULL);
fDelegation->GiveUp(truncate);
fMetaCache.UnlockValid();
fFileSystem->RemoveDelegation(fDelegation);
MutexLocker stateLocker(fStateLock);
fOpenState->fDelegation = NULL;
ReleaseOpenState();
delete fDelegation;
fDelegation = NULL;
}
void
Inode::ReleaseOpenState()
{
ASSERT(fOpenState != NULL);
if (fOpenState->ReleaseReference() == 1) {
ASSERT(fAIOCount == 0);
fOpenState = NULL;
}
}
status_t
Inode::SyncAndCommit(bool force)
{
if (!force && fDelegation != NULL)
return B_OK;
file_cache_sync(fFileCache);
WaitAIOComplete();
return Commit();
}
void
Inode::BeginAIOOp()
{
MutexLocker _(fAIOLock);
fAIOCount++;
if (fAIOCount == 1)
acquire_sem(fAIOWait);
}
void
Inode::EndAIOOp()
{
MutexLocker _(fAIOLock);
ASSERT(fAIOCount > 0);
fAIOCount--;
if (fAIOCount == 0)
release_sem(fAIOWait);
}
↑ V773 The function was exited without releasing the 'inode' pointer. A memory leak is possible.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fType, fChange.