Fix reading attributes of directories

When reading an attribute of a directory there was no guarantee that the
underlying package would be open. When it wasn't reading an attribute
would fail, unless the attribute data were already cached. The reasons
for this are:
* UnpackingDirectory didn't forward the {Init,Uninit}VFS() calls to the
  underlying PackageDirectory.
* Only PackageFile was actually opening the package in InitVFS().

Now we forward the {Init,Uninit}VFS() calls in all cases -- even in
{Add,Remove}PackageNode(), when the active package node changes -- and
opening/closing the package is now done in
PackageNode::{Init,Uninit}VFS().
This commit is contained in:
Ingo Weinhold 2013-06-01 00:31:37 +02:00
parent fa962e6008
commit 118028674c
10 changed files with 165 additions and 38 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef NODE_H
@ -27,10 +27,11 @@ class Directory;
class PackageNode;
// node flags
// internal node flags
enum {
NODE_FLAG_KNOWN_TO_VFS = 0x01
// internal flag
NODE_FLAG_KNOWN_TO_VFS = 0x01,
NODE_FLAG_VFS_INIT_ERROR = 0x02,
// used by subclasses
};
@ -61,6 +62,7 @@ public:
virtual void VFSUninit();
// base class version must be called
inline bool IsKnownToVFS() const;
inline bool HasVFSInitError() const;
void SetID(ino_t id);
void SetParent(Directory* parent);
@ -133,6 +135,13 @@ Node::IsKnownToVFS() const
}
bool
Node::HasVFSInitError() const
{
return (fFlags & NODE_FLAG_VFS_INIT_ERROR) != 0;
}
// #pragma mark -

View File

