Add support for shine-through directories
* packagefs_mount(): Initialize the fs_volume earlier, so it is more usuable in Volume::Mount(). * The new mount parameter "shine-through" can be used to specify which shine-through mode shall be used. Can be "system", "common", "home", and "none". Depending on the setting it is decided which directories of the underlying file system are bind-mounted on top of ours. * Fix infinite loop in Volume::_RemovePackageContent(). * Use the RETURN_ERROR() macro in more places to help with debugging.
This commit is contained in:
parent
d61a8548f9
commit
314cb5f13e
@ -22,6 +22,7 @@
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
#include <Notifications.h>
|
||||
#include <vfs.h>
|
||||
|
||||
#include <package/hpkg/ErrorOutput.h>
|
||||
#include <package/hpkg/PackageEntry.h>
|
||||
@ -44,6 +45,16 @@ using BPackageKit::BHPKG::BPrivate::PackageReaderImpl;
|
||||
// node ID of the root directory
|
||||
static const ino_t kRootDirectoryID = 1;
|
||||
|
||||
// shine-through directories
|
||||
const char* const kSystemShineThroughDirectories[] = {
|
||||
"packages", NULL
|
||||
};
|
||||
const char* const kCommonShineThroughDirectories[] = {
|
||||
"non-packaged", "packages", "settings", "var", NULL
|
||||
};
|
||||
const char* const* kHomeShineThroughDirectories
|
||||
= kCommonShineThroughDirectories;
|
||||
|
||||
|
||||
// #pragma mark - Job
|
||||
|
||||
@ -355,14 +366,21 @@ Volume::Mount(const char* parameterString)
|
||||
|
||||
const char* packages = NULL;
|
||||
const char* volumeName = NULL;
|
||||
const char* shineThrough = NULL;
|
||||
|
||||
void* parameterHandle = parse_driver_settings_string(parameterString);
|
||||
if (parameterHandle != NULL) {
|
||||
packages
|
||||
= get_driver_parameter(parameterHandle, "packages", NULL, NULL);
|
||||
volumeName
|
||||
= get_driver_parameter(parameterHandle, "volume-name", NULL, NULL);
|
||||
delete_driver_settings(parameterHandle);
|
||||
packages = get_driver_parameter(parameterHandle, "packages", NULL,
|
||||
NULL);
|
||||
volumeName = get_driver_parameter(parameterHandle, "volume-name", NULL,
|
||||
NULL);
|
||||
shineThrough = get_driver_parameter(parameterHandle, "shine-through",
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
CObjectDeleter<void, status_t> parameterHandleDeleter(parameterHandle,
|
||||
&delete_driver_settings);
|
||||
|
||||
if (packages == NULL || packages[0] == '\0') {
|
||||
ERROR("need package folder ('packages' parameter)!\n");
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
@ -396,11 +414,23 @@ Volume::Mount(const char* parameterString)
|
||||
error = PublishVNode(fRootDirectory);
|
||||
if (error != B_OK) {
|
||||
fRootDirectory->ReleaseReference();
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
// establish shine-through directories
|
||||
error = _CreateShineThroughDirectories(shineThrough);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// run the package loader
|
||||
resume_thread(fPackageLoader);
|
||||
dprintf("mounted packagefs successfully!\n");
|
||||
{
|
||||
for (Node* node = fRootDirectory->FirstChild(); node != NULL;
|
||||
node = fRootDirectory->NextChild(node)) {
|
||||
dprintf(" node %lld: \"%s\"\n", node->ID(), node->Name());
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@ -452,7 +482,7 @@ Volume::AddPackageDomain(const char* path)
|
||||
|
||||
status_t error = packageDomain->Init(path);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
|
||||
Job* job = new(std::nothrow) AddPackageDomainJob(this, packageDomain);
|
||||
if (job == NULL)
|
||||
@ -536,7 +566,7 @@ Volume::_AddInitialPackageDomain(const char* path)
|
||||
|
||||
status_t error = domain->Init(path);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
|
||||
return _AddPackageDomain(domain, false);
|
||||
}
|
||||
@ -556,7 +586,7 @@ Volume::_AddPackageDomain(PackageDomain* domain, bool notify)
|
||||
if (error != B_OK) {
|
||||
ERROR("Failed to prepare package domain \"%s\": %s\n",
|
||||
domain->Path(), strerror(errno));
|
||||
return errno;
|
||||
RETURN_ERROR(errno);
|
||||
}
|
||||
|
||||
// iterate through the dir and create packages
|
||||
@ -564,7 +594,7 @@ Volume::_AddPackageDomain(PackageDomain* domain, bool notify)
|
||||
if (dir == NULL) {
|
||||
ERROR("Failed to open package domain directory \"%s\": %s\n",
|
||||
domain->Path(), strerror(errno));
|
||||
return errno;
|
||||
RETURN_ERROR(errno);
|
||||
}
|
||||
CObjectDeleter<DIR, int> dirCloser(dir, closedir);
|
||||
|
||||
@ -589,7 +619,7 @@ Volume::_AddPackageDomain(PackageDomain* domain, bool notify)
|
||||
break;
|
||||
_RemovePackageContent(activePackage, NULL, notify);
|
||||
}
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -614,17 +644,17 @@ Volume::_LoadPackage(Package* package)
|
||||
PackageReaderImpl packageReader(&errorOutput);
|
||||
status_t error = packageReader.Init(fd, false);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// parse content
|
||||
PackageLoaderContentHandler handler(package);
|
||||
error = handler.Init();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
|
||||
error = packageReader.ParseContent(&handler);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@ -641,7 +671,7 @@ Volume::_AddPackageContent(Package* package, bool notify)
|
||||
status_t error = _AddPackageContentRootNode(package, node, notify);
|
||||
if (error != B_OK) {
|
||||
_RemovePackageContent(package, node, notify);
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -658,12 +688,12 @@ Volume::_RemovePackageContent(Package* package, PackageNode* endNode,
|
||||
if (node == endNode)
|
||||
break;
|
||||
|
||||
// skip over ".PackageInfo" file, it isn't part of the package content
|
||||
if (strcmp(node->Name(), B_HPKG_PACKAGE_INFO_FILE_NAME) == 0)
|
||||
continue;
|
||||
|
||||
PackageNode* nextNode = package->Nodes().GetNext(node);
|
||||
_RemovePackageContentRootNode(package, node, NULL, notify);
|
||||
|
||||
// skip over ".PackageInfo" file, it isn't part of the package content
|
||||
if (strcmp(node->Name(), B_HPKG_PACKAGE_INFO_FILE_NAME) != 0)
|
||||
_RemovePackageContentRootNode(package, node, NULL, notify);
|
||||
|
||||
node = nextNode;
|
||||
}
|
||||
}
|
||||
@ -696,7 +726,7 @@ Volume::_AddPackageContentRootNode(Package* package,
|
||||
// remove the added package nodes
|
||||
_RemovePackageContentRootNode(package, rootPackageNode, packageNode,
|
||||
notify);
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
// recursive into directory
|
||||
@ -798,7 +828,7 @@ Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode,
|
||||
status_t error = _CreateNode(packageNode->Mode(), directory,
|
||||
packageNode->Name(), node);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
newNode = true;
|
||||
}
|
||||
BReference<Node> nodeReference(node);
|
||||
@ -808,7 +838,7 @@ Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode,
|
||||
// remove the node, if created before
|
||||
if (newNode)
|
||||
_RemoveNode(node);
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
if (notify) {
|
||||
@ -911,7 +941,7 @@ Volume::_CreateNode(mode_t mode, Directory* parent, const char* name,
|
||||
|
||||
status_t error = node->Init(parent, name);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
RETURN_ERROR(error);
|
||||
|
||||
parent->AddChild(node);
|
||||
|
||||
@ -1084,3 +1114,92 @@ Volume::_DomainEntryMoved(PackageDomain* domain, dev_t deviceID,
|
||||
_DomainEntryCreated(domain, deviceID, toDirectoryID, nodeID, name, true,
|
||||
notify);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Volume::_CreateShineThroughDirectories(const char* shineThroughSetting)
|
||||
{
|
||||
if (shineThroughSetting == NULL)
|
||||
return B_OK;
|
||||
|
||||
// get the directories to map
|
||||
const char* const* directories = NULL;
|
||||
|
||||
if (strcmp(shineThroughSetting, "system") == 0)
|
||||
directories = kSystemShineThroughDirectories;
|
||||
else if (strcmp(shineThroughSetting, "common") == 0)
|
||||
directories = kCommonShineThroughDirectories;
|
||||
else if (strcmp(shineThroughSetting, "home") == 0)
|
||||
directories = kHomeShineThroughDirectories;
|
||||
else if (strcmp(shineThroughSetting, "none") == 0)
|
||||
directories = NULL;
|
||||
else
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
if (directories == NULL)
|
||||
return B_OK;
|
||||
|
||||
// get our mount point
|
||||
dev_t mountPointDevice;
|
||||
ino_t mountPointNode;
|
||||
status_t error = vfs_get_mount_point(fFSVolume->id, &mountPointDevice,
|
||||
&mountPointNode);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// iterate through the directory list, create the directories, and bind them
|
||||
// to the mount point subdirectories
|
||||
while (const char* directoryName = *(directories++)) {
|
||||
// look up the mount point subdirectory
|
||||
struct vnode* vnode;
|
||||
error = vfs_entry_ref_to_vnode(mountPointDevice, mountPointNode,
|
||||
directoryName, &vnode);
|
||||
if (error != B_OK) {
|
||||
dprintf("packagefs: Failed to get shine-through directory \"%s\": "
|
||||
"%s\n", directoryName, strerror(error));
|
||||
continue;
|
||||
}
|
||||
CObjectDeleter<struct vnode> vnodePutter(vnode, &vfs_put_vnode);
|
||||
|
||||
// stat it
|
||||
struct stat st;
|
||||
error = vfs_stat_vnode(vnode, &st);
|
||||
if (error != B_OK) {
|
||||
dprintf("packagefs: Failed to stat shine-through directory \"%s\": "
|
||||
"%s\n", directoryName, strerror(error));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
dprintf("packagefs: Shine-through entry \"%s\" is not a "
|
||||
"directory\n", directoryName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// create the directory
|
||||
Node* directory;
|
||||
error = _CreateNode(S_IFDIR, fRootDirectory, directoryName, directory);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// publish its vnode, so the VFS will find it without asking us
|
||||
error = PublishVNode(directory);
|
||||
if (error != B_OK) {
|
||||
_RemoveNode(directory);
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
// bind the directory
|
||||
error = vfs_bind_mount_directory(st.st_dev, st.st_ino, fFSVolume->id,
|
||||
directory->ID());
|
||||
|
||||
PutVNode(directory->ID());
|
||||
// release our reference again -- on success
|
||||
// vfs_bind_mount_directory() got one
|
||||
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
@ -118,6 +118,8 @@ private:
|
||||
ino_t nodeID, const char* fromName,
|
||||
const char* name, bool notify);
|
||||
|
||||
status_t _CreateShineThroughDirectories(
|
||||
const char* shineThroughSetting);
|
||||
private:
|
||||
rw_lock fLock;
|
||||
fs_volume* fFSVolume;
|
||||
|
@ -117,14 +117,17 @@ packagefs_mount(fs_volume* fsVolume, const char* device, uint32 flags,
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
ObjectDeleter<Volume> volumeDeleter(volume);
|
||||
|
||||
// Initialize the fs_volume now already, so it is mostly usable in during
|
||||
// mounting.
|
||||
fsVolume->private_volume = volumeDeleter.Detach();
|
||||
fsVolume->ops = &gPackageFSVolumeOps;
|
||||
|
||||
status_t error = volume->Mount(parameters);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// set return values
|
||||
*_rootID = volume->RootDirectory()->ID();
|
||||
fsVolume->private_volume = volumeDeleter.Detach();
|
||||
fsVolume->ops = &gPackageFSVolumeOps;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user