* 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
@@ -32,7 +32,7 @@ Directory::~Directory()
status_t status_t
Directory::Link(Entry *entry) Directory::Link(Entry *entry)
{ {
if (GetRefCount() == 0) if (fReferrers.IsEmpty())
return Node::Link(entry); return Node::Link(entry);
return B_IS_A_DIRECTORY; return B_IS_A_DIRECTORY;
} }
@@ -80,23 +80,44 @@ Node::InitCheck() const
return (fVolume && fID >= 0 ? B_OK : B_NO_INIT); 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 // Link
status_t status_t
Node::Link(Entry *entry) Node::Link(Entry *entry)
{ {
PRINT(("Node[%Ld]::Link(): %ld ->...\n", fID, fRefCount)); PRINT(("Node[%Ld]::Link(): %ld ->...\n", fID, fRefCount));
status_t error = B_OK;
fReferrers.Insert(entry); fReferrers.Insert(entry);
if (++fRefCount == 1) {
// error = GetVolume()->NodeAdded(this); status_t error = AddReference();
error = GetVolume()->NewNode(this); if (error != B_OK)
if (error == B_OK) fReferrers.Remove(entry);
fIsKnownToVFS = true;
else {
fRefCount--;
fReferrers.Remove(entry);
}
}
return error; return error;
} }
@@ -105,16 +126,10 @@ status_t
Node::Unlink(Entry *entry) Node::Unlink(Entry *entry)
{ {
PRINT(("Node[%Ld]::Unlink(): %ld ->...\n", fID, fRefCount)); PRINT(("Node[%Ld]::Unlink(): %ld ->...\n", fID, fRefCount));
status_t error = B_OK; RemoveReference();
if (--fRefCount == 0) { fReferrers.Remove(entry);
// error = GetVolume()->NodeRemoved(this);
error = GetVolume()->RemoveNode(this); return B_OK;
if (error != B_OK)
fRefCount++;
}
if (error == B_OK)
fReferrers.Remove(entry);
return error;
} }
// SetMTime // SetMTime
@@ -41,9 +41,12 @@ public:
inline vnode_id GetID() const { return fID; } 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 Link(Entry *entry);
virtual status_t Unlink(Entry *entry); virtual status_t Unlink(Entry *entry);
int32 GetRefCount() { return fRefCount; }
inline bool IsDirectory() const { return S_ISDIR(fMode); } inline bool IsDirectory() const { return S_ISDIR(fMode); }
inline bool IsFile() const { return S_ISREG(fMode); } inline bool IsFile() const { return S_ISREG(fMode); }
@@ -873,15 +873,18 @@ ramfs_rename(void *ns, void *_oldDir, const char *oldName,
Directory *oldDir = dynamic_cast<Directory*>((Node*)_oldDir); Directory *oldDir = dynamic_cast<Directory*>((Node*)_oldDir);
Directory *newDir = dynamic_cast<Directory*>((Node*)_newDir); Directory *newDir = dynamic_cast<Directory*>((Node*)_newDir);
status_t error = B_OK; status_t error = B_OK;
// check name // check name
if (!oldName || *oldName == '\0' if (!oldName || *oldName == '\0'
|| !strcmp(oldName, ".") || !strcmp(oldName, "..") || !strcmp(oldName, ".") || !strcmp(oldName, "..")
|| !newName || *newName == '\0' || !newName || *newName == '\0'
|| !strcmp(newName, ".") || !strcmp(newName, "..")) { || !strcmp(newName, ".") || !strcmp(newName, "..")) {
SET_ERROR(error, B_BAD_VALUE); SET_ERROR(error, B_BAD_VALUE);
// check nodes // check nodes
} else if (!oldDir || !newDir) { } else if (!oldDir || !newDir) {
SET_ERROR(error, B_BAD_VALUE); SET_ERROR(error, B_BAD_VALUE);
// check if the entry isn't actually moved or renamed // check if the entry isn't actually moved or renamed
} else if (oldDir == newDir && !strcmp(oldName, newName)) { } else if (oldDir == newDir && !strcmp(oldName, newName)) {
SET_ERROR(error, B_BAD_VALUE); 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)); oldDir->GetID(), oldName, newDir->GetID(), newName));
NodeMTimeUpdater mTimeUpdater1(oldDir); NodeMTimeUpdater mTimeUpdater1(oldDir);
NodeMTimeUpdater mTimeUpdater2(newDir); NodeMTimeUpdater mTimeUpdater2(newDir);
// target directory deleted? // target directory deleted?
if (is_vnode_removed(volume->GetID(), newDir->GetID()) > 0) if (is_vnode_removed(volume->GetID(), newDir->GetID()) > 0)
SET_ERROR(error, B_NOT_ALLOWED); SET_ERROR(error, B_NOT_ALLOWED);
// check directory write permissions // check directory write permissions
if (error == B_OK) if (error == B_OK)
error = oldDir->CheckPermissions(ACCESS_W); error = oldDir->CheckPermissions(ACCESS_W);
if (error == B_OK) if (error == B_OK)
error = newDir->CheckPermissions(ACCESS_W); error = newDir->CheckPermissions(ACCESS_W);
Node *node = NULL; Node *node = NULL;
Entry *entry = NULL; Entry *entry = NULL;
if (error == B_OK) { if (error == B_OK) {
@@ -919,6 +925,7 @@ oldDir->GetID(), oldName, newDir->GetID(), newName));
} }
} }
} }
// check the target directory situation // check the target directory situation
Node *clobberNode = NULL; Node *clobberNode = NULL;
Entry *clobberEntry = NULL; Entry *clobberEntry = NULL;
@@ -931,33 +938,47 @@ oldDir->GetID(), oldName, newDir->GetID(), newName));
} }
} }
} }
// do the job // do the job
if (error == B_OK) { if (error == B_OK) {
if (clobberEntry) { // temporarily acquire an additional reference to make
error = clobberEntry->Link(node); // sure the node isn't deleted when we remove the entry
// notify listeners error = node->AddReference();
if (error == B_OK) {
// delete the original entry
error = oldDir->DeleteEntry(entry);
if (error == B_OK) { if (error == B_OK) {
notify_listener(B_ENTRY_REMOVED, // create the new one/relink the target entry
volume->GetID(), newDir->GetID(), 0, if (clobberEntry)
clobberNode->GetID(), NULL); error = clobberEntry->Link(node);
} else
} else { error = newDir->CreateEntry(node, newName);
Entry *newEntry = NULL;
// TODO: If the node is a directory, this does fail!!! if (error == B_OK) {
error = newDir->CreateEntry(node, newName, &newEntry); // send a "removed" notification for the clobbered
if (error == B_OK) { // entry
error = oldDir->DeleteEntry(entry); if (clobberEntry) {
if (error != B_OK) notify_listener(B_ENTRY_REMOVED,
newDir->DeleteEntry(newEntry); 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 // release the entries
if (clobberEntry) if (clobberEntry)
volume->PutNode(clobberNode); volume->PutNode(clobberNode);
if (entry) if (entry)
volume->PutNode(node); volume->PutNode(node);
} }
// notify listeners // notify listeners
if (error == B_OK) { if (error == B_OK) {
notify_listener(B_ENTRY_MOVED, volume->GetID(), oldDir->GetID(), notify_listener(B_ENTRY_MOVED, volume->GetID(), oldDir->GetID(),
@@ -965,6 +986,7 @@ oldDir->GetID(), oldName, newDir->GetID(), newName));
} }
} else } else
SET_ERROR(error, B_ERROR); SET_ERROR(error, B_ERROR);
RETURN_ERROR(error); RETURN_ERROR(error);
} }