* Split the Node references and referrers concepts. One can now have a

reference without being a referring entry.
* Reworked entry renaming. We do now remove the original entry first and
  then create a target entry. Renaming directories didn't work before since
  there would temporarily have been two links to a directory, which is not
  allowed. Replacing an entry was also broken: The original entry was not
  removed. Due to reversing the entry creation/deletion order we also do no
  longer suffer from the Tracker bug, that entries would disappear from
  queries when being renamed.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20204 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2007-02-23 01:01:26 +00:00
parent 0d427dec46
commit e696418178
4 changed files with 78 additions and 38 deletions

View File

@ -32,7 +32,7 @@ Directory::~Directory()
status_t
Directory::Link(Entry *entry)
{
if (GetRefCount() == 0)
if (fReferrers.IsEmpty())
return Node::Link(entry);
return B_IS_A_DIRECTORY;
}

View File

@ -80,23 +80,44 @@ Node::InitCheck() const
return (fVolume && fID >= 0 ? B_OK : B_NO_INIT);
}
// AddReference
status_t
Node::AddReference()
{
if (++fRefCount == 1) {
status_t error = GetVolume()->NewNode(this);
if (error != B_OK) {
fRefCount--;
return error;
}
fIsKnownToVFS = true;
}
return B_OK;
}
// RemoveReference
void
Node::RemoveReference()
{
if (--fRefCount == 0) {
GetVolume()->RemoveNode(this);
fRefCount++;
}
}
// Link
status_t
Node::Link(Entry *entry)
{
PRINT(("Node[%Ld]::Link(): %ld ->...\n", fID, fRefCount));
status_t error = B_OK;
fReferrers.Insert(entry);
if (++fRefCount == 1) {
// error = GetVolume()->NodeAdded(this);
error = GetVolume()->NewNode(this);
if (error == B_OK)
fIsKnownToVFS = true;
else {
fRefCount--;
fReferrers.Remove(entry);
}
}
status_t error = AddReference();
if (error != B_OK)
fReferrers.Remove(entry);
return error;
}
@ -105,16 +126,10 @@ status_t
Node::Unlink(Entry *entry)
{
PRINT(("Node[%Ld]::Unlink(): %ld ->...\n", fID, fRefCount));
status_t error = B_OK;
if (--fRefCount == 0) {
// error = GetVolume()->NodeRemoved(this);
error = GetVolume()->RemoveNode(this);
if (error != B_OK)
fRefCount++;
}
if (error == B_OK)
fReferrers.Remove(entry);
return error;
RemoveReference();
fReferrers.Remove(entry);
return B_OK;
}
// SetMTime

View File

@ -41,9 +41,12 @@ public:
inline vnode_id GetID() const { return fID; }
status_t AddReference();
void RemoveReference();
int32 GetRefCount() { return fRefCount; }
virtual status_t Link(Entry *entry);
virtual status_t Unlink(Entry *entry);
int32 GetRefCount() { return fRefCount; }
inline bool IsDirectory() const { return S_ISDIR(fMode); }
inline bool IsFile() const { return S_ISREG(fMode); }

View File

@ -873,15 +873,18 @@ ramfs_rename(void *ns, void *_oldDir, const char *oldName,
Directory *oldDir = dynamic_cast<Directory*>((Node*)_oldDir);
Directory *newDir = dynamic_cast<Directory*>((Node*)_newDir);
status_t error = B_OK;
// check name
if (!oldName || *oldName == '\0'
|| !strcmp(oldName, ".") || !strcmp(oldName, "..")
|| !newName || *newName == '\0'
|| !strcmp(newName, ".") || !strcmp(newName, "..")) {
SET_ERROR(error, B_BAD_VALUE);
// check nodes
} else if (!oldDir || !newDir) {
SET_ERROR(error, B_BAD_VALUE);
// check if the entry isn't actually moved or renamed
} else if (oldDir == newDir && !strcmp(oldName, newName)) {
SET_ERROR(error, B_BAD_VALUE);
@ -890,14 +893,17 @@ FUNCTION(("old dir: %Ld, old name: `%s', new dir: %Ld, new name: `%s'\n",
oldDir->GetID(), oldName, newDir->GetID(), newName));
NodeMTimeUpdater mTimeUpdater1(oldDir);
NodeMTimeUpdater mTimeUpdater2(newDir);
// target directory deleted?
if (is_vnode_removed(volume->GetID(), newDir->GetID()) > 0)
SET_ERROR(error, B_NOT_ALLOWED);
// check directory write permissions
if (error == B_OK)
error = oldDir->CheckPermissions(ACCESS_W);
if (error == B_OK)
error = newDir->CheckPermissions(ACCESS_W);
Node *node = NULL;
Entry *entry = NULL;
if (error == B_OK) {
@ -919,6 +925,7 @@ oldDir->GetID(), oldName, newDir->GetID(), newName));
}
}
}
// check the target directory situation
Node *clobberNode = NULL;
Entry *clobberEntry = NULL;
@ -931,33 +938,47 @@ oldDir->GetID(), oldName, newDir->GetID(), newName));
}
}
}
// do the job
if (error == B_OK) {
if (clobberEntry) {
error = clobberEntry->Link(node);
// notify listeners
// temporarily acquire an additional reference to make
// sure the node isn't deleted when we remove the entry
error = node->AddReference();
if (error == B_OK) {
// delete the original entry
error = oldDir->DeleteEntry(entry);
if (error == B_OK) {
notify_listener(B_ENTRY_REMOVED,
volume->GetID(), newDir->GetID(), 0,
clobberNode->GetID(), NULL);
}
} else {
Entry *newEntry = NULL;
// TODO: If the node is a directory, this does fail!!!
error = newDir->CreateEntry(node, newName, &newEntry);
if (error == B_OK) {
error = oldDir->DeleteEntry(entry);
if (error != B_OK)
newDir->DeleteEntry(newEntry);
// create the new one/relink the target entry
if (clobberEntry)
error = clobberEntry->Link(node);
else
error = newDir->CreateEntry(node, newName);
if (error == B_OK) {
// send a "removed" notification for the clobbered
// entry
if (clobberEntry) {
notify_listener(B_ENTRY_REMOVED,
volume->GetID(), newDir->GetID(), 0,
clobberNode->GetID(), newName);
}
} else {
// try to recreate the original entry, in case of
// failure
newDir->CreateEntry(node, oldName);
}
}
node->RemoveReference();
}
}
// release the entries
if (clobberEntry)
volume->PutNode(clobberNode);
if (entry)
volume->PutNode(node);
}
// notify listeners
if (error == B_OK) {
notify_listener(B_ENTRY_MOVED, volume->GetID(), oldDir->GetID(),
@ -965,6 +986,7 @@ oldDir->GetID(), oldName, newDir->GetID(), newName));
}
} else
SET_ERROR(error, B_ERROR);
RETURN_ERROR(error);
}