* 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:
parent
0d427dec46
commit
e696418178
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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); }
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user