/*
* Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "Volume.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <new>
#include <AppDefs.h>
#include <driver_settings.h>
#include <KernelExport.h>
#include <NodeMonitor.h>
#include <package/PackageInfoAttributes.h>
#include <AutoDeleter.h>
#include <PackagesDirectoryDefs.h>
#include <vfs.h>
#include "AttributeIndex.h"
#include "DebugSupport.h"
#include "kernel_interface.h"
#include "LastModifiedIndex.h"
#include "NameIndex.h"
#include "OldUnpackingNodeAttributes.h"
#include "PackageFSRoot.h"
#include "PackageLinkDirectory.h"
#include "PackageLinksDirectory.h"
#include "Resolvable.h"
#include "SizeIndex.h"
#include "UnpackingLeafNode.h"
#include "UnpackingDirectory.h"
#include "Utils.h"
#include "Version.h"
// node ID of the root directory
static const ino_t kRootDirectoryID = 1;
static const uint32 kAllStatFields = B_STAT_MODE | B_STAT_UID | B_STAT_GID
| B_STAT_SIZE | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME
| B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME;
// shine-through directories
const char* const kShineThroughDirectories[] = {
"cache", "non-packaged", "packages", "settings", "var", NULL
};
// sanity limit for activation change request
const size_t kMaxActivationRequestSize = 10 * 1024 * 1024;
// sanity limit for activation file size
const size_t kMaxActivationFileSize = 10 * 1024 * 1024;
static const char* const kAdministrativeDirectoryName
= PACKAGES_DIRECTORY_ADMIN_DIRECTORY;
static const char* const kActivationFileName
= PACKAGES_DIRECTORY_ACTIVATION_FILE;
static const char* const kActivationFilePath
= PACKAGES_DIRECTORY_ADMIN_DIRECTORY "/"
PACKAGES_DIRECTORY_ACTIVATION_FILE;
// #pragma mark - ShineThroughDirectory
struct Volume::ShineThroughDirectory : public Directory {
ShineThroughDirectory(ino_t id)
:
Directory(id)
{
get_real_time(fModifiedTime);
}
virtual timespec ModifiedTime() const
{
return fModifiedTime;
}
private:
timespec fModifiedTime;
};
// #pragma mark - ActivationChangeRequest
struct Volume::ActivationChangeRequest {
public:
ActivationChangeRequest()
:
fRequest(NULL),
fRequestSize(0)
{
}
~ActivationChangeRequest()
{
free(fRequest);
}
status_t Init(const void* userRequest, size_t requestSize)
{
// copy request to kernel
if (requestSize > kMaxActivationRequestSize)
RETURN_ERROR(B_BAD_VALUE);
fRequest = (PackageFSActivationChangeRequest*)malloc(requestSize);
if (fRequest == NULL)
RETURN_ERROR(B_NO_MEMORY);
fRequestSize = requestSize;
status_t error = user_memcpy(fRequest, userRequest, fRequestSize);
if (error != B_OK)
RETURN_ERROR(error);
uint32 itemCount = fRequest->itemCount;
const char* requestEnd = (const char*)fRequest + requestSize;
if (&fRequest->items[itemCount] > (void*)requestEnd)
RETURN_ERROR(B_BAD_VALUE);
// adjust the item name pointers and check their validity
addr_t nameDelta = (addr_t)fRequest - (addr_t)userRequest;
for (uint32 i = 0; i < itemCount; i++) {
PackageFSActivationChangeItem& item = fRequest->items[i];
item.name += nameDelta;
if (item.name < (char*)fRequest || item.name >= requestEnd)
RETURN_ERROR(B_BAD_VALUE);
size_t maxNameSize = requestEnd - item.name;
if (strnlen(item.name, maxNameSize) == maxNameSize)
RETURN_ERROR(B_BAD_VALUE);
}
return B_OK;
}
uint32 CountItems() const
{
return fRequest->itemCount;
}
PackageFSActivationChangeItem* ItemAt(uint32 index) const
{
return index < CountItems() ? &fRequest->items[index] : NULL;
}
private:
PackageFSActivationChangeRequest* fRequest;
size_t fRequestSize;
};
// #pragma mark - Volume
Volume::Volume(fs_volume* fsVolume)
:
fFSVolume(fsVolume),
fRootDirectory(NULL),
fPackageFSRoot(NULL),
fPackagesDirectory(NULL),
fPackagesDirectories(),
fPackagesDirectoriesByNodeRef(),
fPackageSettings(),
fNextNodeID(kRootDirectoryID + 1)
{
rw_lock_init(&fLock, "packagefs volume");
}
Volume::~Volume()
{
// remove the packages from the node tree
{
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator();
Package* package = it.Next();) {
_RemovePackageContent(package, NULL, false);
}
}
// delete the packages
_RemoveAllPackages();
// delete all indices
Index* index = fIndices.Clear(true);
while (index != NULL) {
Index* next = index->IndexHashLink();
delete index;
index = next;
}
// remove all nodes from the ID hash table
Node* node = fNodes.Clear(true);
while (node != NULL) {
Node* next = node->IDHashTableNext();
node->ReleaseReference();
node = next;
}
if (fPackageFSRoot != NULL) {
if (this == fPackageFSRoot->SystemVolume())
_RemovePackageLinksDirectory();
fPackageFSRoot->UnregisterVolume(this);
}
if (fRootDirectory != NULL)
fRootDirectory->ReleaseReference();
while (PackagesDirectory* directory = fPackagesDirectories.RemoveHead())
directory->ReleaseReference();
rw_lock_destroy(&fLock);
}
status_t
Volume::Mount(const char* parameterString)
{
// init the hash tables
status_t error = fPackagesDirectoriesByNodeRef.Init();
if (error != B_OK)
RETURN_ERROR(error);
error = fNodes.Init();
if (error != B_OK)
RETURN_ERROR(error);
error = fNodeListeners.Init();
if (error != B_OK)
RETURN_ERROR(error);
error = fPackages.Init();
if (error != B_OK)
RETURN_ERROR(error);
error = fIndices.Init();
if (error != B_OK)
RETURN_ERROR(error);
// create the name index
{
NameIndex* index = new(std::nothrow) NameIndex;
if (index == NULL)
RETURN_ERROR(B_NO_MEMORY);
error = index->Init(this);
if (error != B_OK) {
delete index;
RETURN_ERROR(error);
}
fIndices.Insert(index);
}
// create the size index
{
SizeIndex* index = new(std::nothrow) SizeIndex;
if (index == NULL)
RETURN_ERROR(B_NO_MEMORY);
error = index->Init(this);
if (error != B_OK) {
delete index;
RETURN_ERROR(error);
}
fIndices.Insert(index);
}
// create the last modified index
{
LastModifiedIndex* index = new(std::nothrow) LastModifiedIndex;
if (index == NULL)
RETURN_ERROR(B_NO_MEMORY);
error = index->Init(this);
if (error != B_OK) {
delete index;
RETURN_ERROR(error);
}
fIndices.Insert(index);
}
// create a BEOS:APP_SIG index
{
AttributeIndex* index = new(std::nothrow) AttributeIndex;
if (index == NULL)
RETURN_ERROR(B_NO_MEMORY);
error = index->Init(this, "BEOS:APP_SIG", B_MIME_STRING_TYPE, 0);
if (error != B_OK) {
delete index;
RETURN_ERROR(error);
}
fIndices.Insert(index);
}
// get the mount parameters
const char* packages = NULL;
const char* volumeName = NULL;
const char* mountType = NULL;
const char* shineThrough = NULL;
const char* packagesState = 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);
mountType = get_driver_parameter(parameterHandle, "type", NULL, NULL);
shineThrough = get_driver_parameter(parameterHandle, "shine-through",
NULL, NULL);
packagesState = get_driver_parameter(parameterHandle, "state", NULL,
NULL);
}
CObjectDeleter<void, status_t> parameterHandleDeleter(parameterHandle,
&delete_driver_settings);
if (packages != NULL && packages[0] == '\0') {
FATAL("invalid package folder ('packages' parameter)!\n");
RETURN_ERROR(B_BAD_VALUE);
}
error = _InitMountType(mountType);
if (error != B_OK) {
FATAL("invalid mount type: \"%s\"\n", mountType);
RETURN_ERROR(error);
}
// get our mount point
error = vfs_get_mount_point(fFSVolume->id, &fMountPoint.deviceID,
&fMountPoint.nodeID);
if (error != B_OK)
RETURN_ERROR(error);
// load package settings
error = fPackageSettings.Load(fMountPoint.deviceID, fMountPoint.nodeID,
fMountType);
// abort only in case of serious issues (memory shortage)
if (error == B_NO_MEMORY)
RETURN_ERROR(error);
// create package domain
fPackagesDirectory = new(std::nothrow) PackagesDirectory;
if (fPackagesDirectory == NULL)
RETURN_ERROR(B_NO_MEMORY);
fPackagesDirectories.Add(fPackagesDirectory);
fPackagesDirectoriesByNodeRef.Insert(fPackagesDirectory);
struct stat st;
error = fPackagesDirectory->Init(packages, fMountPoint.deviceID,
fMountPoint.nodeID, st);
if (error != B_OK)
RETURN_ERROR(error);
// If a packages state has been specified, load the needed states.
if (packagesState != NULL) {
error = _LoadOldPackagesStates(packagesState);
if (error != B_OK)
RETURN_ERROR(error);
}
// If no volume name is given, infer it from the mount type.
if (volumeName == NULL) {
switch (fMountType) {
case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
volumeName = "system";
break;
case PACKAGE_FS_MOUNT_TYPE_HOME:
volumeName = "config";
break;
case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
default:
volumeName = "Package FS";
break;
}
}
String volumeNameString;
if (!volumeNameString.SetTo(volumeName))
RETURN_ERROR(B_NO_MEMORY);
// create the root node
fRootDirectory
= new(std::nothrow) ::RootDirectory(kRootDirectoryID, st.st_mtim);
if (fRootDirectory == NULL)
RETURN_ERROR(B_NO_MEMORY);
fRootDirectory->Init(NULL, volumeNameString);
fNodes.Insert(fRootDirectory);
fRootDirectory->AcquireReference();
// one reference for the table
// register with packagefs root
error = ::PackageFSRoot::RegisterVolume(this);
if (error != B_OK)
RETURN_ERROR(error);
if (this == fPackageFSRoot->SystemVolume()) {
error = _AddPackageLinksDirectory();
if (error != B_OK)
RETURN_ERROR(error);
}
// create shine-through directories
error = _CreateShineThroughDirectories(shineThrough);
if (error != B_OK)
RETURN_ERROR(error);
// add initial packages
error = _AddInitialPackages();
if (error != B_OK)
RETURN_ERROR(error);
// publish the root node
fRootDirectory->AcquireReference();
error = PublishVNode(fRootDirectory);
if (error != B_OK) {
fRootDirectory->ReleaseReference();
RETURN_ERROR(error);
}
// bind and publish the shine-through directories
error = _PublishShineThroughDirectories();
if (error != B_OK)
RETURN_ERROR(error);
StringPool::DumpUsageStatistics();
return B_OK;
}
void
Volume::Unmount()
{
}
status_t
Volume::IOCtl(Node* node, uint32 operation, void* buffer, size_t size)
{
switch (operation) {
case PACKAGE_FS_OPERATION_GET_VOLUME_INFO:
{
if (size < sizeof(PackageFSVolumeInfo))
RETURN_ERROR(B_BAD_VALUE);
PackageFSVolumeInfo* userVolumeInfo
= (PackageFSVolumeInfo*)buffer;
VolumeReadLocker volumeReadLocker(this);
PackageFSVolumeInfo volumeInfo;
volumeInfo.mountType = fMountType;
volumeInfo.rootDeviceID = fPackageFSRoot->DeviceID();
volumeInfo.rootDirectoryID = fPackageFSRoot->NodeID();
volumeInfo.packagesDirectoryCount = fPackagesDirectories.Count();
status_t error = user_memcpy(userVolumeInfo, &volumeInfo,
sizeof(volumeInfo));
if (error != B_OK)
RETURN_ERROR(error);
uint32 directoryIndex = 0;
for (PackagesDirectoryList::Iterator it
= fPackagesDirectories.GetIterator();
PackagesDirectory* directory = it.Next();
directoryIndex++) {
PackageFSDirectoryInfo info;
info.deviceID = directory->DeviceID();
info.nodeID = directory->NodeID();
PackageFSDirectoryInfo* userInfo
= userVolumeInfo->packagesDirectoryInfos + directoryIndex;
if (addr_t(userInfo + 1) > (addr_t)buffer + size)
break;
if (user_memcpy(userInfo, &info, sizeof(info)) != B_OK)
return B_BAD_ADDRESS;
}
return B_OK;
}
case PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS:
{
if (size < sizeof(PackageFSGetPackageInfosRequest))
RETURN_ERROR(B_BAD_VALUE);
PackageFSGetPackageInfosRequest* request
= (PackageFSGetPackageInfosRequest*)buffer;
VolumeReadLocker volumeReadLocker(this);
addr_t bufferEnd = (addr_t)buffer + size;
uint32 packageCount = fPackages.CountElements();
char* nameBuffer = (char*)(request->infos + packageCount);
uint32 packageIndex = 0;
for (PackageFileNameHashTable::Iterator it
= fPackages.GetIterator(); it.HasNext();
packageIndex++) {
Package* package = it.Next();
PackageFSPackageInfo info;
info.packageDeviceID = package->DeviceID();
info.packageNodeID = package->NodeID();
PackagesDirectory* directory = package->Directory();
info.directoryDeviceID = directory->DeviceID();
info.directoryNodeID = directory->NodeID();
info.name = nameBuffer;
PackageFSPackageInfo* userInfo = request->infos + packageIndex;
if (addr_t(userInfo + 1) <= bufferEnd) {
if (user_memcpy(userInfo, &info, sizeof(info)) != B_OK)
return B_BAD_ADDRESS;
}
const char* name = package->FileName();
size_t nameSize = strlen(name) + 1;
char* nameEnd = nameBuffer + nameSize;
if ((addr_t)nameEnd <= bufferEnd) {
if (user_memcpy(nameBuffer, name, nameSize) != B_OK)
return B_BAD_ADDRESS;
}
nameBuffer = nameEnd;
}
PackageFSGetPackageInfosRequest header;
header.bufferSize = nameBuffer - (char*)request;
header.packageCount = packageCount;
size_t headerSize = (char*)&request->infos - (char*)request;
RETURN_ERROR(user_memcpy(request, &header, headerSize));
}
case PACKAGE_FS_OPERATION_CHANGE_ACTIVATION:
{
ActivationChangeRequest request;
status_t error = request.Init(buffer, size);
if (error != B_OK)
RETURN_ERROR(B_BAD_VALUE);
return _ChangeActivation(request);
}
default:
return B_BAD_VALUE;
}
}
void
Volume::AddNodeListener(NodeListener* listener, Node* node)
{
ASSERT(!listener->IsListening());
listener->StartedListening(node);
if (NodeListener* list = fNodeListeners.Lookup(node))
list->AddNodeListener(listener);
else
fNodeListeners.Insert(listener);
}
void
Volume::RemoveNodeListener(NodeListener* listener)
{
ASSERT(listener->IsListening());
Node* node = listener->ListenedNode();
if (NodeListener* next = listener->RemoveNodeListener()) {
// list not empty yet -- if we removed the head, add a new head to the
// hash table
NodeListener* list = fNodeListeners.Lookup(node);
if (list == listener) {
fNodeListeners.Remove(listener);
fNodeListeners.Insert(next);
}
} else
fNodeListeners.Remove(listener);
listener->StoppedListening();
}
void
Volume::AddQuery(Query* query)
{
fQueries.Add(query);
}
void
Volume::RemoveQuery(Query* query)
{
fQueries.Remove(query);
}
void
Volume::UpdateLiveQueries(Node* node, const char* attribute, int32 type,
const void* oldKey, size_t oldLength, const void* newKey,
size_t newLength)
{
for (QueryList::Iterator it = fQueries.GetIterator();
Query* query = it.Next();) {
query->LiveUpdate(node, attribute, type, oldKey, oldLength, newKey,
newLength);
}
}
status_t
Volume::GetVNode(ino_t nodeID, Node*& _node)
{
return get_vnode(fFSVolume, nodeID, (void**)&_node);
}
status_t
Volume::PutVNode(ino_t nodeID)
{
return put_vnode(fFSVolume, nodeID);
}
status_t
Volume::RemoveVNode(ino_t nodeID)
{
return remove_vnode(fFSVolume, nodeID);
}
status_t
Volume::PublishVNode(Node* node)
{
return publish_vnode(fFSVolume, node->ID(), node, &gPackageFSVnodeOps,
node->Mode() & S_IFMT, 0);
}
void
Volume::PackageLinkNodeAdded(Node* node)
{
_AddPackageLinksNode(node);
notify_entry_created(ID(), node->Parent()->ID(), node->Name(), node->ID());
_NotifyNodeAdded(node);
}
void
Volume::PackageLinkNodeRemoved(Node* node)
{
_RemovePackageLinksNode(node);
notify_entry_removed(ID(), node->Parent()->ID(), node->Name(), node->ID());
_NotifyNodeRemoved(node);
}
void
Volume::PackageLinkNodeChanged(Node* node, uint32 statFields,
const OldNodeAttributes& oldAttributes)
{
Directory* parent = node->Parent();
notify_stat_changed(ID(), parent != NULL ? parent->ID() : -1, node->ID(),
statFields);
_NotifyNodeChanged(node, statFields, oldAttributes);
}
status_t
Volume::_LoadOldPackagesStates(const char* packagesState)
{
// open and stat the admininistrative dir
int fd = openat(fPackagesDirectory->DirectoryFD(),
kAdministrativeDirectoryName, O_RDONLY);
if (fd < 0) {
ERROR("Failed to open administrative directory: %s\n", strerror(errno));
RETURN_ERROR(errno);
}
struct stat adminDirStat;
if (fstat(fd, &adminDirStat) < 0) {
ERROR("Failed to fstat() administrative directory: %s\n",
strerror(errno));
RETURN_ERROR(errno);
}
// iterate through the "administrative" dir
DIR* dir = fdopendir(fd);
if (dir == NULL) {
ERROR("Failed to open administrative directory: %s\n", strerror(errno));
RETURN_ERROR(errno);
}
CObjectDeleter<DIR, int> dirCloser(dir, closedir);
while (dirent* entry = readdir(dir)) {
if (strncmp(entry->d_name, "state_", 6) != 0
|| strcmp(entry->d_name, packagesState) < 0) {
continue;
}
PackagesDirectory* packagesDirectory
= new(std::nothrow) PackagesDirectory;
status_t error = packagesDirectory->InitOldState(adminDirStat.st_dev,
adminDirStat.st_ino, entry->d_name);
if (error != B_OK) {
delete packagesDirectory;
continue;
}
fPackagesDirectories.Add(packagesDirectory);
fPackagesDirectoriesByNodeRef.Insert(packagesDirectory);
INFORM("added old packages dir state \"%s\"\n",
packagesDirectory->StateName().Data());
}
// sort the packages directories by state age
fPackagesDirectories.Sort(&PackagesDirectory::IsNewer);
return B_OK;
}
status_t
Volume::_AddInitialPackages()
{
PackagesDirectory* packagesDirectory = fPackagesDirectories.Last();
INFORM("Adding packages from \"%s\"\n", packagesDirectory->Path());
// try reading the activation file of the oldest state
status_t error = _AddInitialPackagesFromActivationFile(packagesDirectory);
if (error != B_OK && packagesDirectory != fPackagesDirectory) {
WARN("Loading packages from old state \"%s\" failed. Loading packages "
"from latest state.\n", packagesDirectory->StateName().Data());
// remove all packages already added
{
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
_RemoveAllPackages();
}
// remove the old states
while (fPackagesDirectories.Last() != fPackagesDirectory)
fPackagesDirectories.RemoveTail()->ReleaseReference();
// try reading the activation file of the latest state
packagesDirectory = fPackagesDirectory;
error = _AddInitialPackagesFromActivationFile(packagesDirectory);
}
if (error != B_OK) {
INFORM("Loading packages from activation file failed. Loading all "
"packages in packages directory.\n");
// remove all packages already added
{
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
_RemoveAllPackages();
}
// read the whole directory
error = _AddInitialPackagesFromDirectory();
if (error != B_OK)
RETURN_ERROR(error);
}
// add the packages to the node tree
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator();
Package* package = it.Next();) {
error = _AddPackageContent(package, false);
if (error != B_OK) {
for (it.Rewind(); Package* activePackage = it.Next();) {
if (activePackage == package)
break;
_RemovePackageContent(activePackage, NULL, false);
}
RETURN_ERROR(error);
}
}
return B_OK;
}
status_t
Volume::_AddInitialPackagesFromActivationFile(
PackagesDirectory* packagesDirectory)
{
// try reading the activation file
int fd = openat(packagesDirectory->DirectoryFD(),
packagesDirectory == fPackagesDirectory
? kActivationFilePath : kActivationFileName,
O_RDONLY);
if (fd < 0) {
INFORM("Failed to open packages activation file: %s\n",
strerror(errno));
RETURN_ERROR(errno);
}
FileDescriptorCloser fdCloser(fd);
// read the whole file into memory to simplify things
struct stat st;
if (fstat(fd, &st) != 0) {
ERROR("Failed to stat packages activation file: %s\n",
strerror(errno));
RETURN_ERROR(errno);
}
if (st.st_size > (off_t)kMaxActivationFileSize) {
ERROR("The packages activation file is too big.\n");
RETURN_ERROR(B_BAD_DATA);
}
char* fileContent = (char*)malloc(st.st_size + 1);
if (fileContent == NULL)
RETURN_ERROR(B_NO_MEMORY);
MemoryDeleter fileContentDeleter(fileContent);
ssize_t bytesRead = read(fd, fileContent, st.st_size);
if (bytesRead < 0) {
ERROR("Failed to read packages activation file: %s\n", strerror(errno));
RETURN_ERROR(errno);
}
if (bytesRead != st.st_size) {
ERROR("Failed to read whole packages activation file\n");
RETURN_ERROR(B_ERROR);
}
// null-terminate to simplify parsing
fileContent[st.st_size] = '\0';
// parse the file and add the respective packages
const char* packageName = fileContent;
char* const fileContentEnd = fileContent + st.st_size;
while (packageName < fileContentEnd) {
char* packageNameEnd = strchr(packageName, '\n');
if (packageNameEnd == NULL)
packageNameEnd = fileContentEnd;
// skip empty lines
if (packageName == packageNameEnd) {
packageName++;
continue;
}
*packageNameEnd = '\0';
if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) {
ERROR("Invalid packages activation file content.\n");
RETURN_ERROR(B_BAD_DATA);
}
status_t error = _LoadAndAddInitialPackage(packagesDirectory,
packageName);
if (error != B_OK)
RETURN_ERROR(error);
packageName = packageNameEnd + 1;
}
return B_OK;
}
status_t
Volume::_AddInitialPackagesFromDirectory()
{
// iterate through the dir and create packages
int fd = openat(fPackagesDirectory->DirectoryFD(), ".", O_RDONLY);
if (fd < 0) {
ERROR("Failed to open packages directory: %s\n", strerror(errno));
RETURN_ERROR(errno);
}
DIR* dir = fdopendir(fd);
if (dir == NULL) {
ERROR("Failed to open packages directory \"%s\": %s\n",
fPackagesDirectory->Path(), strerror(errno));
RETURN_ERROR(errno);
}
CObjectDeleter<DIR, int> dirCloser(dir, closedir);
while (dirent* entry = readdir(dir)) {
// skip "." and ".."
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
// also skip any entry without a ".hpkg" extension
size_t nameLength = strlen(entry->d_name);
if (nameLength < 5
|| memcmp(entry->d_name + nameLength - 5, ".hpkg", 5) != 0) {
continue;
}
_LoadAndAddInitialPackage(fPackagesDirectory, entry->d_name);
}
return B_OK;
}
status_t
Volume::_LoadAndAddInitialPackage(PackagesDirectory* packagesDirectory,
const char* name)
{
Package* package;
status_t error = _LoadPackage(packagesDirectory, name, package);
if (error != B_OK) {
ERROR("Failed to load package \"%s\": %s\n", name, strerror(error));
RETURN_ERROR(error);
}
BReference<Package> packageReference(package, true);
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
_AddPackage(package);
return B_OK;
}
inline void
Volume::_AddPackage(Package* package)
{
fPackages.Insert(package);
package->AcquireReference();
}
inline void
Volume::_RemovePackage(Package* package)
{
fPackages.Remove(package);
package->ReleaseReference();
}
void
Volume::_RemoveAllPackages()
{
Package* package = fPackages.Clear(true);
while (package != NULL) {
Package* next = package->FileNameHashTableNext();
package->ReleaseReference();
package = next;
}
}
inline Package*
Volume::_FindPackage(const char* fileName) const
{
return fPackages.Lookup(fileName);
}
status_t
Volume::_AddPackageContent(Package* package, bool notify)
{
// Open the package. We don't need the FD here, but this is an optimization.
// The attribute indices may want to read the package nodes' attributes and
// the package file would be opened and closed for each attribute instance.
// Since Package keeps and shares the FD as long as at least one party has
// the package open, we prevent that.
int fd = package->Open();
if (fd < 0)
RETURN_ERROR(fd);
PackageCloser packageCloser(package);
status_t error = fPackageFSRoot->AddPackage(package);
if (error != B_OK)
RETURN_ERROR(error);
for (PackageNodeList::Iterator it = package->Nodes().GetIterator();
PackageNode* node = it.Next();) {
// skip over ".PackageInfo" file, it isn't part of the package content
if (strcmp(node->Name(),
BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
continue;
}
error = _AddPackageContentRootNode(package, node, notify);
if (error != B_OK) {
_RemovePackageContent(package, node, notify);
RETURN_ERROR(error);
}
}
return B_OK;
}
void
Volume::_RemovePackageContent(Package* package, PackageNode* endNode,
bool notify)
{
PackageNode* node = package->Nodes().Head();
while (node != NULL) {
if (node == endNode)
break;
PackageNode* nextNode = package->Nodes().GetNext(node);
// skip over ".PackageInfo" file, it isn't part of the package content
if (strcmp(node->Name(),
BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) != 0) {
_RemovePackageContentRootNode(package, node, NULL, notify);
}
node = nextNode;
}
fPackageFSRoot->RemovePackage(package);;
}
/*! This method recursively iterates through the descendents of the given
package root node and adds all package nodes to the node tree in
pre-order.
Due to limited kernel stack space we avoid deep recursive function calls
and rather use the package node stack implied by the tree.
*/
status_t
Volume::_AddPackageContentRootNode(Package* package,
PackageNode* rootPackageNode, bool notify)
{
PackageNode* packageNode = rootPackageNode;
Directory* directory = fRootDirectory;
directory->WriteLock();
do {
Node* node;
status_t error = _AddPackageNode(directory, packageNode, notify, node);
// returns B_OK with a NULL node, when skipping the node
if (error != B_OK) {
// unlock all directories
while (directory != NULL) {
directory->WriteUnlock();
directory = directory->Parent();
}
// remove the added package nodes
_RemovePackageContentRootNode(package, rootPackageNode, packageNode,
notify);
RETURN_ERROR(error);
}
// recurse into directory, unless we're supposed to skip the node
if (node != NULL) {
if (PackageDirectory* packageDirectory
= dynamic_cast<PackageDirectory*>(packageNode)) {
if (packageDirectory->FirstChild() != NULL) {
directory = dynamic_cast<Directory*>(node);
packageNode = packageDirectory->FirstChild();
directory->WriteLock();
continue;
}
}
}
// continue with the next available (ancestors's) sibling
do {
PackageDirectory* packageDirectory = packageNode->Parent();
PackageNode* sibling = packageDirectory != NULL
? packageDirectory->NextChild(packageNode) : NULL;
if (sibling != NULL) {
packageNode = sibling;
break;
}
// no more siblings -- go back up the tree
packageNode = packageDirectory;
directory->WriteUnlock();
directory = directory->Parent();
// the parent is still locked, so this is safe
} while (packageNode != NULL);
} while (packageNode != NULL);
return B_OK;
}
/*! Recursively iterates through the descendents of the given package root node
and removes all package nodes from the node tree in post-order, until
encountering \a endPackageNode (if non-null).
Due to limited kernel stack space we avoid deep recursive function calls
and rather use the package node stack implied by the tree.
*/
void
Volume::_RemovePackageContentRootNode(Package* package,
PackageNode* rootPackageNode, PackageNode* endPackageNode, bool notify)
{
PackageNode* packageNode = rootPackageNode;
Directory* directory = fRootDirectory;
directory->WriteLock();
do {
if (packageNode == endPackageNode)
break;
// recurse into directory
if (PackageDirectory* packageDirectory
= dynamic_cast<PackageDirectory*>(packageNode)) {
if (packageDirectory->FirstChild() != NULL) {
if (Directory* childDirectory = dynamic_cast<Directory*>(
directory->FindChild(packageNode->Name()))) {
directory = childDirectory;
packageNode = packageDirectory->FirstChild();
directory->WriteLock();
continue;
}
}
}
// continue with the next available (ancestors's) sibling
do {
PackageDirectory* packageDirectory = packageNode->Parent();
PackageNode* sibling = packageDirectory != NULL
? packageDirectory->NextChild(packageNode) : NULL;
// we're done with the node -- remove it
_RemovePackageNode(directory, packageNode,
directory->FindChild(packageNode->Name()), notify);
if (sibling != NULL) {
packageNode = sibling;
break;
}
// no more siblings -- go back up the tree
packageNode = packageDirectory;
directory->WriteUnlock();
directory = directory->Parent();
// the parent is still locked, so this is safe
} while (packageNode != NULL/* && packageNode != rootPackageNode*/);
} while (packageNode != NULL/* && packageNode != rootPackageNode*/);
}
status_t
Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode,
bool notify, Node*& _node)
{
bool newNode = false;
UnpackingNode* unpackingNode;
Node* node = directory->FindChild(packageNode->Name());
PackageNode* oldPackageNode = NULL;
if (node != NULL) {
unpackingNode = dynamic_cast<UnpackingNode*>(node);
if (unpackingNode == NULL) {
_node = NULL;
return B_OK;
}
oldPackageNode = unpackingNode->GetPackageNode();
} else {
status_t error = _CreateUnpackingNode(packageNode->Mode(), directory,
packageNode->Name(), unpackingNode);
if (error != B_OK)
RETURN_ERROR(error);
node = unpackingNode->GetNode();
newNode = true;
}
BReference<Node> nodeReference(node);
NodeWriteLocker nodeWriteLocker(node);
BReference<Node> newNodeReference;
NodeWriteLocker newNodeWriteLocker;
Node* oldNode = NULL;
if (!newNode && !S_ISDIR(node->Mode()) && oldPackageNode != NULL
&& unpackingNode->WillBeFirstPackageNode(packageNode)) {
// The package node we're going to add will represent the node,
// replacing the current head package node. Since the node isn't a
// directory, we must make sure that clients having opened or mapped the
// node won't be surprised. So we create a new node and remove the
// current one.
// create a new node and transfer the package nodes to it
UnpackingNode* newUnpackingNode;
status_t error = unpackingNode->CloneTransferPackageNodes(
fNextNodeID++, newUnpackingNode);
if (error != B_OK)
RETURN_ERROR(error);
// remove the old node
_NotifyNodeRemoved(node);
_RemoveNodeAndVNode(node);
oldNode = node;
// add the new node
unpackingNode = newUnpackingNode;
node = unpackingNode->GetNode();
newNodeReference.SetTo(node);
newNodeWriteLocker.SetTo(node, false);
directory->AddChild(node);
fNodes.Insert(node);
newNode = true;
}
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.
if (newNode) {
if (oldNode != NULL) {
_NotifyNodeAdded(node);
if (notify) {
notify_entry_removed(ID(), directory->ID(), oldNode->Name(),
oldNode->ID());
notify_entry_created(ID(), directory->ID(), node->Name(),
node->ID());
}
} else
_RemoveNode(node);
}
RETURN_ERROR(error);
}
if (newNode) {
_NotifyNodeAdded(node);
} else if (packageNode == unpackingNode->GetPackageNode()) {
_NotifyNodeChanged(node, kAllStatFields,
OldUnpackingNodeAttributes(oldPackageNode));
}
if (notify) {
if (newNode) {
if (oldNode != NULL) {
notify_entry_removed(ID(), directory->ID(), oldNode->Name(),
oldNode->ID());
}
notify_entry_created(ID(), directory->ID(), node->Name(),
node->ID());
} else if (packageNode == unpackingNode->GetPackageNode()) {
// The new package node has become the one representing the node.
// Send stat changed notification for directories and entry
// removed + created notifications for files and symlinks.
notify_stat_changed(ID(), directory->ID(), node->ID(),
kAllStatFields);
// TODO: Actually the attributes might change, too!
}
}
_node = node;
return B_OK;
}
void
Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode,
Node* node, bool notify)
{
UnpackingNode* unpackingNode = dynamic_cast<UnpackingNode*>(node);
if (unpackingNode == NULL)
return;
BReference<Node> nodeReference(node);
NodeWriteLocker nodeWriteLocker(node);
PackageNode* headPackageNode = unpackingNode->GetPackageNode();
bool nodeRemoved = false;
Node* newNode = NULL;
BReference<Node> newNodeReference;
NodeWriteLocker newNodeWriteLocker;
// If this is the last package node of the node, remove it completely.
if (unpackingNode->IsOnlyPackageNode(packageNode)) {
// Notify before removing the node. Otherwise the indices might not
// find the node anymore.
_NotifyNodeRemoved(node);
unpackingNode->PrepareForRemoval();
_RemoveNodeAndVNode(node);
nodeRemoved = true;
} else if (packageNode == headPackageNode) {
// The node does at least have one more package node, but the one to be
// removed is the head. Unless it's a directory, we replace the node
// 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, ID());
_NotifyNodeChanged(node, kAllStatFields,
OldUnpackingNodeAttributes(headPackageNode));
} else {
// create a new node and transfer the package nodes to it
UnpackingNode* newUnpackingNode;
status_t error = unpackingNode->CloneTransferPackageNodes(
fNextNodeID++, newUnpackingNode);
if (error == B_OK) {
// remove the package node
newUnpackingNode->RemovePackageNode(packageNode, ID());
// remove the old node
_NotifyNodeRemoved(node);
_RemoveNodeAndVNode(node);
// add the new node
newNode = newUnpackingNode->GetNode();
newNodeReference.SetTo(newNode);
newNodeWriteLocker.SetTo(newNode, false);
directory->AddChild(newNode);
fNodes.Insert(newNode);
_NotifyNodeAdded(newNode);
} else {
// There's nothing we can do. Remove the node completely.
_NotifyNodeRemoved(node);
unpackingNode->PrepareForRemoval();
_RemoveNodeAndVNode(node);
nodeRemoved = true;
}
}
} else {
// The package node to remove is not the head of the node. This change
// doesn't have any visible effect.
unpackingNode->RemovePackageNode(packageNode, ID());
}
if (!notify)
return;
// send notifications
if (nodeRemoved) {
notify_entry_removed(ID(), directory->ID(), node->Name(), node->ID());
} else if (packageNode == headPackageNode) {
// The removed package node was the one representing the node.
// Send stat changed notification for directories and entry
// removed + created notifications for files and symlinks.
if (S_ISDIR(packageNode->Mode())) {
notify_stat_changed(ID(), directory->ID(), node->ID(),
kAllStatFields);
// TODO: Actually the attributes might change, too!
} else {
notify_entry_removed(ID(), directory->ID(), node->Name(),
node->ID());
notify_entry_created(ID(), directory->ID(), newNode->Name(),
newNode->ID());
}
}
}
status_t
Volume::_CreateUnpackingNode(mode_t mode, Directory* parent, const String& name,
UnpackingNode*& _node)
{
UnpackingNode* unpackingNode;
if (S_ISREG(mode) || S_ISLNK(mode))
unpackingNode = new(std::nothrow) UnpackingLeafNode(fNextNodeID++);
else if (S_ISDIR(mode))
unpackingNode = new(std::nothrow) UnpackingDirectory(fNextNodeID++);
else
RETURN_ERROR(B_UNSUPPORTED);
if (unpackingNode == NULL)
RETURN_ERROR(B_NO_MEMORY);
Node* node = unpackingNode->GetNode();
BReference<Node> nodeReference(node, true);
status_t error = node->Init(parent, name);
if (error != B_OK)
RETURN_ERROR(error);
parent->AddChild(node);
fNodes.Insert(node);
nodeReference.Detach();
// we keep the initial node reference for the table
_node = unpackingNode;
return B_OK;
}
void
Volume::_RemoveNode(Node* node)
{
// remove from parent
Directory* parent = node->Parent();
parent->RemoveChild(node);
// remove from node table
fNodes.Remove(node);
node->ReleaseReference();
}
void
Volume::_RemoveNodeAndVNode(Node* node)
{
// If the node is known to the VFS, we get the vnode, remove it, and put it,
// so that the VFS will discard it as soon as possible (i.e. now, if no one
// else is using it).
NodeWriteLocker nodeWriteLocker(node);
// Remove the node from its parent and the volume. This makes the node
// inaccessible via the get_vnode() and lookup() hooks.
_RemoveNode(node);
bool getVNode = node->IsKnownToVFS();
nodeWriteLocker.Unlock();
// Get a vnode reference, if the node is already known to the VFS.
Node* dummyNode;
if (getVNode && GetVNode(node->ID(), dummyNode) == B_OK) {
// TODO: There still is a race condition here which we can't avoid
// without more help from the VFS. Right after we drop the write
// lock a vnode for the node could be discarded by the VFS. At that
// point another thread trying to get the vnode by ID would create
// a vnode, mark it busy and call our get_vnode() hook. It would
// block since we (i.e. the package loader thread executing this
// method) still have the volume write lock. Our get_vnode() call
// would block, since it finds the vnode marked busy. It times out
// eventually, but until then a good deal of FS operations might
// block as well due to us holding the volume lock and probably
// several node locks as well. A get_vnode*() variant (e.g.
// get_vnode_etc() with flags parameter) that wouldn't block and
// only get the vnode, if already loaded and non-busy, would be
// perfect here.
RemoveVNode(node->ID());
PutVNode(node->ID());
}
}
status_t
Volume::_LoadPackage(PackagesDirectory* packagesDirectory, const char* name,
Package*& _package)
{
// Find the package -- check the specified packages directory and iterate
// toward the newer states.
struct stat st;
for (;;) {
if (packagesDirectory == NULL)
return B_ENTRY_NOT_FOUND;
if (fstatat(packagesDirectory->DirectoryFD(), name, &st, 0) == 0) {
// check whether the entry is a file
if (!S_ISREG(st.st_mode))
return B_BAD_VALUE;
break;
}
packagesDirectory = fPackagesDirectories.GetPrevious(packagesDirectory);
}
// create a package
Package* package = new(std::nothrow) Package(this, packagesDirectory,
st.st_dev, st.st_ino);
if (package == NULL)
RETURN_ERROR(B_NO_MEMORY);
BReference<Package> packageReference(package, true);
status_t error = package->Init(name);
if (error != B_OK)
return error;
error = package->Load(fPackageSettings);
if (error != B_OK)
return error;
_package = packageReference.Detach();
return B_OK;
}
status_t
Volume::_ChangeActivation(ActivationChangeRequest& request)
{
uint32 itemCount = request.CountItems();
if (itemCount == 0)
return B_OK;
// first check the request
int32 newPackageCount = 0;
int32 oldPackageCount = 0;
{
VolumeReadLocker volumeLocker(this);
for (uint32 i = 0; i < itemCount; i++) {
PackageFSActivationChangeItem* item = request.ItemAt(i);
if (item->parentDeviceID != fPackagesDirectory->DeviceID()
|| item->parentDirectoryID != fPackagesDirectory->NodeID()) {
ERROR("Volume::_ChangeActivation(): mismatching packages "
"directory\n");
RETURN_ERROR(B_MISMATCHED_VALUES);
}
Package* package = _FindPackage(item->name);
// TODO: We should better look up the package by node_ref!
if (item->type == PACKAGE_FS_ACTIVATE_PACKAGE) {
if (package != NULL) {
ERROR("Volume::_ChangeActivation(): package to activate "
"already activated: \"%s\"\n", item->name);
RETURN_ERROR(B_NAME_IN_USE);
}
newPackageCount++;
} else if (item->type == PACKAGE_FS_DEACTIVATE_PACKAGE) {
if (package == NULL) {
ERROR("Volume::_ChangeActivation(): package to deactivate "
"not found: \"%s\"\n", item->name);
RETURN_ERROR(B_NAME_NOT_FOUND);
}
oldPackageCount++;
} else if (item->type == PACKAGE_FS_REACTIVATE_PACKAGE) {
if (package == NULL) {
ERROR("Volume::_ChangeActivation(): package to reactivate "
"not found: \"%s\"\n", item->name);
RETURN_ERROR(B_NAME_NOT_FOUND);
}
oldPackageCount++;
newPackageCount++;
} else
RETURN_ERROR(B_BAD_VALUE);
}
}
INFORM("Volume::_ChangeActivation(): %" B_PRId32 " new packages, %" B_PRId32
" old packages\n", newPackageCount, oldPackageCount);
// Things look good so far -- allocate reference arrays for the packages to
// add and remove.
BReference<Package>* newPackageReferences
= new(std::nothrow) BReference<Package>[newPackageCount];
if (newPackageReferences == NULL)
RETURN_ERROR(B_NO_MEMORY);
ArrayDeleter<BReference<Package> > newPackageReferencesDeleter(
newPackageReferences);
BReference<Package>* oldPackageReferences
= new(std::nothrow) BReference<Package>[oldPackageCount];
if (oldPackageReferences == NULL)
RETURN_ERROR(B_NO_MEMORY);
ArrayDeleter<BReference<Package> > oldPackageReferencesDeleter(
oldPackageReferences);
// load all new packages
int32 newPackageIndex = 0;
for (uint32 i = 0; i < itemCount; i++) {
PackageFSActivationChangeItem* item = request.ItemAt(i);
if (item->type != PACKAGE_FS_ACTIVATE_PACKAGE
&& item->type != PACKAGE_FS_REACTIVATE_PACKAGE) {
continue;
}
Package* package;
status_t error = _LoadPackage(fPackagesDirectory, item->name, package);
if (error != B_OK) {
ERROR("Volume::_ChangeActivation(): failed to load package "
"\"%s\"\n", item->name);
RETURN_ERROR(error);
}
newPackageReferences[newPackageIndex++].SetTo(package, true);
}
// apply the changes
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
// TODO: Add a change counter to Volume, so we can easily check whether
// everything is still the same.
// remove the old packages
int32 oldPackageIndex = 0;
for (uint32 i = 0; i < itemCount; i++) {
PackageFSActivationChangeItem* item = request.ItemAt(i);
if (item->type != PACKAGE_FS_DEACTIVATE_PACKAGE
&& item->type != PACKAGE_FS_REACTIVATE_PACKAGE) {
continue;
}
Package* package = _FindPackage(item->name);
// TODO: We should better look up the package by node_ref!
oldPackageReferences[oldPackageIndex++].SetTo(package);
_RemovePackageContent(package, NULL, true);
_RemovePackage(package);
INFORM("package \"%s\" deactivated\n", package->FileName().Data());
}
// TODO: Since package removal cannot fail, consider adding the new packages
// first. The reactivation case may make that problematic, since two packages
// with the same name would be active after activating the new one. Check!
// add the new packages
status_t error = B_OK;
for (newPackageIndex = 0; newPackageIndex < newPackageCount;
newPackageIndex++) {
Package* package = newPackageReferences[newPackageIndex];
_AddPackage(package);
// add the package to the node tree
error = _AddPackageContent(package, true);
if (error != B_OK) {
_RemovePackage(package);
break;
}
INFORM("package \"%s\" activated\n", package->FileName().Data());
}
// Try to roll back the changes, if an error occurred.
if (error != B_OK) {
for (int32 i = newPackageIndex - 1; i >= 0; i--) {
Package* package = newPackageReferences[i];
_RemovePackageContent(package, NULL, true);
_RemovePackage(package);
}
for (int32 i = oldPackageCount - 1; i >= 0; i--) {
Package* package = oldPackageReferences[i];
_AddPackage(package);
if (_AddPackageContent(package, true) != B_OK) {
// nothing we can do here
ERROR("Volume::_ChangeActivation(): failed to roll back "
"deactivation of package \"%s\" after error\n",
package->FileName().Data());
_RemovePackage(package);
}
}
}
return error;
}
status_t
Volume::_InitMountType(const char* mountType)
{
if (mountType == NULL)
fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM;
else if (strcmp(mountType, "system") == 0)
fMountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM;
else if (strcmp(mountType, "home") == 0)
fMountType = PACKAGE_FS_MOUNT_TYPE_HOME;
else if (strcmp(mountType, "custom") == 0)
fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM;
else
RETURN_ERROR(B_BAD_VALUE);
return B_OK;
}
status_t
Volume::_CreateShineThroughDirectory(Directory* parent, const char* name,
Directory*& _directory)
{
ShineThroughDirectory* directory = new(std::nothrow) ShineThroughDirectory(
fNextNodeID++);
if (directory == NULL)
RETURN_ERROR(B_NO_MEMORY);
BReference<ShineThroughDirectory> directoryReference(directory, true);
String nameString;
if (!nameString.SetTo(name))
RETURN_ERROR(B_NO_MEMORY);
status_t error = directory->Init(parent, nameString);
if (error != B_OK)
RETURN_ERROR(error);
parent->AddChild(directory);
fNodes.Insert(directory);
directoryReference.Detach();
// we keep the initial node reference for the table
_directory = directory;
return B_OK;
}
status_t
Volume::_CreateShineThroughDirectories(const char* shineThroughSetting)
{
// get the directories to map
const char* const* directories = NULL;
if (shineThroughSetting == NULL) {
// nothing specified -- derive from mount type
switch (fMountType) {
case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
case PACKAGE_FS_MOUNT_TYPE_HOME:
directories = kShineThroughDirectories;
break;
case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
return B_OK;
case PACKAGE_FS_MOUNT_TYPE_ENUM_COUNT:
return B_BAD_VALUE;
}
} else if (strcmp(shineThroughSetting, "system") == 0)
directories = kShineThroughDirectories;
else if (strcmp(shineThroughSetting, "home") == 0)
directories = kShineThroughDirectories;
else if (strcmp(shineThroughSetting, "none") == 0)
directories = NULL;
else
RETURN_ERROR(B_BAD_VALUE);
if (directories == NULL)
return B_OK;
// iterate through the directory list and create the directories
while (const char* directoryName = *(directories++)) {
// create the directory
Directory* directory;
status_t error = _CreateShineThroughDirectory(fRootDirectory,
directoryName, directory);
if (error != B_OK)
RETURN_ERROR(error);
}
return B_OK;
}
status_t
Volume::_PublishShineThroughDirectories()
{
// Iterate through the root directory children and bind the shine-through
// directories to the respective mount point subdirectories.
Node* nextNode;
for (Node* node = fRootDirectory->FirstChild(); node != NULL;
node = nextNode) {
nextNode = fRootDirectory->NextChild(node);
// skip anything but shine-through directories
ShineThroughDirectory* directory
= dynamic_cast<ShineThroughDirectory*>(node);
if (directory == NULL)
continue;
const char* directoryName = directory->Name();
// look up the mount point subdirectory
struct vnode* vnode;
status_t error = vfs_entry_ref_to_vnode(fMountPoint.deviceID,
fMountPoint.nodeID, directoryName, &vnode);
if (error != B_OK) {
dprintf("packagefs: Failed to get shine-through directory \"%s\": "
"%s\n", directoryName, strerror(error));
_RemoveNode(directory);
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));
_RemoveNode(directory);
continue;
}
if (!S_ISDIR(st.st_mode)) {
dprintf("packagefs: Shine-through entry \"%s\" is not a "
"directory\n", directoryName);
_RemoveNode(directory);
continue;
}
// publish the vnode, so the VFS will find it without asking us
directory->AcquireReference();
error = PublishVNode(directory);
if (error != B_OK) {
directory->ReleaseReference();
_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;
}
status_t
Volume::_AddPackageLinksDirectory()
{
// called when mounting, so we don't need to lock the volume
PackageLinksDirectory* packageLinksDirectory
= fPackageFSRoot->GetPackageLinksDirectory();
NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory);
NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
packageLinksDirectory->SetParent(fRootDirectory);
fRootDirectory->AddChild(packageLinksDirectory);
_AddPackageLinksNode(packageLinksDirectory);
packageLinksDirectory->SetListener(this);
return B_OK;
}
void
Volume::_RemovePackageLinksDirectory()
{
PackageLinksDirectory* packageLinksDirectory
= fPackageFSRoot->GetPackageLinksDirectory();
VolumeWriteLocker volumeLocker(this);
NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory);
NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
if (packageLinksDirectory->Parent() == fRootDirectory) {
packageLinksDirectory->SetListener(NULL);
fRootDirectory->RemoveChild(packageLinksDirectory);
packageLinksDirectory->SetParent(NULL);
}
}
void
Volume::_AddPackageLinksNode(Node* node)
{
node->SetID(fNextNodeID++);
fNodes.Insert(node);
node->AcquireReference();
// If this is a directory, recursively add descendants. The directory tree
// for the package links isn't deep, so we can do recursion.
if (Directory* directory = dynamic_cast<Directory*>(node)) {
for (Node* child = directory->FirstChild(); child != NULL;
child = directory->NextChild(child)) {
NodeWriteLocker childWriteLocker(child);
_AddPackageLinksNode(child);
}
}
}
void
Volume::_RemovePackageLinksNode(Node* node)
{
// If this is a directory, recursively remove descendants. The directory
// tree for the package links isn't deep, so we can do recursion.
if (Directory* directory = dynamic_cast<Directory*>(node)) {
for (Node* child = directory->FirstChild(); child != NULL;
child = directory->NextChild(child)) {
NodeWriteLocker childWriteLocker(child);
_RemovePackageLinksNode(child);
}
}
fNodes.Remove(node);
node->ReleaseReference();
}
inline Volume*
Volume::_SystemVolumeIfNotSelf() const
{
if (Volume* systemVolume = fPackageFSRoot->SystemVolume())
return systemVolume == this ? NULL : systemVolume;
return NULL;
}
void
Volume::_NotifyNodeAdded(Node* node)
{
Node* key = node;
for (int i = 0; i < 2; i++) {
if (NodeListener* listener = fNodeListeners.Lookup(key)) {
NodeListener* last = listener->PreviousNodeListener();
while (true) {
NodeListener* next = listener->NextNodeListener();
listener->NodeAdded(node);
if (listener == last)
break;
listener = next;
}
}
key = NULL;
}
}
void
Volume::_NotifyNodeRemoved(Node* node)
{
Node* key = node;
for (int i = 0; i < 2; i++) {
if (NodeListener* listener = fNodeListeners.Lookup(key)) {
NodeListener* last = listener->PreviousNodeListener();
while (true) {
NodeListener* next = listener->NextNodeListener();
listener->NodeRemoved(node);
if (listener == last)
break;
listener = next;
}
}
key = NULL;
}
}
void
Volume::_NotifyNodeChanged(Node* node, uint32 statFields,
const OldNodeAttributes& oldAttributes)
{
Node* key = node;
for (int i = 0; i < 2; i++) {
if (NodeListener* listener = fNodeListeners.Lookup(key)) {
NodeListener* last = listener->PreviousNodeListener();
while (true) {
NodeListener* next = listener->NextNodeListener();
listener->NodeChanged(node, statFields, oldAttributes);
if (listener == last)
break;
listener = next;
}
}
key = NULL;
}
}
↑ V773 The function was exited without releasing the 'unpackingNode' pointer. A memory leak is possible.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fMountType, fMountPoint.