* Package: Added Open() (and matching Close()) method, which opens the package

file once, tracking an open count.
* Added VFSInit()/VFSUninit() methods to the Node and PackageNode class
  hierarchies, called by the {get,put}_vnode() PackageFile implements them to
  set up/tear down access to the file data. Also added a Read() reading the
  data.
* Implemented the open(), free_cookie(), and read() FS hooks for real. Reading
  files works now. Executing doesn't yet -- it requires working with
  IORequests in a way not supported by the userlandfs.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34102 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-11-17 22:04:46 +00:00
parent 37fca3d23f
commit 744a460c12
19 changed files with 476 additions and 69 deletions
@@ -38,6 +38,19 @@ Directory::Init(Directory* parent, const char* name)
}
status_t
Directory::VFSInit(dev_t deviceID)
{
return B_OK;
}
void
Directory::VFSUninit()
{
}
mode_t
Directory::Mode() const
{
@@ -98,6 +111,13 @@ Directory::AddPackageNode(PackageNode* packageNode)
}
status_t
Directory::Read(off_t offset, void* buffer, size_t* bufferSize)
{
return B_IS_A_DIRECTORY;
}
void
Directory::AddChild(Node* node)
{
@@ -31,6 +31,9 @@ public:
virtual status_t Init(Directory* parent, const char* name);
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;
@@ -39,6 +42,9 @@ public:
virtual status_t AddPackageNode(PackageNode* packageNode);
virtual status_t Read(off_t offset, void* buffer,
size_t* bufferSize);
void AddChild(Node* node);
void RemoveChild(Node* node);
Node* FindChild(const char* name);
@@ -26,6 +26,23 @@ LeafNode::Init(Directory* parent, const char* name)
}
status_t
LeafNode::VFSInit(dev_t deviceID)
{
if (PackageLeafNode* packageNode = fPackageNodes.Head())
return packageNode->VFSInit(deviceID, fID);
return B_OK;
}
void
LeafNode::VFSUninit()
{
if (PackageLeafNode* packageNode = fPackageNodes.Head())
packageNode->VFSUninit();
}
mode_t
LeafNode::Mode() const
{
@@ -85,6 +102,15 @@ LeafNode::AddPackageNode(PackageNode* packageNode)
}
status_t
LeafNode::Read(off_t offset, void* buffer, size_t* bufferSize)
{
if (PackageLeafNode* packageNode = fPackageNodes.Head())
return packageNode->Read(offset, buffer, bufferSize);
return B_ERROR;
}
const char*
LeafNode::SymlinkPath() const
{
@@ -17,6 +17,9 @@ public:
virtual status_t Init(Directory* parent, const char* name);
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;
@@ -25,6 +28,9 @@ public:
virtual status_t AddPackageNode(PackageNode* packageNode);
virtual status_t Read(off_t offset, void* buffer,
size_t* bufferSize);
const char* SymlinkPath() const;
private:
@@ -42,6 +42,9 @@ public:
virtual status_t Init(Directory* parent, const char* name);
virtual status_t VFSInit(dev_t deviceID) = 0;
virtual void VFSUninit() = 0;
virtual mode_t Mode() const = 0;
virtual uid_t UserID() const = 0;
virtual gid_t GroupID() const = 0;
@@ -50,6 +53,9 @@ public:
virtual status_t AddPackageNode(PackageNode* packageNode) = 0;
virtual status_t Read(off_t offset, void* buffer,
size_t* bufferSize) = 0;
protected:
rw_lock fLock;
ino_t fID;
@@ -6,19 +6,28 @@
#include "Package.h"
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <util/AutoLock.h>
#include "DebugSupport.h"
#include "PackageDomain.h"
Package::Package(PackageDomain* domain, dev_t deviceID, ino_t nodeID)
:
fDomain(domain),
fName(NULL),
fFD(-1),
fOpenCount(0),
fNodeID(nodeID),
fDeviceID(deviceID)
{
mutex_init(&fLock, "packagefs package");
}
@@ -28,6 +37,8 @@ Package::~Package()
delete node;
free(fName);
mutex_destroy(&fLock);
}
@@ -47,3 +58,55 @@ Package::AddNode(PackageNode* node)
{
fNodes.Add(node);
}
int
Package::Open()
{
MutexLocker locker(fLock);
if (fOpenCount > 0) {
fOpenCount++;
return fFD;
}
// open the file
fFD = openat(fDomain->DirectoryFD(), fName, O_RDONLY);
if (fFD < 0) {
ERROR("Failed to open package file \"%s\"\n", fName);
return errno;
}
// stat it to verify that it's still the same file
struct stat st;
if (fstat(fFD, &st) < 0) {
ERROR("Failed to stat package file \"%s\"\n", fName);
close(fFD);
fFD = -1;
return errno;
}
if (st.st_dev != fDeviceID || st.st_ino != fNodeID) {
close(fFD);
fFD = -1;
RETURN_ERROR(B_ENTRY_NOT_FOUND);
}
fOpenCount = 1;
return fFD;
}
void
Package::Close()
{
MutexLocker locker(fLock);
if (fOpenCount == 0) {
ERROR("Package open count already 0!\n");
return;
}
if (--fOpenCount == 0) {
close(fFD);
fFD = -1;
}
}
@@ -9,6 +9,8 @@
#include <util/khash.h>
#include <util/OpenHashTable.h>
#include <lock.h>
#include "PackageNode.h"
@@ -30,12 +32,18 @@ public:
void AddNode(PackageNode* node);
int Open();
void Close();
const PackageNodeList& Nodes() const
{ return fNodes; }
private:
mutex fLock;
PackageDomain* fDomain;
char* fName;
int fFD;
uint32 fOpenCount;
Package* fHashTableNext;
ino_t fNodeID;
dev_t fDeviceID;
@@ -43,6 +51,29 @@ private:
};
struct PackageCloser {
PackageCloser(Package* package)
:
fPackage(package)
{
}
~PackageCloser()
{
if (fPackage != NULL)
fPackage->Close();
}
void Detach()
{
fPackage = NULL;
}
private:
Package* fPackage;
};
struct PackageHashDefinition {
typedef const char* KeyType;
typedef Package ValueType;
@@ -7,9 +7,9 @@
#include "PackageDirectory.h"
PackageDirectory::PackageDirectory(mode_t mode)
PackageDirectory::PackageDirectory(Package* package, mode_t mode)
:
PackageNode(mode)
PackageNode(package, mode)
{
}
@@ -14,7 +14,7 @@
class PackageDirectory : public PackageNode,
public DoublyLinkedListLinkImpl<PackageDirectory> {
public:
PackageDirectory(mode_t mode);
PackageDirectory(Package* package, mode_t mode);
virtual ~PackageDirectory();
void AddChild(PackageNode* node);
@@ -6,11 +6,99 @@
#include "PackageFile.h"
#include <algorithm>
#include <new>
PackageFile::PackageFile(mode_t mode, const PackageData& data)
#include <fs_cache.h>
#include "DataReader.h"
#include "PackageDataReader.h"
#include "DebugSupport.h"
#include "Package.h"
// #pragma mark - DataAccessor
struct PackageFile::DataAccessor {
DataAccessor(PackageData* data)
:
fData(data),
fDataReader(NULL),
fReader(NULL),
fFileCache(NULL)
{
}
~DataAccessor()
{
file_cache_delete(fFileCache);
delete fReader;
delete fDataReader;
}
status_t Init(dev_t deviceID, ino_t nodeID, int fd)
{
// create a DataReader for the compressed data
if (fData->IsEncodedInline()) {
fDataReader = new(std::nothrow) BufferDataReader(
fData->InlineData(), fData->CompressedSize());
} else
fDataReader = new(std::nothrow) FDDataReader(fd);
if (fDataReader == NULL)
RETURN_ERROR(B_NO_MEMORY);
// create a PackageDataReader
status_t error = PackageDataReaderFactory::CreatePackageDataReader(
fDataReader, *fData, fReader);
if (error != B_OK)
RETURN_ERROR(error);
// create a file cache
fFileCache = file_cache_create(deviceID, nodeID,
fData->UncompressedSize());
if (fFileCache == NULL)
RETURN_ERROR(B_NO_MEMORY);
return B_OK;
}
status_t ReadData(off_t offset, void* buffer, size_t* bufferSize)
{
if (offset < 0 || (uint64)offset > fData->UncompressedSize())
return B_BAD_VALUE;
size_t toRead = std::min((uint64)*bufferSize,
fData->UncompressedSize() - offset);
if (toRead > 0) {
status_t error = fReader->ReadData(offset, buffer, toRead);
if (error != B_OK)
RETURN_ERROR(error);
}
*bufferSize = toRead;
return B_OK;
}
private:
PackageData* fData;
DataReader* fDataReader;
PackageDataReader* fReader;
void* fFileCache;
};
// #pragma mark - PackageFile
PackageFile::PackageFile(Package* package, mode_t mode, const PackageData& data)
:
PackageLeafNode(mode),
fData(data)
PackageLeafNode(package, mode),
fData(data),
fDataAccessor(NULL)
{
}
@@ -20,8 +108,54 @@ PackageFile::~PackageFile()
}
status_t
PackageFile::VFSInit(dev_t deviceID, ino_t nodeID)
{
// open the package
int fd = fPackage->Open();
if (fd < 0)
RETURN_ERROR(fd);
PackageCloser packageCloser(fPackage);
// create the data accessor
fDataAccessor = new(std::nothrow) DataAccessor(&fData);
if (fDataAccessor == NULL)
RETURN_ERROR(B_NO_MEMORY);
status_t error = fDataAccessor->Init(deviceID, nodeID, fd);
if (error != B_OK) {
delete fDataAccessor;
fDataAccessor = NULL;
return error;
}
packageCloser.Detach();
return B_OK;
}
void
PackageFile::VFSUninit()
{
if (fDataAccessor != NULL) {
fPackage->Close();
delete fDataAccessor;
fDataAccessor = NULL;
}
}
off_t
PackageFile::FileSize() const
{
return fData.UncompressedSize();
}
status_t
PackageFile::Read(off_t offset, void* buffer, size_t* bufferSize)
{
if (fDataAccessor == NULL)
return B_BAD_VALUE;
return fDataAccessor->ReadData(offset, buffer, bufferSize);
}
@@ -13,14 +13,24 @@
class PackageFile : public PackageLeafNode {
public:
PackageFile(mode_t mode,
PackageFile(Package* package, mode_t mode,
const PackageData& data);
virtual ~PackageFile();
virtual status_t VFSInit(dev_t deviceID, ino_t nodeID);
virtual void VFSUninit();
virtual off_t FileSize() const;
virtual status_t Read(off_t offset, void* buffer,
size_t* bufferSize);
private:
struct DataAccessor;
private:
PackageData fData;
DataAccessor* fDataAccessor;
};
@@ -7,9 +7,9 @@
#include "PackageLeafNode.h"
PackageLeafNode::PackageLeafNode(mode_t mode)
PackageLeafNode::PackageLeafNode(Package* package, mode_t mode)
:
PackageNode(mode)
PackageNode(package, mode)
{
}
@@ -24,3 +24,10 @@ PackageLeafNode::SymlinkPath() const
{
return NULL;
}
status_t
PackageLeafNode::Read(off_t offset, void* buffer, size_t* bufferSize)
{
return EBADF;
}
@@ -14,11 +14,14 @@ class PackageData;
class PackageLeafNode : public PackageNode {
public:
PackageLeafNode(mode_t mode);
PackageLeafNode(Package* package, mode_t mode);
virtual ~PackageLeafNode();
virtual const char* SymlinkPath() const;
virtual status_t Read(off_t offset, void* buffer,
size_t* bufferSize);
public:
SinglyLinkedListLink<PackageLeafNode> fListLink;
};
@@ -12,8 +12,9 @@
#include "DebugSupport.h"
PackageNode::PackageNode(mode_t mode)
PackageNode::PackageNode(Package* package, mode_t mode)
:
fPackage(package),
fParent(NULL),
fName(NULL),
fMode(mode),
@@ -41,6 +42,19 @@ PackageNode::Init(PackageDirectory* parent, const char* name)
}
status_t
PackageNode::VFSInit(dev_t deviceID, ino_t nodeID)
{
return B_OK;
}
void
PackageNode::VFSUninit()
{
}
off_t
PackageNode::FileSize() const
{
@@ -13,12 +13,13 @@
#include <util/SinglyLinkedList.h>
class Package;
class PackageDirectory;
class PackageNode : public SinglyLinkedListLinkImpl<PackageNode> {
public:
PackageNode(mode_t mode);
PackageNode(Package* package, mode_t mode);
virtual ~PackageNode();
PackageDirectory* Parent() const { return fParent; }
@@ -27,6 +28,9 @@ public:
virtual status_t Init(PackageDirectory* parent,
const char* name);
virtual status_t VFSInit(dev_t deviceID, ino_t nodeID);
virtual void VFSUninit();
mode_t Mode() const { return fMode; }
uid_t UserID() const { return fUserID; }
@@ -43,6 +47,7 @@ public:
virtual off_t FileSize() const;
protected:
Package* fPackage;
PackageDirectory* fParent;
char* fName;
mode_t fMode;
@@ -10,9 +10,9 @@
#include <string.h>
PackageSymlink::PackageSymlink(mode_t mode)
PackageSymlink::PackageSymlink(Package* package, mode_t mode)
:
PackageLeafNode(mode),
PackageLeafNode(package, mode),
fSymlinkPath(NULL)
{
}
@@ -11,7 +11,7 @@
class PackageSymlink : public PackageLeafNode {
public:
PackageSymlink(mode_t mode);
PackageSymlink(Package* package, mode_t mode);
virtual ~PackageSymlink();
status_t SetSymlinkPath(const char* path);
@@ -141,10 +141,11 @@ struct Volume::PackageLoaderContentHandler : PackageContentHandler {
PackageNode* node;
if (S_ISREG(mode)) {
// file
node = new(std::nothrow) PackageFile(mode, entry->Data());
node = new(std::nothrow) PackageFile(fPackage, mode, entry->Data());
} else if (S_ISLNK(mode)) {
// symlink
PackageSymlink* symlink = new(std::nothrow) PackageSymlink(mode);
PackageSymlink* symlink = new(std::nothrow) PackageSymlink(
fPackage, mode);
if (symlink == NULL)
RETURN_ERROR(B_NO_MEMORY);
@@ -157,7 +158,7 @@ struct Volume::PackageLoaderContentHandler : PackageContentHandler {
node = symlink;
} else if (S_ISDIR(mode)) {
// directory
node = new(std::nothrow) PackageDirectory(mode);
node = new(std::nothrow) PackageDirectory(fPackage, mode);
} else
RETURN_ERROR(B_BAD_DATA);
@@ -476,16 +477,12 @@ status_t
Volume::_LoadPackage(Package* package)
{
// open package file
int fd = openat(package->Domain()->DirectoryFD(), package->Name(),
O_RDONLY);
if (fd < 0) {
ERROR("Failed to open package file \"%s\"\n", package->Name());
return errno;
}
// TODO: Verify that it's still the same file.
FDCloser fdCloser(fd);
int fd = package->Open();
if (fd < 0)
RETURN_ERROR(fd);
PackageCloser packageCloser(package);
// open package
// initialize package reader
PackageLoaderErrorOutput errorOutput(package);
PackageReader packageReader(&errorOutput);
status_t error = packageReader.Init(fd, false);
@@ -58,6 +58,43 @@ set_dirent_name(struct dirent* buffer, size_t bufferSize, const char* name,
}
static status_t
check_access(Node* node, int mode)
{
// write access requested?
if (mode & W_OK)
return B_READ_ONLY_DEVICE;
// get node permissions
int userPermissions = (node->Mode() & S_IRWXU) >> 6;
int groupPermissions = (node->Mode() & S_IRWXG) >> 3;
int otherPermissions = node->Mode() & S_IRWXO;
// get the permissions for this uid/gid
int permissions = 0;
uid_t uid = geteuid();
if (uid == 0) {
// user is root
// root has always read/write permission, but at least one of the
// X bits must be set for execute permission
permissions = userPermissions | groupPermissions | otherPermissions
| S_IROTH | S_IWOTH;
} else if (uid == node->UserID()) {
// user is node owner
permissions = userPermissions;
} else if (is_user_in_group(node->GroupID())) {
// user is in owning group
permissions = groupPermissions;
} else {
// user is one of the others
permissions = otherPermissions;
}
return (mode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
}
// #pragma mark - Volume
@@ -171,9 +208,16 @@ packagefs_get_vnode(fs_volume* fsVolume, ino_t vnid, fs_vnode* fsNode,
Node* node = volume->FindNode(vnid);
if (node == NULL)
return B_ENTRY_NOT_FOUND;
node->AcquireReference();
BReference<Node> nodeReference(node);
volumeLocker.Unlock();
fsNode->private_node = node;
NodeWriteLocker nodeLocker(node);
status_t error = node->VFSInit(volume->ID());
if (error != B_OK)
RETURN_ERROR(error);
nodeLocker.Unlock();
fsNode->private_node = nodeReference.Detach();
fsNode->ops = &gPackageFSVnodeOps;
*_type = node->Mode() & S_IFMT;
*_flags = 0;
@@ -191,12 +235,28 @@ packagefs_put_vnode(fs_volume* fsVolume, fs_vnode* fsNode, bool reenter)
FUNCTION("volume: %p, node: %p\n", volume, node);
TOUCH(volume);
NodeWriteLocker nodeLocker(node);
node->VFSUninit();
nodeLocker.Unlock();
node->ReleaseReference();
return B_OK;
}
// #pragma mark - Request I/O
#if 0
static status_t
packagefs_io(fs_volume* volume, fs_vnode* vnode, void* cookie,
io_request* request)
{
}
#endif
// #pragma mark - Nodes
@@ -238,39 +298,8 @@ packagefs_access(fs_volume* fsVolume, fs_vnode* fsNode, int mode)
FUNCTION("volume: %p, node: %p (%lld)\n", volume, node, node->ID());
TOUCH(volume);
// write access requested?
if (mode & W_OK)
return B_READ_ONLY_DEVICE;
NodeReadLocker nodeLocker(node);
// get node permissions
int userPermissions = (node->Mode() & S_IRWXU) >> 6;
int groupPermissions = (node->Mode() & S_IRWXG) >> 3;
int otherPermissions = node->Mode() & S_IRWXO;
// get the permissions for this uid/gid
int permissions = 0;
uid_t uid = geteuid();
if (uid == 0) {
// user is root
// root has always read/write permission, but at least one of the
// X bits must be set for execute permission
permissions = userPermissions | groupPermissions | otherPermissions
| S_IROTH | S_IWOTH;
} else if (uid == node->UserID()) {
// user is node owner
permissions = userPermissions;
} else if (is_user_in_group(node->GroupID())) {
// user is in owning group
permissions = groupPermissions;
} else {
// user is one of the others
permissions = otherPermissions;
}
return (mode & ~permissions) ? B_NOT_ALLOWED : B_OK;
return check_access(node, mode);
}
@@ -304,6 +333,17 @@ packagefs_read_stat(fs_volume* fsVolume, fs_vnode* fsNode, struct stat* st)
// #pragma mark - Files
struct FileCookie {
int openMode;
FileCookie(int openMode)
:
openMode(openMode)
{
}
};
static status_t
packagefs_open(fs_volume* fsVolume, fs_vnode* fsNode, int openMode,
void** _cookie)
@@ -314,10 +354,26 @@ packagefs_open(fs_volume* fsVolume, fs_vnode* fsNode, int openMode,
FUNCTION("volume: %p, node: %p (%lld), openMode %#x\n", volume, node,
node->ID(), openMode);
TOUCH(volume);
TOUCH(node);
// TODO: Implement it for real!
*_cookie = NULL;
NodeReadLocker nodeLocker(node);
// check the open mode and permissions
if (S_ISDIR(node->Mode()) && (openMode & O_RWMASK) != O_RDONLY)
return B_IS_A_DIRECTORY;
if ((openMode & O_RWMASK) != O_RDONLY)
return B_NOT_ALLOWED;
status_t error = check_access(node, R_OK);
if (error != B_OK)
return error;
// allocate the cookie
FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
if (cookie == NULL)
RETURN_ERROR(B_NO_MEMORY);
*_cookie = cookie;
return B_OK;
}
@@ -331,17 +387,40 @@ packagefs_close(fs_volume* fs, fs_vnode* _node, void* cookie)
static status_t
packagefs_free_cookie(fs_volume* fs, fs_vnode* _node, void* cookie)
packagefs_free_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
{
Volume* volume = (Volume*)fsVolume->private_volume;
Node* node = (Node*)fsNode->private_node;
FileCookie* cookie = (FileCookie*)_cookie;
FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
node->ID(), cookie);
TOUCH(volume);
TOUCH(node);
delete cookie;
return B_OK;
}
static status_t
packagefs_read(fs_volume* fs, fs_vnode* _node, void* cookie, off_t pos,
void* buffer, size_t* bufferSize)
packagefs_read(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie,
off_t offset, void* buffer, size_t* bufferSize)
{
return B_UNSUPPORTED;
Volume* volume = (Volume*)fsVolume->private_volume;
Node* node = (Node*)fsNode->private_node;
FileCookie* cookie = (FileCookie*)_cookie;
FUNCTION("volume: %p, node: %p (%lld), cookie: %p, offset: %lld, "
"buffer: %p, size: %lu\n", volume, node, node->ID(), cookie, offset,
buffer, *bufferSize);
TOUCH(volume);
if ((cookie->openMode & O_RWMASK) != O_RDONLY)
return EBADF;
return node->Read(offset, buffer, bufferSize);
}
@@ -635,7 +714,7 @@ fs_vnode_ops gPackageFSVnodeOps = {
NULL, // read_pages,
NULL, // write_pages,
NULL, // io()
NULL, // &packagefs_io,
NULL, // cancel_io()
NULL, // get_file_map,