* 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 <AutoDeleterPosix.h>
#include <AutoDeleterDrivers.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"
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;
const char* const kShineThroughDirectories[] = {
"cache", "non-packaged", "packages", "settings", "var", NULL
};
const size_t kMaxActivationRequestSize = 10 * 1024 * 1024;
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;
struct Volume::ShineThroughDirectory : public Directory {
ShineThroughDirectory(ino_t id)
:
Directory(id)
{
get_real_time(fModifiedTime);
}
virtual timespec ModifiedTime() const
{
return fModifiedTime;
}
private:
timespec fModifiedTime;
};
struct Volume::ActivationChangeRequest {
public:
ActivationChangeRequest()
:
fRequest(NULL),
fRequestSize(0)
{
}
~ActivationChangeRequest()
{
free(fRequest);
}
status_t Init(const void* userRequest, size_t requestSize)
{
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);
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;
};
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()
{
{
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator();
Package* package = it.Next();) {
_RemovePackageContent(package, NULL, false);
}
}
_RemoveAllPackages();
Index* index = fIndices.Clear(true);
while (index != NULL) {
Index* next = index->IndexHashLink();
delete index;
index = next;
}
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)
{
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);
{
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);
}
{
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);
}
{
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);
}
{
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);
}
const char* packages = NULL;
const char* volumeName = NULL;
const char* mountType = NULL;
const char* shineThrough = NULL;
const char* packagesState = NULL;
DriverSettingsUnloader parameterHandle(
parse_driver_settings_string(parameterString));
if (parameterHandle.IsSet()) {
packages = get_driver_parameter(parameterHandle.Get(), "packages",
NULL, NULL);
volumeName = get_driver_parameter(parameterHandle.Get(), "volume-name",
NULL, NULL);
mountType = get_driver_parameter(parameterHandle.Get(), "type", NULL,
NULL);
shineThrough = get_driver_parameter(parameterHandle.Get(),
"shine-through", NULL, NULL);
packagesState = get_driver_parameter(parameterHandle.Get(), "state",
NULL, NULL);
}
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);
}
error = vfs_get_mount_point(fFSVolume->id, &fMountPoint.deviceID,
&fMountPoint.nodeID);
if (error != B_OK)
RETURN_ERROR(error);
error = fPackageSettings.Load(fMountPoint.deviceID, fMountPoint.nodeID,
fMountType);
if (error == B_NO_MEMORY)
RETURN_ERROR(error);
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 (packagesState != NULL) {
error = _LoadOldPackagesStates(packagesState);
if (error != B_OK)
RETURN_ERROR(error);
}
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);
fRootDirectory
= new ::RootDirectory(kRootDirectoryID, st.st_mtim);
if (fRootDirectory == NULL)
RETURN_ERROR(B_NO_MEMORY);
fRootDirectory->Init(volumeNameString);
fNodes.Insert(fRootDirectory);
fRootDirectory->AcquireReference();
error = ::PackageFSRoot::RegisterVolume(this);
if (error != B_OK)
RETURN_ERROR(error);
if (this == fPackageFSRoot->SystemVolume()) {
error = _AddPackageLinksDirectory();
if (error != B_OK)
RETURN_ERROR(error);
}
error = _CreateShineThroughDirectories(shineThrough);
if (error != B_OK)
RETURN_ERROR(error);
error = _AddInitialPackages();
if (error != B_OK)
RETURN_ERROR(error);
fRootDirectory->AcquireReference();
error = PublishVNode(fRootDirectory);
if (error != B_OK) {
fRootDirectory->ReleaseReference();
RETURN_ERROR(error);
}
error = _PublishShineThroughDirectories();
if (error != B_OK)
RETURN_ERROR(error);
StringPool::DumpUsageStatistics();
return B_OK;
}
void
Volume::Unmount()
{
PutVNode(fRootDirectory->ID());
}
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_WRITE_LOCKED_RW_LOCK(&fLock);
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_WRITE_LOCKED_RW_LOCK(&fLock);
ASSERT(listener->IsListening());
Node* node = listener->ListenedNode();
if (NodeListener* next = listener->RemoveNodeListener()) {
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)
{
ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
fQueries.Add(query);
}
void
Volume::RemoveQuery(Query* query)
{
ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
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)
{
ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
_AddPackageLinksNode(node);
notify_entry_created(ID(), node->GetParentUnchecked()->ID(), node->Name(), node->ID());
_NotifyNodeAdded(node);
}
void
Volume::PackageLinkNodeRemoved(Node* node)
{
ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
_RemovePackageLinksNode(node);
notify_entry_removed(ID(), node->GetParentUnchecked()->ID(), node->Name(), node->ID());
_NotifyNodeRemoved(node);
}
void
Volume::PackageLinkNodeChanged(Node* node, uint32 statFields,
const OldNodeAttributes& oldAttributes)
{
ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
Directory* parent = node->GetParentUnchecked();
notify_stat_changed(ID(), parent != NULL ? parent->ID() : -1, node->ID(),
statFields);
_NotifyNodeChanged(node, statFields, oldAttributes);
}
status_t
Volume::_LoadOldPackagesStates(const char* packagesState)
{
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);
}
DirCloser dir(fdopendir(fd));
if (!dir.IsSet()) {
ERROR("Failed to open administrative directory: %s\n", strerror(errno));
RETURN_ERROR(errno);
}
while (dirent* entry = readdir(dir.Get())) {
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());
}
fPackagesDirectories.Sort(&PackagesDirectory::IsNewer);
return B_OK;
}
status_t
Volume::_AddInitialPackages()
{
PackagesDirectory* packagesDirectory = fPackagesDirectories.Last();
INFORM("Adding packages from \"%s\"\n", packagesDirectory->Path());
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());
{
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
_RemoveAllPackages();
}
while (fPackagesDirectories.Last() != fPackagesDirectory)
fPackagesDirectories.RemoveTail()->ReleaseReference();
packagesDirectory = fPackagesDirectory;
error = _AddInitialPackagesFromActivationFile(packagesDirectory);
}
if (error != B_OK) {
INFORM("Loading packages from activation file failed. Loading all "
"packages in packages directory.\n");
{
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
_RemoveAllPackages();
}
error = _AddInitialPackagesFromDirectory();
if (error != B_OK)
RETURN_ERROR(error);
}
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)
{
FileDescriptorCloser fd(openat(packagesDirectory->DirectoryFD(),
packagesDirectory == fPackagesDirectory
? kActivationFilePath : kActivationFileName,
O_RDONLY));
if (!fd.IsSet()) {
INFORM("Failed to open packages activation file: %s\n",
strerror(errno));
RETURN_ERROR(errno);
}
struct stat st;
if (fstat(fd.Get(), &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.Get(), 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);
}
fileContent[st.st_size] = '\0';
const char* packageName = fileContent;
char* const fileContentEnd = fileContent + st.st_size;
while (packageName < fileContentEnd) {
char* packageNameEnd = strchr(packageName, '\n');
if (packageNameEnd == NULL)
packageNameEnd = fileContentEnd;
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()
{
int fd = openat(fPackagesDirectory->DirectoryFD(), ".", O_RDONLY);
if (fd < 0) {
ERROR("Failed to open packages directory: %s\n", strerror(errno));
RETURN_ERROR(errno);
}
DirCloser dir(fdopendir(fd));
if (!dir.IsSet()) {
ERROR("Failed to open packages directory \"%s\": %s\n",
fPackagesDirectory->Path(), strerror(errno));
RETURN_ERROR(errno);
}
while (dirent* entry = readdir(dir.Get())) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
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)
{
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::ConstIterator it = package->Nodes().GetIterator();
PackageNode* node = it.Next();) {
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);
if (strcmp(node->Name(),
BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) != 0) {
_RemovePackageContentRootNode(package, node, NULL, notify);
}
node = nextNode;
}
fPackageFSRoot->RemovePackage(package);
}
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)
{
ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
PackageNode* packageNode = rootPackageNode;
Directory* directory = fRootDirectory;
directory->WriteLock();
do {
Node* node;
status_t error = _AddPackageNode(directory, packageNode, notify, node);
if (error != B_OK) {
while (directory != NULL) {
directory->WriteUnlock();
directory = directory->GetParentUnchecked();
}
_RemovePackageContentRootNode(package, rootPackageNode, packageNode,
notify);
RETURN_ERROR(error);
}
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;
}
}
}
do {
PackageDirectory* packageDirectory = packageNode->Parent();
PackageNode* sibling = packageDirectory != NULL
? packageDirectory->NextChild(packageNode) : NULL;
if (sibling != NULL) {
packageNode = sibling;
break;
}
packageNode = packageDirectory;
directory->WriteUnlock();
directory = directory->GetParentUnchecked();
} while (packageNode != NULL);
} while (packageNode != NULL);
ASSERT(directory == NULL);
return B_OK;
}
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)
{
ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
PackageNode* packageNode = rootPackageNode;
Directory* directory = fRootDirectory;
directory->WriteLock();
do {
if (packageNode == endPackageNode) {
while (directory != NULL) {
directory->WriteUnlock();
directory = directory->GetParentUnchecked();
}
break;
}
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;
}
}
}
do {
PackageDirectory* packageDirectory = packageNode->Parent();
PackageNode* sibling = packageDirectory != NULL
? packageDirectory->NextChild(packageNode) : NULL;
_RemovePackageNode(directory, packageNode,
directory->FindChild(packageNode->Name()), notify);
if (sibling != NULL) {
packageNode = sibling;
break;
}
packageNode = packageDirectory;
directory->WriteUnlock();
directory = directory->GetParentUnchecked();
} while (packageNode != NULL);
} while (packageNode != NULL);
ASSERT(directory == NULL);
}
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);
DirectoryWriteLocker directoryNodeWriteLocker;
if (Directory* directory = dynamic_cast<Directory*>(node))
directoryNodeWriteLocker.SetTo(directory, false, true);
BReference<Node> newNodeReference;
DirectoryWriteLocker newDirectoryNodeWriteLocker;
Node* oldNode = NULL;
if (!newNode && !S_ISDIR(node->Mode()) && oldPackageNode != NULL
&& unpackingNode->WillBeFirstPackageNode(packageNode)) {
UnpackingNode* newUnpackingNode;
status_t error = unpackingNode->CloneTransferPackageNodes(
fNextNodeID++, newUnpackingNode);
if (error != B_OK)
RETURN_ERROR(error);
_NotifyNodeRemoved(node);
_RemoveNodeAndVNode(node);
oldNode = node;
unpackingNode = newUnpackingNode;
node = unpackingNode->GetNode();
newNodeReference.SetTo(node);
if (Directory* newDirectory = dynamic_cast<Directory*>(node))
newDirectoryNodeWriteLocker.SetTo(newDirectory, false, true);
directory->AddChild(node);
fNodes.Insert(node);
newNode = true;
}
status_t error = unpackingNode->AddPackageNode(packageNode, ID());
if (error != B_OK) {
dprintf("packagefs: Failed to add node \"%s\" of package \"%s\": %s\n",
packageNode->Name().Data(), packageNode->GetPackage()->Name().Data(),
strerror(error));
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()) {
notify_stat_changed(ID(), directory->ID(), node->ID(),
kAllStatFields);
}
}
_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);
DirectoryWriteLocker directoryNodeWriteLocker;
if (Directory* directory = dynamic_cast<Directory*>(node))
directoryNodeWriteLocker.SetTo(directory, false, true);
PackageNode* headPackageNode = unpackingNode->GetPackageNode();
bool nodeRemoved = false;
Node* newNode = NULL;
BReference<Node> newNodeReference;
DirectoryWriteLocker newDirectoryNodeWriteLocker;
if (unpackingNode->IsOnlyPackageNode(packageNode)) {
_NotifyNodeRemoved(node);
unpackingNode->PrepareForRemoval();
_RemoveNodeAndVNode(node);
nodeRemoved = true;
} else if (packageNode == headPackageNode) {
if (S_ISDIR(packageNode->Mode())) {
unpackingNode->RemovePackageNode(packageNode, ID());
_NotifyNodeChanged(node, kAllStatFields,
OldUnpackingNodeAttributes(headPackageNode));
} else {
UnpackingNode* newUnpackingNode;
status_t error = unpackingNode->CloneTransferPackageNodes(
fNextNodeID++, newUnpackingNode);
if (error == B_OK) {
newUnpackingNode->RemovePackageNode(packageNode, ID());
_NotifyNodeRemoved(node);
_RemoveNodeAndVNode(node);
newNode = newUnpackingNode->GetNode();
newNodeReference.SetTo(newNode);
if (Directory* newDirectory = dynamic_cast<Directory*>(newNode))
newDirectoryNodeWriteLocker.SetTo(newDirectory, false, true);
directory->AddChild(newNode);
fNodes.Insert(newNode);
_NotifyNodeAdded(newNode);
} else {
_NotifyNodeRemoved(node);
unpackingNode->PrepareForRemoval();
_RemoveNodeAndVNode(node);
nodeRemoved = true;
}
}
} else {
unpackingNode->RemovePackageNode(packageNode, ID());
}
if (!notify)
return;
if (nodeRemoved) {
notify_entry_removed(ID(), directory->ID(), node->Name(), node->ID());
} else if (packageNode == headPackageNode) {
if (S_ISDIR(packageNode->Mode())) {
notify_stat_changed(ID(), directory->ID(), node->ID(),
kAllStatFields);
} 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 UnpackingLeafNode(fNextNodeID++);
else if (S_ISDIR(mode))
unpackingNode = new 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(name);
if (error != B_OK)
RETURN_ERROR(error);
parent->AddChild(node);
fNodes.Insert(node);
nodeReference.Detach();
_node = unpackingNode;
return B_OK;
}
void
Volume::_RemoveNode(Node* node)
{
Directory* parent = node->GetParentUnchecked();
parent->RemoveChild(node);
fNodes.Remove(node);
node->ReleaseReference();
}
void
Volume::_RemoveNodeAndVNode(Node* node)
{
Directory* parent = NULL;
DirectoryWriteLocker nodeWriteLocker;
if (Directory* directory = dynamic_cast<Directory*>(node))
nodeWriteLocker.SetTo(directory, false, true);
else
parent = node->GetParentUnchecked();
_RemoveNode(node);
const bool isKnownToVFS = node->IsKnownToVFS();
if (parent != NULL) {
parent->WriteUnlock();
} else {
nodeWriteLocker.Unlock();
}
Node* dummyNode;
if (isKnownToVFS && GetVNode(node->ID(), dummyNode) == B_OK) {
RemoveVNode(node->ID());
PutVNode(node->ID());
}
if (parent != NULL)
parent->WriteLock();
}
status_t
Volume::_LoadPackage(PackagesDirectory* packagesDirectory, const char* name,
Package*& _package)
{
struct stat st;
for (;;) {
if (packagesDirectory == NULL)
return B_ENTRY_NOT_FOUND;
if (fstatat(packagesDirectory->DirectoryFD(), name, &st, 0) == 0) {
if (!S_ISREG(st.st_mode))
return B_BAD_VALUE;
break;
}
packagesDirectory = fPackagesDirectories.GetPrevious(packagesDirectory);
}
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;
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);
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);
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);
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);
}
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
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);
oldPackageReferences[oldPackageIndex++].SetTo(package);
_RemovePackageContent(package, NULL, true);
_RemovePackage(package);
INFORM("package \"%s\" deactivated\n", package->FileName().Data());
}
status_t error = B_OK;
for (newPackageIndex = 0; newPackageIndex < newPackageCount;
newPackageIndex++) {
Package* package = newPackageReferences[newPackageIndex];
_AddPackage(package);
error = _AddPackageContent(package, true);
if (error != B_OK) {
_RemovePackage(package);
break;
}
INFORM("package \"%s\" activated\n", package->FileName().Data());
}
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) {
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(nameString);
if (error != B_OK)
RETURN_ERROR(error);
parent->AddChild(directory);
fNodes.Insert(directory);
directoryReference.Detach();
_directory = directory;
return B_OK;
}
status_t
Volume::_CreateShineThroughDirectories(const char* shineThroughSetting)
{
const char* const* directories = NULL;
if (shineThroughSetting == NULL) {
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;
DirectoryWriteLocker rootDirectoryWriteLocker(fRootDirectory);
while (const char* directoryName = *(directories++)) {
Directory* directory;
status_t error = _CreateShineThroughDirectory(fRootDirectory,
directoryName, directory);
if (error != B_OK)
RETURN_ERROR(error);
}
return B_OK;
}
status_t
Volume::_PublishShineThroughDirectories()
{
DirectoryWriteLocker rootDirectoryWriteLocker(fRootDirectory);
Node* nextNode;
for (Node* node = fRootDirectory->FirstChild(); node != NULL;
node = nextNode) {
nextNode = fRootDirectory->NextChild(node);
ShineThroughDirectory* directory
= dynamic_cast<ShineThroughDirectory*>(node);
if (directory == NULL)
continue;
const char* directoryName = directory->Name();
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;
}
VnodePutter vnodePutter(vnode);
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;
}
directory->AcquireReference();
error = PublishVNode(directory);
if (error != B_OK) {
directory->ReleaseReference();
_RemoveNode(directory);
RETURN_ERROR(error);
}
error = vfs_bind_mount_directory(st.st_dev, st.st_ino, fFSVolume->id,
directory->ID());
PutVNode(directory->ID());
if (error != B_OK)
RETURN_ERROR(error);
}
return B_OK;
}
status_t
Volume::_AddPackageLinksDirectory()
{
PackageLinksDirectory* packageLinksDirectory
= fPackageFSRoot->GetPackageLinksDirectory();
DirectoryWriteLocker rootDirectoryWriteLocker(fRootDirectory);
DirectoryWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
fRootDirectory->AddChild(packageLinksDirectory);
_AddPackageLinksNode(packageLinksDirectory);
packageLinksDirectory->SetListener(this);
return B_OK;
}
void
Volume::_RemovePackageLinksDirectory()
{
PackageLinksDirectory* packageLinksDirectory
= fPackageFSRoot->GetPackageLinksDirectory();
VolumeWriteLocker volumeLocker(this);
DirectoryWriteLocker rootDirectoryWriteLocker(fRootDirectory);
DirectoryWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
if (packageLinksDirectory->GetParentUnchecked() == fRootDirectory) {
packageLinksDirectory->SetListener(NULL);
fRootDirectory->RemoveChild(packageLinksDirectory);
}
}
void
Volume::_AddPackageLinksNode(Node* node)
{
node->SetID(fNextNodeID++);
fNodes.Insert(node);
node->AcquireReference();
if (Directory* directory = dynamic_cast<Directory*>(node)) {
DirectoryReadLocker directoryReadLocker(directory);
for (Node* child = directory->FirstChild(); child != NULL;
child = directory->NextChild(child)) {
_AddPackageLinksNode(child);
}
}
}
void
Volume::_RemovePackageLinksNode(Node* node)
{
if (Directory* directory = dynamic_cast<Directory*>(node)) {
DirectoryReadLocker directoryReadLocker(directory);
for (Node* child = directory->FirstChild(); child != NULL;
child = directory->NextChild(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;
}
}