nfs4: support hard links properly

The main purpose of this patch is to prevent VFS from removing a vnode to early
what might have happened if the NFS client knew it had more than one name but
then one of them was deleted. Moreover, all discovered and still valid names
are stored what may be useful in proper file handle recovery when they are
volatile.

This patch fixes both #9558 and #9561.
This commit is contained in:
Pawel Dziepak
2013-03-26 20:51:37 +01:00
parent efc29cc57a
commit 469f13fdfe
16 changed files with 487 additions and 373 deletions
@@ -308,19 +308,6 @@ DirectoryCache::NotifyChanges(DirectoryCacheSnapshot* oldSnapshot,
} else { } else {
notify_entry_created(fInode->GetFileSystem()->DevId(), notify_entry_created(fInode->GetFileSystem()->DevId(),
fInode->ID(), newCurrent->fName, newCurrent->fNode); fInode->ID(), newCurrent->fName, newCurrent->fNode);
do {
FileInfo fi;
fi.fFileId = newCurrent->fNode;
fi.fParent = fInode->fInfo.fHandle;
status_t result = fi.CreateName(fInode->fInfo.fPath,
newCurrent->fName);
if (result != B_OK)
break;
fInode->GetFileSystem()->InoIdMap()->AddEntry(fi,
Inode::FileIdToInoT(newCurrent->fNode), true);
} while (false);
} }
} else } else
oldSnapshot->fEntries.Remove(prev, oldCurrent); oldSnapshot->fEntries.Remove(prev, oldCurrent);
+140 -78
View File
@@ -13,77 +13,119 @@
#include "Request.h" #include "Request.h"
status_t InodeName::InodeName(InodeNames* parent, const char* name)
FileInfo::ParsePath(RequestBuilder& req, uint32& count, const char* _path, :
bool getFileHandle) fParent(parent),
fName(strdup(name))
{ {
ASSERT(_path != NULL); if (fParent != NULL)
fParent->AcquireReference();
char* path = strdup(_path);
if (path == NULL)
return B_NO_MEMORY;
char* pathStart = path;
char* pathEnd;
while (pathStart != NULL) {
pathEnd = strchr(pathStart, '/');
if (pathEnd != NULL)
*pathEnd = '\0';
if (pathEnd != pathStart) {
if (!strcmp(pathStart, "..")) {
req.LookUpUp();
count++;
} else if (strcmp(pathStart, ".")) {
req.LookUp(pathStart);
if (getFileHandle)
req.GetFH();
count++;
}
}
if (pathEnd != NULL && pathEnd[1] != '\0')
pathStart = pathEnd + 1;
else
pathStart = NULL;
}
free(path);
return B_OK;
} }
status_t InodeName::~InodeName()
FileInfo::CreateName(const char* dirPath, const char* name)
{ {
ASSERT(name != NULL); if (fParent != NULL)
fParent->ReleaseReference();
free(const_cast<char*>(fName)); free(const_cast<char*>(fName));
fName = strdup(name); }
if (fName == NULL)
return B_NO_MEMORY;
free(const_cast<char*>(fPath));
fPath = NULL;
if (dirPath != NULL) {
char* path = reinterpret_cast<char*>(malloc(strlen(name) + 2
+ strlen(dirPath)));
if (path == NULL)
return B_NO_MEMORY;
strcpy(path, dirPath); InodeNames::InodeNames()
strcat(path, "/"); {
strcat(path, name); mutex_init(&fLock, NULL);
}
fPath = path;
} else
fPath = strdup(name);
if (fPath == NULL) InodeNames::~InodeNames()
return B_NO_MEMORY; {
while (!fNames.IsEmpty())
delete fNames.RemoveHead();
mutex_destroy(&fLock);
}
status_t
InodeNames::AddName(InodeNames* parent, const char* name)
{
MutexLocker _(fLock);
InodeName* current = fNames.Head();
while (current != NULL) {
if (current->fParent == parent && !strcmp(current->fName, name))
return B_OK; return B_OK;
current = fNames.GetNext(current);
}
InodeName* newName = new InodeName(parent, name);
if (newName == NULL)
return B_NO_MEMORY;
fNames.Add(newName);
return B_OK;
}
bool
InodeNames::RemoveName(InodeNames* parent, const char* name)
{
MutexLocker _(fLock);
InodeName* previous = NULL;
InodeName* current = fNames.Head();
while (current != NULL) {
if (current->fParent == parent && !strcmp(current->fName, name)) {
fNames.Remove(previous, current);
delete current;
break;
}
previous = current;
current = fNames.GetNext(current);
}
return fNames.IsEmpty();
}
FileInfo::FileInfo()
:
fFileId(0),
fNames(NULL)
{
}
FileInfo::~FileInfo()
{
if (fNames != NULL)
fNames->ReleaseReference();
}
FileInfo::FileInfo(const FileInfo& fi)
:
fFileId(fi.fFileId),
fHandle(fi.fHandle),
fNames(fi.fNames)
{
if (fNames != NULL)
fNames->AcquireReference();
}
FileInfo&
FileInfo::operator=(const FileInfo& fi)
{
fFileId = fi.fFileId;
fHandle = fi.fHandle;
if (fNames != NULL)
fNames->ReleaseReference();
fNames = fi.fNames;
if (fNames != NULL)
fNames->AcquireReference();
return *this;
} }
@@ -96,16 +138,41 @@ FileInfo::UpdateFileHandles(FileSystem* fs)
RequestBuilder& req = request.Builder(); RequestBuilder& req = request.Builder();
req.PutRootFH(); req.PutRootFH();
req.GetFH();
uint32 lookupCount = 0; uint32 lookupCount = 0;
status_t result = ParsePath(req, lookupCount, fs->Path(), true); const char** path = fs->Path();
if (result != B_OK) if (path != NULL) {
return result; for (; path[lookupCount] != NULL; lookupCount++)
req.LookUp(path[lookupCount]);
}
result = ParsePath(req, lookupCount, fPath, true); uint32 i;
if (result != B_OK) InodeNames* names = fNames;
return result; for (i = 0; names != NULL; i++)
names = names->fNames.Head()->fParent;
if (i > 0) {
names = fNames;
InodeNames** pathNames = new InodeNames*[i];
if (pathNames == NULL)
return B_NO_MEMORY;
for (i = 0; names != NULL; i++) {
pathNames[i] = names;
names = names->fNames.Head()->fParent;
}
for (; i > 0; i--) {
if (!strcmp(pathNames[i - 1]->fNames.Head()->fName, ""))
continue;
req.LookUp(pathNames[i - 1]->fNames.Head()->fName);
lookupCount++;
}
delete[] pathNames;
}
req.GetFH();
if (fs->IsAttrSupported(FATTR4_FILEID)) { if (fs->IsAttrSupported(FATTR4_FILEID)) {
AttrValue attr; AttrValue attr;
@@ -115,25 +182,20 @@ FileInfo::UpdateFileHandles(FileSystem* fs)
req.Verify(&attr, 1); req.Verify(&attr, 1);
} }
result = request.Send(); status_t result = request.Send();
if (result != B_OK) if (result != B_OK)
return result; return result;
ReplyInterpreter& reply = request.Reply(); ReplyInterpreter& reply = request.Reply();
FileHandle parent;
FileHandle child;
reply.PutRootFH(); reply.PutRootFH();
reply.GetFH(&child); for (uint32 i = 0; i < lookupCount; i++)
parent = child;
for (uint32 i = 0; i < lookupCount; i++) {
reply.LookUp(); reply.LookUp();
parent = child;
result = reply.GetFH(&child); FileHandle handle;
result = reply.GetFH(&handle);
if (result != B_OK) if (result != B_OK)
return result; return result;
}
if (fs->IsAttrSupported(FATTR4_FILEID)) { if (fs->IsAttrSupported(FATTR4_FILEID)) {
result = reply.Verify(); result = reply.Verify();
@@ -141,8 +203,8 @@ FileInfo::UpdateFileHandles(FileSystem* fs)
return result; return result;
} }
fHandle = child; fHandle = handle;
fParent = parent; fNames->fHandle = handle;
return B_OK; return B_OK;
} }
+40 -62
View File
@@ -12,7 +12,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <lock.h>
#include <SupportDefs.h> #include <SupportDefs.h>
#include <util/KernelReferenceable.h>
#define NFS4_FHSIZE 128 #define NFS4_FHSIZE 128
@@ -26,11 +28,34 @@ struct FileHandle {
inline FileHandle& operator=(const FileHandle& fh); inline FileHandle& operator=(const FileHandle& fh);
inline bool operator==(const FileHandle& handle) const;
inline bool operator!=(const FileHandle& handle) const; inline bool operator!=(const FileHandle& handle) const;
inline bool operator>(const FileHandle& handle) const; inline bool operator>(const FileHandle& handle) const;
inline bool operator<(const FileHandle& handle) const; inline bool operator<(const FileHandle& handle) const;
}; };
class InodeNames;
struct InodeName : public SinglyLinkedListLinkImpl<InodeName> {
InodeName(InodeNames* parent, const char* name);
~InodeName();
InodeNames* fParent;
const char* fName;
};
struct InodeNames : public KernelReferenceable {
InodeNames();
~InodeNames();
status_t AddName(InodeNames* parent, const char* name);
bool RemoveName(InodeNames* parent,
const char* name);
mutex fLock;
SinglyLinkedList<InodeName> fNames;
FileHandle fHandle;
};
class FileSystem; class FileSystem;
class RequestBuilder; class RequestBuilder;
@@ -42,23 +67,16 @@ struct FileInfo {
uint64 fFileId; uint64 fFileId;
FileHandle fHandle; FileHandle fHandle;
FileHandle fParent; InodeNames* fNames;
const char* fName;
const char* fPath;
FileHandle fAttrDir; FileHandle fAttrDir;
inline FileInfo(); FileInfo();
inline ~FileInfo(); ~FileInfo();
inline FileInfo(const FileInfo& fi); FileInfo(const FileInfo& fi);
inline FileInfo& operator=(const FileInfo& fi); FileInfo& operator=(const FileInfo& fi);
status_t UpdateFileHandles(FileSystem* fs); status_t UpdateFileHandles(FileSystem* fs);
static status_t ParsePath(RequestBuilder& req, uint32& count,
const char* _path, bool getFileHandle = false);
status_t CreateName(const char* dirPath, const char* name);
}; };
struct FileSystemId { struct FileSystemId {
@@ -97,11 +115,18 @@ FileHandle::operator=(const FileHandle& fh)
inline bool inline bool
FileHandle::operator!=(const FileHandle& handle) const FileHandle::operator==(const FileHandle& handle) const
{ {
if (fSize != handle.fSize) if (fSize != handle.fSize)
return true; return false;
return memcmp(fData, handle.fData, fSize) != 0; return memcmp(fData, handle.fData, fSize) == 0;
}
inline bool
FileHandle::operator!=(const FileHandle& handle) const
{
return !operator==(handle);
} }
@@ -123,53 +148,6 @@ FileHandle::operator<(const FileHandle& handle) const
} }
inline
FileInfo::FileInfo()
:
fFileId(0),
fName(NULL),
fPath(NULL)
{
}
inline
FileInfo::~FileInfo()
{
free(const_cast<char*>(fName));
free(const_cast<char*>(fPath));
}
inline
FileInfo::FileInfo(const FileInfo& fi)
:
fFileId(fi.fFileId),
fHandle(fi.fHandle),
fParent(fi.fParent),
fName(strdup(fi.fName)),
fPath(strdup(fi.fPath))
{
}
inline FileInfo&
FileInfo::operator=(const FileInfo& fi)
{
fFileId = fi.fFileId;
fHandle = fi.fHandle;
fParent = fi.fParent;
free(const_cast<char*>(fName));
fName = strdup(fi.fName);
free(const_cast<char*>(fPath));
fPath = strdup(fi.fPath);
return *this;
}
inline bool inline bool
FileSystemId::operator==(const FileSystemId& fsid) const FileSystemId::operator==(const FileSystemId& fsid) const
{ {
@@ -54,33 +54,74 @@ FileSystem::~FileSystem()
mutex_destroy(&fOpenOwnerLock); mutex_destroy(&fOpenOwnerLock);
mutex_destroy(&fCreateFileLock); mutex_destroy(&fCreateFileLock);
free(const_cast<char*>(fPath)); if (fPath != NULL) {
for (uint32 i = 0; fPath[i] != NULL; i++)
free(const_cast<char*>(fPath[i]));
}
delete[] fPath;
delete fRoot; delete fRoot;
} }
static const char* static InodeNames*
GetPath(const char* root, const char* path) GetInodeNames(const char** root, const char* _path)
{ {
ASSERT(path != NULL); ASSERT(_path != NULL);
int slash = 0;
int i; int i;
for (i = 0; path[i] != '\0'; i++) { char* path = strdup(_path);
if (path[i] == '/') if (path == NULL)
slash = i; return NULL;
MemoryDeleter _(path);
if (root == NULL) if (root != NULL) {
break; for (i = 0; root[i] != NULL; i++) {
char* pathEnd = strchr(path, '/');
if (path[i] != root[i] || root[i] == '\0') if (pathEnd == path) {
break; path++;
i--;
continue;
} }
if (path[i] == '\0') if (pathEnd == NULL) {
path = NULL;
break;
} else
path = pathEnd + 1;
}
}
InodeNames* names = NULL;
if (path == NULL) {
names = new InodeNames;
if (names == NULL)
return NULL; return NULL;
return path + slash; names->AddName(NULL, "");
return names;
}
do {
char* pathEnd = strchr(path, '/');
if (pathEnd != NULL)
*pathEnd = '\0';
InodeNames* name = new InodeNames;
if (name == NULL) {
delete names;
return NULL;
}
name->AddName(names, path);
names = name;
if (pathEnd == NULL)
break;
path = pathEnd + 1;
} while (*path != '\0');
return names;
} }
@@ -103,7 +144,7 @@ FileSystem::Mount(FileSystem** _fs, RPC::Server* serv, const char* fsPath,
req.PutRootFH(); req.PutRootFH();
uint32 lookupCount = 0; uint32 lookupCount = 0;
status_t result = FileInfo::ParsePath(req, lookupCount, fsPath); status_t result = _ParsePath(req, lookupCount, fsPath);
if (result != B_OK) if (result != B_OK)
return result; return result;
@@ -156,30 +197,25 @@ FileSystem::Mount(FileSystem** _fs, RPC::Server* serv, const char* fsPath,
FSLocations* locs FSLocations* locs
= reinterpret_cast<FSLocations*>(values[3].fData.fLocations); = reinterpret_cast<FSLocations*>(values[3].fData.fLocations);
fs->fPath = strdup(locs->fRootPath); fs->fPath = locs->fRootPath;
locs->fRootPath = NULL;
} else } else
fs->fPath = NULL; fs->fPath = NULL;
FileInfo fi; FileInfo fi;
const char* name;
if (fsPath != NULL && fsPath[0] == '/')
fsPath++;
fs->fServer = serv; fs->fServer = serv;
fs->fDevId = id; fs->fDevId = id;
fs->fFsId = *fsid; fs->fFsId = *fsid;
fi.fHandle = fh; fi.fHandle = fh;
fi.fParent = fh;
fi.fPath = strdup(GetPath(fs->fPath, fsPath));
if (fi.fPath != NULL) { fi.fNames = GetInodeNames(fs->fPath, fsPath);
name = strrchr(fi.fPath, '/'); if (fi.fNames == NULL) {
if (name != NULL) { delete[] values;
name++; return B_NO_MEMORY;
fi.fName = strdup(name);
}
} }
fi.fNames->fHandle = fh;
delete[] values; delete[] values;
@@ -188,7 +224,7 @@ FileSystem::Mount(FileSystem** _fs, RPC::Server* serv, const char* fsPath,
if (result != B_OK) if (result != B_OK)
return result; return result;
name = strrchr(fsPath, '/'); char* name = strrchr(fsPath, '/');
if (name != NULL) { if (name != NULL) {
name++; name++;
reinterpret_cast<RootInode*>(inode)->SetName(name); reinterpret_cast<RootInode*>(inode)->SetName(name);
@@ -263,8 +299,14 @@ FileSystem::Migrate(const RPC::Server* serv)
if (gRPCServerManager->Acquire(&fServer, &resolver, if (gRPCServerManager->Acquire(&fServer, &resolver,
CreateNFS4Server) == B_OK) { CreateNFS4Server) == B_OK) {
free(const_cast<char*>(fPath)); if (fPath != NULL) {
fPath = strdup(locs->fLocations[i].fRootPath); for (uint32 i = 0; fPath[i] != NULL; i++)
free(const_cast<char*>(fPath[i]));
}
delete[] fPath;
fPath = locs->fLocations[i].fRootPath;
locs->fLocations[i].fRootPath = NULL;
if (fPath == NULL) { if (fPath == NULL) {
gRPCServerManager->Release(fServer); gRPCServerManager->Release(fServer);
@@ -391,3 +433,41 @@ FileSystem::GetDelegation(const FileHandle& handle)
return it.Current(); return it.Current();
} }
status_t
FileSystem::_ParsePath(RequestBuilder& req, uint32& count, const char* _path)
{
ASSERT(_path != NULL);
char* path = strdup(_path);
if (path == NULL)
return B_NO_MEMORY;
char* pathStart = path;
char* pathEnd;
while (pathStart != NULL) {
pathEnd = strchr(pathStart, '/');
if (pathEnd != NULL)
*pathEnd = '\0';
if (pathEnd != pathStart) {
if (!strcmp(pathStart, "..")) {
req.LookUpUp();
count++;
} else if (strcmp(pathStart, ".")) {
req.LookUp(pathStart);
count++;
}
}
if (pathEnd != NULL && pathEnd[1] != '\0')
pathStart = pathEnd + 1;
else
pathStart = NULL;
}
free(path);
return B_OK;
}
@@ -59,7 +59,7 @@ public:
inline RPC::Server* Server(); inline RPC::Server* Server();
inline NFS4Server* NFSServer(); inline NFS4Server* NFSServer();
inline const char* Path() const; inline const char** Path() const;
inline const FileSystemId& FsId() const; inline const FileSystemId& FsId() const;
inline uint64 AllocFileId(); inline uint64 AllocFileId();
@@ -80,6 +80,9 @@ public:
private: private:
FileSystem(const MountConfiguration& config); FileSystem(const MountConfiguration& config);
static status_t _ParsePath(RequestBuilder& req, uint32& count,
const char* _path);
mutex fCreateFileLock; mutex fCreateFileLock;
mutex fDelegationLock; mutex fDelegationLock;
@@ -99,7 +102,7 @@ private:
bool fNamedAttrs; bool fNamedAttrs;
FileSystemId fFsId; FileSystemId fFsId;
const char* fPath; const char** fPath;
RootInode* fRoot; RootInode* fRoot;
@@ -158,7 +161,7 @@ FileSystem::NFSServer()
} }
inline const char* inline const char**
FileSystem::Path() const FileSystem::Path() const
{ {
ASSERT(fPath != NULL); ASSERT(fPath != NULL);
+5 -14
View File
@@ -232,14 +232,7 @@ Inode::Link(Inode* dir, const char* name)
return result; return result;
fFileSystem->Root()->MakeInfoInvalid(); fFileSystem->Root()->MakeInfoInvalid();
fInfo.fNames->AddName(dir->fInfo.fNames, name);
FileInfo fi = fInfo;
fi.fParent = dir->fInfo.fHandle;
result = fi.CreateName(fInfo.fPath, name);
if (result != B_OK)
return result;
fFileSystem->InoIdMap()->AddEntry(fi, fInfo.fFileId);
dir->fCache->Lock(); dir->fCache->Lock();
if (dir->fCache->Valid()) { if (dir->fCache->Valid()) {
@@ -875,19 +868,17 @@ Inode::ChildAdded(const char* name, uint64 fileID,
FileInfo fi; FileInfo fi;
fi.fFileId = fileID; fi.fFileId = fileID;
fi.fHandle = fileHandle; fi.fHandle = fileHandle;
fi.fParent = fInfo.fHandle;
status_t result = fi.CreateName(fInfo.fPath, name);
if (result != B_OK)
return result;
return fFileSystem->InoIdMap()->AddEntry(fi, FileIdToInoT(fileID)); return fFileSystem->InoIdMap()->AddName(fi, fInfo.fNames, name,
FileIdToInoT(fileID));
} }
const char* const char*
Inode::Name() const Inode::Name() const
{ {
return fInfo.fName; ASSERT(fInfo.fNames->fNames.Head() != NULL);
return fInfo.fNames->fNames.Head()->fName;
} }
@@ -0,0 +1,81 @@
/*
* Copyright 2013 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "InodeIdMap.h"
status_t
InodeIdMap::AddName(FileInfo& fileInfo, InodeNames* parent,
const char* name, ino_t id)
{
MutexLocker _(fLock);
AVLTreeMap<ino_t, FileInfo>::Iterator iterator = fMap.Find(id);
if (iterator.HasCurrent()) {
if (fileInfo.fHandle == iterator.Current().fHandle) {
return iterator.CurrentValuePointer()->fNames->AddName(parent,
name);
}
}
fMap.Remove(id);
fileInfo.fNames = new InodeNames;
if (fileInfo.fNames == NULL)
return B_NO_MEMORY;
fileInfo.fNames->fHandle = fileInfo.fHandle;
status_t result = fileInfo.fNames->AddName(parent, name);
if (result != B_OK) {
delete fileInfo.fNames;
return result;
}
return fMap.Insert(id, fileInfo);
}
bool
InodeIdMap::RemoveName(ino_t id, InodeNames* parent, const char* name)
{
ASSERT(name != NULL);
MutexLocker _(fLock);
AVLTreeMap<ino_t, FileInfo>::Iterator iterator = fMap.Find(id);
if (!iterator.HasCurrent())
return true;
FileInfo* fileInfo = iterator.CurrentValuePointer();
return fileInfo->fNames->RemoveName(parent, name);
}
status_t
InodeIdMap::RemoveEntry(ino_t id)
{
MutexLocker _(fLock);
return fMap.Remove(id);
}
status_t
InodeIdMap::GetFileInfo(FileInfo* fileInfo, ino_t id)
{
ASSERT(fileInfo != NULL);
MutexLocker _(fLock);
AVLTreeMap<ino_t, FileInfo>::Iterator iterator = fMap.Find(id);
if (!iterator.HasCurrent())
return B_ENTRY_NOT_FOUND;
*fileInfo = iterator.Current();
if (fileInfo->fNames->fNames.IsEmpty())
return B_ENTRY_NOT_FOUND;
return B_OK;
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012 Haiku, Inc. All rights reserved. * Copyright 2012,2013 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -16,27 +16,24 @@
#include "FileInfo.h" #include "FileInfo.h"
struct InodeIdMapEntry {
FileInfo fFileInfo;
bool fRemoved;
};
class InodeIdMap { class InodeIdMap {
public: public:
inline InodeIdMap(); inline InodeIdMap();
inline ~InodeIdMap(); inline ~InodeIdMap();
inline status_t AddEntry(const FileInfo& fi, status_t AddName(FileInfo& fileInfo,
ino_t id, bool weak = false); InodeNames* parent,
inline status_t MarkRemoved(ino_t id); const char* name, ino_t id);
inline status_t RemoveEntry(ino_t id); bool RemoveName(ino_t id,
inline status_t GetFileInfo(FileInfo* fi, ino_t id); InodeNames* parent,
const char* name);
protected: status_t RemoveEntry(ino_t id);
inline bool _IsEntryRemoved(ino_t id); status_t GetFileInfo(FileInfo* fileInfo,
ino_t id);
private: private:
AVLTreeMap<ino_t, InodeIdMapEntry> fMap; AVLTreeMap<ino_t, FileInfo> fMap;
mutex fLock; mutex fLock;
}; };
@@ -56,71 +53,5 @@ InodeIdMap::~InodeIdMap()
} }
inline status_t
InodeIdMap::AddEntry(const FileInfo& fi, ino_t id, bool weak)
{
InodeIdMapEntry entry;
MutexLocker _(fLock);
if (!weak || _IsEntryRemoved(id))
fMap.Remove(id);
entry.fFileInfo = fi;
entry.fRemoved = false;
return fMap.Insert(id, entry);
}
inline status_t
InodeIdMap::MarkRemoved(ino_t id)
{
MutexLocker _(fLock);
AVLTreeMap<ino_t, InodeIdMapEntry>::Iterator it = fMap.Find(id);
if (!it.HasCurrent())
return B_ENTRY_NOT_FOUND;
it.CurrentValuePointer()->fRemoved = true;
return B_OK;
}
inline status_t
InodeIdMap::RemoveEntry(ino_t id)
{
MutexLocker _(fLock);
if (_IsEntryRemoved(id))
return fMap.Remove(id);
return B_OK;
}
inline status_t
InodeIdMap::GetFileInfo(FileInfo* fi, ino_t id)
{
ASSERT(fi != NULL);
MutexLocker _(fLock);
AVLTreeMap<ino_t, InodeIdMapEntry>::Iterator it = fMap.Find(id);
if (!it.HasCurrent())
return B_ENTRY_NOT_FOUND;
*fi = it.Current().fFileInfo;
return B_OK;
}
// Caller must hold fLock
inline bool
InodeIdMap::_IsEntryRemoved(ino_t id)
{
AVLTreeMap<ino_t, InodeIdMapEntry>::Iterator it = fMap.Find(id);
if (!it.HasCurrent())
return true;
return it.Current().fRemoved;
}
#endif // INODEIDMAP_H #endif // INODEIDMAP_H
@@ -36,13 +36,12 @@ Inode::CreateState(const char* name, int mode, int perms, OpenState* state,
if (result != B_OK) if (result != B_OK)
return result; return result;
FileInfo fi; FileInfo fileInfo;
fi.fFileId = fileID; fileInfo.fFileId = fileID;
fi.fHandle = handle; fileInfo.fHandle = handle;
fi.fParent = fInfo.fHandle;
fi.CreateName(fInfo.fPath, name);
fFileSystem->InoIdMap()->AddEntry(fi, FileIdToInoT(fileID)); fFileSystem->InoIdMap()->AddName(fileInfo, fInfo.fNames, name,
FileIdToInoT(fileID));
fCache->Lock(); fCache->Lock();
if (fCache->Valid()) { if (fCache->Valid()) {
@@ -56,7 +55,7 @@ Inode::CreateState(const char* name, int mode, int perms, OpenState* state,
fCache->Unlock(); fCache->Unlock();
state->fFileSystem = fFileSystem; state->fFileSystem = fFileSystem;
state->fInfo = fi; state->fInfo = fileInfo;
state->fMode = mode & O_RWMASK; state->fMode = mode & O_RWMASK;
return B_OK; return B_OK;
@@ -253,8 +252,6 @@ Inode::OpenAttr(const char* _name, int mode, OpenAttrCookie* cookie,
if (state == NULL) if (state == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
state->fInfo.fName = strdup(name);
state->fInfo.fParent = fInfo.fAttrDir;
state->fFileSystem = fFileSystem; state->fFileSystem = fFileSystem;
result = NFS4Inode::OpenAttr(state, name, mode, &data, create); result = NFS4Inode::OpenAttr(state, name, mode, &data, create);
if (result != B_OK) { if (result != B_OK) {
@@ -12,6 +12,7 @@ KernelAddon nfs4 :
FileSystem.cpp FileSystem.cpp
IdMap.cpp IdMap.cpp
Inode.cpp Inode.cpp
InodeIdMap.cpp
InodeDir.cpp InodeDir.cpp
InodeRegular.cpp InodeRegular.cpp
kernel_interface.cpp kernel_interface.cpp
@@ -125,9 +125,7 @@ NFS4Inode::LookUp(const char* name, uint64* change, uint64* fileID,
Request request(serv, fFileSystem); Request request(serv, fFileSystem);
RequestBuilder& req = request.Builder(); RequestBuilder& req = request.Builder();
if (parent) (void)parent; // TODO: add support for named attributes
req.PutFH(fInfo.fParent);
else
req.PutFH(fInfo.fHandle); req.PutFH(fInfo.fHandle);
if (change != NULL) { if (change != NULL) {
@@ -562,16 +560,16 @@ NFS4Inode::OpenFile(OpenState* state, int mode, OpenDelegationData* delegation)
// Since we are opening the file using a pair (parentFH, name) we // Since we are opening the file using a pair (parentFH, name) we
// need to check for race conditions. // need to check for race conditions.
if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
req.PutFH(fInfo.fParent); req.PutFH(fInfo.fNames->fNames.Head()->fParent->fHandle);
req.LookUp(fInfo.fName); req.LookUp(fInfo.fNames->fNames.Head()->fName);
AttrValue attr; AttrValue attr;
attr.fAttribute = FATTR4_FILEID; attr.fAttribute = FATTR4_FILEID;
attr.fFreePointer = false; attr.fFreePointer = false;
attr.fData.fValue64 = fInfo.fFileId; attr.fData.fValue64 = fInfo.fFileId;
req.Verify(&attr, 1); req.Verify(&attr, 1);
} else if (fFileSystem->ExpireType() == FH4_PERSISTENT) { } else if (fFileSystem->ExpireType() == FH4_PERSISTENT) {
req.PutFH(fInfo.fParent); req.PutFH(fInfo.fNames->fNames.Head()->fParent->fHandle);
req.LookUp(fInfo.fName); req.LookUp(fInfo.fNames->fNames.Head()->fName);
AttrValue attr; AttrValue attr;
attr.fAttribute = FATTR4_FILEHANDLE; attr.fAttribute = FATTR4_FILEHANDLE;
attr.fFreePointer = true; attr.fFreePointer = true;
@@ -580,9 +578,10 @@ NFS4Inode::OpenFile(OpenState* state, int mode, OpenDelegationData* delegation)
req.Verify(&attr, 1); req.Verify(&attr, 1);
} }
req.PutFH(fInfo.fParent); req.PutFH(fInfo.fNames->fNames.Head()->fParent->fHandle);
req.Open(CLAIM_NULL, sequence, sModeToAccess(mode), state->fClientID, req.Open(CLAIM_NULL, sequence, sModeToAccess(mode), state->fClientID,
OPEN4_NOCREATE, fFileSystem->OpenOwner(), fInfo.fName); OPEN4_NOCREATE, fFileSystem->OpenOwner(),
fInfo.fNames->fNames.Head()->fName);
req.GetFH(); req.GetFH();
result = request.Send(); result = request.Send();
@@ -796,9 +795,7 @@ NFS4Inode::CreateObject(const char* name, const char* path, int mode,
Request request(serv, fFileSystem); Request request(serv, fFileSystem);
RequestBuilder& req = request.Builder(); RequestBuilder& req = request.Builder();
if (parent) (void)parent; // TODO: support named attributes
req.PutFH(fInfo.fParent);
else
req.PutFH(fInfo.fHandle); req.PutFH(fInfo.fHandle);
uint32 i = 0; uint32 i = 0;
@@ -19,7 +19,12 @@
FSLocation::~FSLocation() FSLocation::~FSLocation()
{ {
free(const_cast<char*>(fRootPath)); if (fRootPath != NULL) {
for (uint32 i = 0; fRootPath[i] != NULL; i++)
free(const_cast<char*>(fRootPath[i]));
}
delete[] fRootPath;
for (uint32 i = 0; i < fCount; i++) for (uint32 i = 0; i < fCount; i++)
free(const_cast<char*>(fLocations[i])); free(const_cast<char*>(fLocations[i]));
delete[] fLocations; delete[] fLocations;
@@ -28,7 +33,12 @@ FSLocation::~FSLocation()
FSLocations::~FSLocations() FSLocations::~FSLocations()
{ {
free(const_cast<char*>(fRootPath)); if (fRootPath != NULL) {
for (uint32 i = 0; fRootPath[i] != NULL; i++)
free(const_cast<char*>(fRootPath[i]));
}
delete[] fRootPath;
delete[] fLocations; delete[] fLocations;
} }
@@ -528,27 +538,29 @@ ReplyInterpreter::Write(uint32* size)
} }
const char* const char**
ReplyInterpreter::_FlattenPathname(XDR::ReadStream& stream) ReplyInterpreter::_GetPath(XDR::ReadStream& stream)
{ {
uint32 count = stream.GetUInt(); uint32 count = stream.GetUInt();
char* pathname = NULL; char** path = new char*[count + 1];
uint32 size = 0; if (path == NULL)
for (uint32 i = 0; i < count; i++) { return NULL;
const char* path = stream.GetString();
size += strlen(path) + 1;
if (pathname == NULL) {
pathname = reinterpret_cast<char*>(malloc(strlen(path) + 1));
pathname[0] = '\0';
} else {
*pathname++ = '/';
pathname = reinterpret_cast<char*>(realloc(pathname, size));
}
strcat(pathname, path);
free(const_cast<char*>(path));
}
return pathname; uint32 i;
for (i = 0; i < count; i++) {
path[i] = stream.GetString();
if (path[i] == NULL)
goto out;
}
path[count] = NULL;
return const_cast<const char**>(path);
out:
for (uint32 j = 0; j < i; j++)
free(path[i]);
delete[] path;
return NULL;
} }
@@ -662,11 +674,11 @@ ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs,
values[current].fAttribute = FATTR4_FS_LOCATIONS; values[current].fAttribute = FATTR4_FS_LOCATIONS;
FSLocations* locs = new FSLocations; FSLocations* locs = new FSLocations;
locs->fRootPath = _FlattenPathname(stream); locs->fRootPath = _GetPath(stream);
locs->fCount = stream.GetUInt(); locs->fCount = stream.GetUInt();
locs->fLocations = new FSLocation[locs->fCount]; locs->fLocations = new FSLocation[locs->fCount];
for (uint32 i = 0; i < locs->fCount; i++) { for (uint32 i = 0; i < locs->fCount; i++) {
locs->fLocations[i].fRootPath = _FlattenPathname(stream); locs->fLocations[i].fRootPath = _GetPath(stream);
locs->fLocations[i].fCount = stream.GetUInt(); locs->fLocations[i].fCount = stream.GetUInt();
locs->fLocations[i].fLocations locs->fLocations[i].fLocations
= new const char*[locs->fLocations[i].fCount]; = new const char*[locs->fLocations[i].fCount];
@@ -17,7 +17,8 @@
struct FSLocation { struct FSLocation {
const char* fRootPath; const char** fRootPath;
const char** fLocations; const char** fLocations;
uint32 fCount; uint32 fCount;
@@ -25,7 +26,8 @@ struct FSLocation {
}; };
struct FSLocations { struct FSLocations {
const char* fRootPath; const char** fRootPath;
FSLocation* fLocations; FSLocation* fLocations;
uint32 fCount; uint32 fCount;
@@ -108,7 +110,7 @@ public:
private: private:
void _ParseHeader(); void _ParseHeader();
static const char* _FlattenPathname(XDR::ReadStream& stream); static const char** _GetPath(XDR::ReadStream& stream);
status_t _DecodeAttrs(XDR::ReadStream& stream, AttrValue** attrs, status_t _DecodeAttrs(XDR::ReadStream& stream, AttrValue** attrs,
uint32* count); uint32* count);
@@ -27,15 +27,27 @@ void
VnodeToInode::Replace(Inode* newInode) VnodeToInode::Replace(Inode* newInode)
{ {
WriteLocker _(fLock); WriteLocker _(fLock);
if (fInode != NULL && !IsRoot()) { if (fInode != NULL && !IsRoot())
fInode->GetFileSystem()->InoIdMap()->MarkRemoved(fID);
delete fInode; delete fInode;
}
fInode = newInode; fInode = newInode;
if (fInode != NULL) { }
ASSERT(fFileSystem == fInode->GetFileSystem());
fInode->GetFileSystem()->InoIdMap()->AddEntry(fInode->fInfo, fID);
} bool
VnodeToInode::Unlink(InodeNames* parent, const char* name)
{
WriteLocker _(fLock);
if (fInode != NULL && !IsRoot()) {
bool removed = fInode->GetFileSystem()->InoIdMap()->RemoveName(fID,
parent, name);
if (removed) {
delete fInode;
fInode = NULL;
}
return removed;
}
return false;
} }
@@ -28,7 +28,7 @@ public:
Inode* Get(); Inode* Get();
void Replace(Inode* newInode); void Replace(Inode* newInode);
inline void Remove(); bool Unlink(InodeNames* parent, const char* name);
inline void Clear(); inline void Clear();
inline ino_t ID() const; inline ino_t ID() const;
@@ -72,7 +72,7 @@ VnodeToInode::VnodeToInode(ino_t id, FileSystem* fileSystem)
inline inline
VnodeToInode::~VnodeToInode() VnodeToInode::~VnodeToInode()
{ {
Remove(); Replace(NULL);
if (fFileSystem != NULL && !IsRoot()) if (fFileSystem != NULL && !IsRoot())
fFileSystem->InoIdMap()->RemoveEntry(fID); fFileSystem->InoIdMap()->RemoveEntry(fID);
rw_lock_destroy(&fLock); rw_lock_destroy(&fLock);
@@ -93,13 +93,6 @@ VnodeToInode::Unlock()
} }
inline void
VnodeToInode::Remove()
{
Replace(NULL);
}
inline void inline void
VnodeToInode::Clear() VnodeToInode::Clear()
{ {
@@ -321,23 +321,6 @@ nfs4_lookup(fs_volume* volume, fs_vnode* dir, const char* name, ino_t* _id)
} }
static status_t
nfs4_get_vnode_name(fs_volume* volume, fs_vnode* vnode, char* buffer,
size_t bufferSize)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
strncpy(buffer, inode->Name(), bufferSize);
return B_OK;
}
static status_t static status_t
nfs4_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) nfs4_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
{ {
@@ -606,11 +589,12 @@ nfs4_unlink(fs_volume* volume, fs_vnode* dir, const char* name)
if (result == B_OK) { if (result == B_OK) {
result = get_vnode(volume, id, reinterpret_cast<void**>(&vti)); result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
ASSERT(result == B_OK); ASSERT(result == B_OK);
vti->Remove();
put_vnode(volume, id); if (vti->Unlink(inode->fInfo.fNames, name))
remove_vnode(volume, id); remove_vnode(volume, id);
put_vnode(volume, id); put_vnode(volume, id);
put_vnode(volume, id);
} }
return B_OK; return B_OK;
@@ -654,9 +638,10 @@ nfs4_rename(fs_volume* volume, fs_vnode* fromDir, const char* fromName,
if (result == B_OK) { if (result == B_OK) {
result = get_vnode(volume, oldID, reinterpret_cast<void**>(&vti)); result = get_vnode(volume, oldID, reinterpret_cast<void**>(&vti));
ASSERT(result == B_OK); ASSERT(result == B_OK);
vti->Remove(); if (vti->Unlink(toInode->fInfo.fNames, toName))
put_vnode(volume, oldID);
remove_vnode(volume, oldID); remove_vnode(volume, oldID);
put_vnode(volume, oldID);
put_vnode(volume, oldID); put_vnode(volume, oldID);
} }
} }
@@ -670,8 +655,8 @@ nfs4_rename(fs_volume* volume, fs_vnode* fromDir, const char* fromName,
} }
unremove_vnode(volume, id); unremove_vnode(volume, id);
child->fInfo.fParent = toInode->fInfo.fHandle; child->fInfo.fNames->RemoveName(fromInode->fInfo.fNames, fromName);
child->fInfo.CreateName(toInode->fInfo.fPath, toName); child->fInfo.fNames->AddName(toInode->fInfo.fNames, toName);
put_vnode(volume, id); put_vnode(volume, id);
} }
@@ -998,12 +983,14 @@ nfs4_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name)
result = acquire_vnode(volume, id); result = acquire_vnode(volume, id);
if (result == B_OK) { if (result == B_OK) {
ASSERT(get_vnode(volume, id, reinterpret_cast<void**>(&vti)) == B_OK); result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
vti->Remove(); ASSERT(result == B_OK);
put_vnode(volume, id);
if (vti->Unlink(inode->fInfo.fNames, name))
remove_vnode(volume, id); remove_vnode(volume, id);
put_vnode(volume, id); put_vnode(volume, id);
put_vnode(volume, id);
} }
return B_OK; return B_OK;
@@ -1444,7 +1431,7 @@ fs_volume_ops gNFSv4VolumeOps = {
fs_vnode_ops gNFSv4VnodeOps = { fs_vnode_ops gNFSv4VnodeOps = {
nfs4_lookup, nfs4_lookup,
nfs4_get_vnode_name, NULL, // get_vnode_name()
nfs4_put_vnode, nfs4_put_vnode,
nfs4_remove_vnode, nfs4_remove_vnode,