@ -1,5 +1,5 @@
/*
* Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
@ -27,6 +27,25 @@ UnpackingDirectory::~UnpackingDirectory()
}
status_t
UnpackingDirectory::VFSInit(dev_t deviceID)
{
status_t error = NodeInitVFS(deviceID, fID, fPackageDirectories.Head());
if (error == B_OK)
Directory::VFSInit(deviceID);
return error;
}
void
UnpackingDirectory::VFSUninit()
{
NodeUninitVFS(fPackageDirectories.Head(), fFlags);
Directory::VFSUninit();
}
mode_t
UnpackingDirectory::Mode() const
{
@ -80,7 +99,7 @@ UnpackingDirectory::GetNode()
status_t
UnpackingDirectory::AddPackageNode(PackageNode* packageNode)
UnpackingDirectory::AddPackageNode(PackageNode* packageNode, dev_t deviceID)
{
if (!S_ISDIR(packageNode->Mode()))
return B_BAD_VALUE;
@ -92,9 +111,10 @@ UnpackingDirectory::AddPackageNode(PackageNode* packageNode)
bool isNewest = other == NULL
|| packageDirectory->ModifiedTime() > other->ModifiedTime();
if (isNewest)
if (isNewest) {
fPackageDirectories.Insert(other, packageDirectory);
else
NodeReinitVFS(deviceID, fID, packageDirectory, other, fFlags);
} else
fPackageDirectories.Add(packageDirectory);
return B_OK;
@ -102,7 +122,7 @@ UnpackingDirectory::AddPackageNode(PackageNode* packageNode)
void
UnpackingDirectory::RemovePackageNode(PackageNode* packageNode)
UnpackingDirectory::RemovePackageNode(PackageNode* packageNode, dev_t deviceID)
{
bool isNewest = packageNode == fPackageDirectories.Head();
fPackageDirectories.Remove(dynamic_cast<PackageDirectory*>(packageNode));
@ -121,6 +141,7 @@ UnpackingDirectory::RemovePackageNode(PackageNode* packageNode)
fPackageDirectories.Remove(newestNode);
fPackageDirectories.Insert(fPackageDirectories.Head(), newestNode);
NodeReinitVFS(deviceID, fID, newestNode, packageNode, fFlags);
}
}
@ -164,6 +185,9 @@ UnpackingDirectory::PrepareForRemoval()
status_t
UnpackingDirectory::OpenAttributeDirectory(AttributeDirectoryCookie*& _cookie)
{
if (HasVFSInitError())
return B_ERROR;
return UnpackingAttributeDirectoryCookie::Open(fPackageDirectories.Head(),
_cookie);
}
@ -173,6 +197,9 @@ status_t
UnpackingDirectory::OpenAttribute(const StringKey& name, int openMode,
AttributeCookie*& _cookie)
{
if (HasVFSInitError())
return B_ERROR;
return UnpackingAttributeCookie::Open(fPackageDirectories.Head(), name,
openMode, _cookie);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef UNPACKING_DIRECTORY_H
@ -16,6 +16,9 @@ public:
UnpackingDirectory(ino_t id);
virtual ~UnpackingDirectory();
virtual status_t VFSInit(dev_t deviceID);
virtual void VFSUninit();
virtual mode_t Mode() const;
virtual uid_t UserID() const;
virtual gid_t GroupID() const;
@ -24,8 +27,10 @@ public:
virtual Node* GetNode();
virtual status_t AddPackageNode(PackageNode* packageNode);
virtual void RemovePackageNode(PackageNode* packageNode);
virtual status_t AddPackageNode(PackageNode* packageNode,
dev_t deviceID);
virtual void RemovePackageNode(PackageNode* packageNode,
dev_t deviceID);
virtual PackageNode* GetPackageNode();
virtual bool IsOnlyPackageNode(PackageNode* node) const;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
@ -34,10 +34,7 @@ UnpackingLeafNode::~UnpackingLeafNode()
status_t
UnpackingLeafNode::VFSInit(dev_t deviceID)
{
status_t error = B_OK;
if (PackageLeafNode* packageNode = _ActivePackageNode())
error = packageNode->VFSInit(deviceID, fID);
status_t error = NodeInitVFS(deviceID, fID, _ActivePackageNode());
if (error == B_OK)
Node::VFSInit(deviceID);
@ -48,9 +45,7 @@ UnpackingLeafNode::VFSInit(dev_t deviceID)
void
UnpackingLeafNode::VFSUninit()
{
if (PackageLeafNode* packageNode = _ActivePackageNode())
packageNode->VFSUninit();
NodeUninitVFS(_ActivePackageNode(), fFlags);
Node::VFSUninit();
}
@ -110,7 +105,7 @@ UnpackingLeafNode::GetNode()
status_t
UnpackingLeafNode::AddPackageNode(PackageNode* packageNode)
UnpackingLeafNode::AddPackageNode(PackageNode* packageNode, dev_t deviceID)
{
ASSERT(fFinalPackageNode == NULL);
@ -126,6 +121,7 @@ UnpackingLeafNode::AddPackageNode(PackageNode* packageNode)
if (isNewest) {
fPackageNodes.Add(packageLeafNode);
NodeReinitVFS(deviceID, fID, packageLeafNode, headNode, fFlags);
} else {
// add after the head
fPackageNodes.RemoveHead();
@ -138,7 +134,7 @@ UnpackingLeafNode::AddPackageNode(PackageNode* packageNode)
void
UnpackingLeafNode::RemovePackageNode(PackageNode* packageNode)
UnpackingLeafNode::RemovePackageNode(PackageNode* packageNode, dev_t deviceID)
{
ASSERT(fFinalPackageNode == NULL);
@ -160,6 +156,7 @@ UnpackingLeafNode::RemovePackageNode(PackageNode* packageNode)
// re-add the newest node to the head
fPackageNodes.Remove(newestNode);
fPackageNodes.Add(newestNode);
NodeReinitVFS(deviceID, fID, newestNode, packageNode, fFlags);
}
}
@ -238,6 +235,9 @@ UnpackingLeafNode::CloneTransferPackageNodes(ino_t id, UnpackingNode*& _newNode)
status_t
UnpackingLeafNode::Read(off_t offset, void* buffer, size_t* bufferSize)
{
if (HasVFSInitError())
return B_ERROR;
if (PackageLeafNode* packageNode = _ActivePackageNode())
return packageNode->Read(offset, buffer, bufferSize);
return B_ERROR;
@ -247,6 +247,9 @@ UnpackingLeafNode::Read(off_t offset, void* buffer, size_t* bufferSize)
status_t
UnpackingLeafNode::Read(io_request* request)
{
if (HasVFSInitError())
return B_ERROR;
if (PackageLeafNode* packageNode = _ActivePackageNode())
return packageNode->Read(request);
return EBADF;
@ -256,6 +259,9 @@ UnpackingLeafNode::Read(io_request* request)
status_t
UnpackingLeafNode::ReadSymlink(void* buffer, size_t* bufferSize)
{
if (HasVFSInitError())
return B_ERROR;
PackageLeafNode* packageNode = _ActivePackageNode();
if (packageNode == NULL)
return B_BAD_VALUE;
@ -277,6 +283,9 @@ UnpackingLeafNode::ReadSymlink(void* buffer, size_t* bufferSize)
status_t
UnpackingLeafNode::OpenAttributeDirectory(AttributeDirectoryCookie*& _cookie)
{
if (HasVFSInitError())
return B_ERROR;
return UnpackingAttributeDirectoryCookie::Open(_ActivePackageNode(),
_cookie);
}
@ -286,6 +295,9 @@ status_t
UnpackingLeafNode::OpenAttribute(const StringKey& name, int openMode,
AttributeCookie*& _cookie)
{
if (HasVFSInitError())
return B_ERROR;
return UnpackingAttributeCookie::Open(_ActivePackageNode(), name, openMode,
_cookie);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef LEAF_NODE_H
@ -27,8 +27,10 @@ public:
virtual Node* GetNode();
virtual status_t AddPackageNode(PackageNode* packageNode);
virtual void RemovePackageNode(PackageNode* packageNode);
virtual status_t AddPackageNode(PackageNode* packageNode,
dev_t deviceID);
virtual void RemovePackageNode(PackageNode* packageNode,
dev_t deviceID);
virtual PackageNode* GetPackageNode();
virtual bool IsOnlyPackageNode(PackageNode* node) const;

View File

@ -1,11 +1,15 @@
/*
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2011-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "UnpackingNode.h"
#include "DebugSupport.h"
#include "Node.h"
#include "PackageNode.h"
UnpackingNode::~UnpackingNode()
{
@ -17,3 +21,54 @@ UnpackingNode::CloneTransferPackageNodes(ino_t id, UnpackingNode*& _newNode)
{
return B_BAD_VALUE;
}
status_t
UnpackingNode::NodeInitVFS(dev_t deviceID, ino_t nodeID,
PackageNode* packageNode)
{
status_t error = B_OK;
if (packageNode != NULL)
error = packageNode->VFSInit(deviceID, nodeID);
return error;
}
void
UnpackingNode::NodeUninitVFS(PackageNode* packageNode, uint32& nodeFlags)
{
if (packageNode != NULL) {
if ((nodeFlags & NODE_FLAG_VFS_INIT_ERROR) == 0)
packageNode->VFSUninit();
else
nodeFlags &= ~(uint32)NODE_FLAG_VFS_INIT_ERROR;
}
}
void
UnpackingNode::NodeReinitVFS(dev_t deviceID, ino_t nodeID,
PackageNode* packageNode, PackageNode* previousPackageNode,
uint32& nodeFlags)
{
if ((nodeFlags & NODE_FLAG_KNOWN_TO_VFS) == 0)
return;
if (packageNode != previousPackageNode) {
bool hadInitError = (nodeFlags & NODE_FLAG_VFS_INIT_ERROR) != 0;
nodeFlags &= ~(uint32)NODE_FLAG_VFS_INIT_ERROR;
if (packageNode != NULL) {
status_t error = packageNode->VFSInit(deviceID, nodeID);
if (error != B_OK) {
ERROR("UnpackingNode::NodeReinitVFS(): VFSInit() failed for "
"(%" B_PRIdDEV ", %" B_PRIdINO ")\n", deviceID, nodeID);
nodeFlags |= NODE_FLAG_VFS_INIT_ERROR;
}
}
if (previousPackageNode != NULL && !hadInitError)
previousPackageNode->VFSUninit();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2011-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef UNPACKING_NODE_H
@ -19,8 +19,10 @@ public:
virtual Node* GetNode() = 0;
virtual status_t AddPackageNode(PackageNode* packageNode) = 0;
virtual void RemovePackageNode(PackageNode* packageNode) = 0;
virtual status_t AddPackageNode(PackageNode* packageNode,
dev_t deviceID) = 0;
virtual void RemovePackageNode(PackageNode* packageNode,
dev_t deviceID) = 0;
virtual PackageNode* GetPackageNode() = 0;
virtual bool IsOnlyPackageNode(PackageNode* node) const = 0;
@ -30,6 +32,16 @@ public:
virtual void PrepareForRemoval() = 0;
virtual status_t CloneTransferPackageNodes(ino_t id,
UnpackingNode*& _newNode);
protected:
status_t NodeInitVFS(dev_t deviceID, ino_t nodeID,
PackageNode* packageNode);
void NodeUninitVFS(PackageNode* packageNode,
uint32& nodeFlags);
void NodeReinitVFS(dev_t deviceID, ino_t nodeID,
PackageNode* packageNode,
PackageNode* previousPackageNode,
uint32& nodeFlags);
};

View File

@ -1,5 +1,5 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
@ -150,7 +150,8 @@ PackageFile::VFSInit(dev_t deviceID, ino_t nodeID)
MethodDeleter<PackageNode> baseClassUninit(this,
&PackageNode::NonVirtualVFSUninit);
// open the package
// open the package -- that's already done by PackageNode::VFSInit(), so it
// shouldn't fail here. We only need to do it again, since we need the FD.
int fd = fPackage->Open();
if (fd < 0)
RETURN_ERROR(fd);
@ -168,7 +169,6 @@ PackageFile::VFSInit(dev_t deviceID, ino_t nodeID)
return error;
}
packageCloser.Detach();
baseClassUninit.Detach();
return B_OK;
}
@ -178,7 +178,6 @@ void
PackageFile::VFSUninit()
{
if (fDataAccessor != NULL) {
fPackage->Close();
delete fDataAccessor;
fDataAccessor = NULL;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
@ -44,6 +44,11 @@ PackageNode::Init(PackageDirectory* parent, const String& name)
status_t
PackageNode::VFSInit(dev_t deviceID, ino_t nodeID)
{
// open the package
int fd = fPackage->Open();
if (fd < 0)
RETURN_ERROR(fd);
fPackage->AcquireReference();
return B_OK;
}
@ -52,6 +57,7 @@ PackageNode::VFSInit(dev_t deviceID, ino_t nodeID)
void
PackageNode::VFSUninit()
{
fPackage->Close();
fPackage->ReleaseReference();
}

View File

@ -1192,7 +1192,7 @@ Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode,
newNode = true;
}
status_t error = unpackingNode->AddPackageNode(packageNode);
status_t error = unpackingNode->AddPackageNode(packageNode, ID());
if (error != B_OK) {
// Remove the node, if created before. If the node was created to
// replace the previous node, send out notifications instead.
@ -1274,7 +1274,7 @@ Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode,
// with a completely new one and let the old one die. This is necessary
// to avoid surprises for clients that have opened/mapped the node.
if (S_ISDIR(packageNode->Mode())) {
unpackingNode->RemovePackageNode(packageNode);
unpackingNode->RemovePackageNode(packageNode, ID());
_NotifyNodeChanged(node, kAllStatFields,
OldUnpackingNodeAttributes(headPackageNode));
} else {
@ -1284,7 +1284,7 @@ Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode,
fNextNodeID++, newUnpackingNode);
if (error == B_OK) {
// remove the package node
newUnpackingNode->RemovePackageNode(packageNode);
newUnpackingNode->RemovePackageNode(packageNode, ID());
// remove the old node
_NotifyNodeRemoved(node);
@ -1311,7 +1311,7 @@ Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode,
} else {
// The package node to remove is not the head of the node. This change
// doesn't have any visible effect.
unpackingNode->RemovePackageNode(packageNode);
unpackingNode->RemovePackageNode(packageNode, ID());
}
if (!notify)