* 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:
@@ -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); }
|
||||||
|
|||||||
+37
-15
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user