b8bcb08800
CID1108329
827 lines
19 KiB
C++
827 lines
19 KiB
C++
/*
|
|
* Copyright 2013 Haiku, Inc. All rights reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Ingo Weinhold, ingo_weinhold@gmx.de
|
|
*/
|
|
|
|
|
|
#include "VirtualDirectoryManager.h"
|
|
|
|
#include <errno.h>
|
|
|
|
#include <new>
|
|
|
|
#include <Directory.h>
|
|
#include <File.h>
|
|
#include <StringList.h>
|
|
|
|
#include <AutoDeleter.h>
|
|
#include <AutoLocker.h>
|
|
#include <DriverSettings.h>
|
|
#include <NotOwningEntryRef.h>
|
|
#include <Uuid.h>
|
|
|
|
#include "MimeTypes.h"
|
|
#include "Model.h"
|
|
|
|
|
|
namespace BPrivate {
|
|
|
|
static const size_t kMaxVirtualDirectoryFileSize = 10 * 1024;
|
|
static const char* const kTemporaryDefinitionFileBaseDirectoryPath
|
|
= "/tmp/tracker/virtual-directories";
|
|
|
|
|
|
// #pragma mark - VirtualDirectoryManager::Info
|
|
|
|
|
|
class VirtualDirectoryManager::Info {
|
|
private:
|
|
typedef BObjectList<Info> InfoList;
|
|
|
|
public:
|
|
Info(RootInfo* root, Info* parent, const BString& path,
|
|
const node_ref& definitionFileNodeRef,
|
|
const entry_ref& definitionFileEntryRef)
|
|
:
|
|
fRoot(root),
|
|
fParent(parent),
|
|
fPath(path),
|
|
fDefinitionFileNodeRef(definitionFileNodeRef),
|
|
fDefinitionFileEntryRef(definitionFileEntryRef),
|
|
fId(),
|
|
fChildDefinitionsDirectoryRef(-1, -1),
|
|
fChildren(10, true)
|
|
{
|
|
}
|
|
|
|
~Info()
|
|
{
|
|
}
|
|
|
|
RootInfo* Root() const
|
|
{
|
|
return fRoot;
|
|
}
|
|
|
|
Info* Parent() const
|
|
{
|
|
return fParent;
|
|
}
|
|
|
|
const char* Name() const
|
|
{
|
|
return fDefinitionFileEntryRef.name;
|
|
}
|
|
|
|
const BString& Path() const
|
|
{
|
|
return fPath;
|
|
}
|
|
|
|
const node_ref& DefinitionFileNodeRef() const
|
|
{
|
|
return fDefinitionFileNodeRef;
|
|
}
|
|
|
|
const entry_ref& DefinitionFileEntryRef() const
|
|
{
|
|
return fDefinitionFileEntryRef;
|
|
}
|
|
|
|
const InfoList& Children() const
|
|
{
|
|
return fChildren;
|
|
}
|
|
|
|
const BString& Id() const
|
|
{
|
|
return fId;
|
|
}
|
|
|
|
void SetId(const BString& id)
|
|
{
|
|
fId = id;
|
|
}
|
|
|
|
const node_ref& ChildDefinitionsDirectoryRef() const
|
|
{
|
|
return fChildDefinitionsDirectoryRef;
|
|
}
|
|
|
|
void SetChildDefinitionsDirectoryRef(const node_ref& ref)
|
|
{
|
|
fChildDefinitionsDirectoryRef = ref;
|
|
}
|
|
|
|
Info* GetChild(const char* name) const
|
|
{
|
|
for (int32 i = 0; Info* child = fChildren.ItemAt(i); i++) {
|
|
if (strcmp(name, child->Name()) == 0)
|
|
return child;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
Info* CreateChild(const node_ref& definitionFileNodeRef,
|
|
const entry_ref& definitionFileEntryRef)
|
|
{
|
|
BString path;
|
|
if (fPath.IsEmpty()) {
|
|
path = definitionFileEntryRef.name;
|
|
} else {
|
|
path.SetToFormat("%s/%s", fPath.String(),
|
|
definitionFileEntryRef.name);
|
|
}
|
|
if (path.IsEmpty())
|
|
return NULL;
|
|
|
|
Info* info = new(std::nothrow) Info(fRoot, this, path,
|
|
definitionFileNodeRef, definitionFileEntryRef);
|
|
if (info == NULL || !fChildren.AddItem(info)) {
|
|
delete info;
|
|
return NULL;
|
|
}
|
|
return info;
|
|
}
|
|
|
|
bool DeleteChild(Info* info)
|
|
{
|
|
return fChildren.RemoveItem(info, true);
|
|
}
|
|
|
|
void DeleteChildAt(int32 index)
|
|
{
|
|
delete fChildren.RemoveItemAt(index);
|
|
}
|
|
|
|
private:
|
|
RootInfo* fRoot;
|
|
Info* fParent;
|
|
BString fPath;
|
|
node_ref fDefinitionFileNodeRef;
|
|
entry_ref fDefinitionFileEntryRef;
|
|
BString fId;
|
|
node_ref fChildDefinitionsDirectoryRef;
|
|
InfoList fChildren;
|
|
};
|
|
|
|
|
|
// #pragma mark - VirtualDirectoryManager::RootInfo
|
|
|
|
|
|
class VirtualDirectoryManager::RootInfo {
|
|
public:
|
|
RootInfo(const node_ref& definitionFileNodeRef,
|
|
const entry_ref& definitionFileEntryRef)
|
|
:
|
|
fDirectoryPaths(),
|
|
fInfo(new(std::nothrow) VirtualDirectoryManager::Info(this, NULL,
|
|
BString(), definitionFileNodeRef, definitionFileEntryRef)),
|
|
fFileTime(-1),
|
|
fLastChangeTime(-1)
|
|
{
|
|
}
|
|
|
|
~RootInfo()
|
|
{
|
|
delete fInfo;
|
|
}
|
|
|
|
status_t InitCheck() const
|
|
{
|
|
return fInfo != NULL ? B_OK : B_NO_MEMORY;
|
|
}
|
|
|
|
bigtime_t FileTime() const
|
|
{
|
|
return fFileTime;
|
|
}
|
|
|
|
bigtime_t LastChangeTime() const
|
|
{
|
|
return fLastChangeTime;
|
|
}
|
|
|
|
const BStringList& DirectoryPaths() const
|
|
{
|
|
return fDirectoryPaths;
|
|
}
|
|
|
|
status_t ReadDefinition(bool* _changed = NULL)
|
|
{
|
|
// open the definition file
|
|
BFile file;
|
|
status_t error = file.SetTo(&fInfo->DefinitionFileEntryRef(),
|
|
B_READ_ONLY);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
struct stat st;
|
|
error = file.GetStat(&st);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
bigtime_t fileTime = st.st_mtim.tv_sec;
|
|
fileTime *= 1000000;
|
|
fileTime += st.st_mtim.tv_nsec / 1000;
|
|
if (fileTime == fFileTime) {
|
|
if (_changed != NULL)
|
|
*_changed = false;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
if (node_ref(st.st_dev, st.st_ino) != fInfo->DefinitionFileNodeRef())
|
|
return B_ENTRY_NOT_FOUND;
|
|
|
|
// read the contents
|
|
off_t fileSize = st.st_size;
|
|
if (fileSize > (off_t)kMaxVirtualDirectoryFileSize)
|
|
return B_BAD_VALUE;
|
|
|
|
char* buffer = new(std::nothrow) char[fileSize + 1];
|
|
if (buffer == NULL)
|
|
return B_NO_MEMORY;
|
|
ArrayDeleter<char> bufferDeleter(buffer);
|
|
|
|
ssize_t bytesRead = file.ReadAt(0, buffer, fileSize);
|
|
if (bytesRead < 0)
|
|
return bytesRead;
|
|
|
|
buffer[bytesRead] = '\0';
|
|
|
|
// parse it
|
|
BStringList oldDirectoryPaths(fDirectoryPaths);
|
|
fDirectoryPaths.MakeEmpty();
|
|
|
|
BDriverSettings driverSettings;
|
|
error = driverSettings.SetToString(buffer);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
BDriverParameterIterator it
|
|
= driverSettings.ParameterIterator("directory");
|
|
while (it.HasNext()) {
|
|
BDriverParameter parameter = it.Next();
|
|
for (int32 i = 0; i < parameter.CountValues(); i++)
|
|
fDirectoryPaths.Add(parameter.ValueAt(i));
|
|
}
|
|
|
|
// update file time and check whether something has changed
|
|
fFileTime = fileTime;
|
|
|
|
bool changed = fDirectoryPaths != oldDirectoryPaths;
|
|
if (changed || fLastChangeTime < 0)
|
|
fLastChangeTime = fFileTime;
|
|
|
|
if (_changed != NULL)
|
|
*_changed = changed;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
VirtualDirectoryManager::Info* Info() const
|
|
{
|
|
return fInfo;
|
|
}
|
|
|
|
private:
|
|
typedef std::map<BString, VirtualDirectoryManager::Info*> InfoMap;
|
|
|
|
private:
|
|
BStringList fDirectoryPaths;
|
|
VirtualDirectoryManager::Info* fInfo;
|
|
bigtime_t fFileTime;
|
|
// actual file modified time
|
|
bigtime_t fLastChangeTime;
|
|
// last time something actually changed
|
|
};
|
|
|
|
|
|
// #pragma mark - VirtualDirectoryManager
|
|
|
|
|
|
VirtualDirectoryManager::VirtualDirectoryManager()
|
|
:
|
|
fLock("virtual directory manager")
|
|
{
|
|
}
|
|
|
|
|
|
/*static*/ VirtualDirectoryManager*
|
|
VirtualDirectoryManager::Instance()
|
|
{
|
|
static VirtualDirectoryManager* manager
|
|
= new(std::nothrow) VirtualDirectoryManager;
|
|
return manager;
|
|
}
|
|
|
|
|
|
status_t
|
|
VirtualDirectoryManager::ResolveDirectoryPaths(
|
|
const node_ref& definitionFileNodeRef,
|
|
const entry_ref& definitionFileEntryRef, BStringList& _directoryPaths,
|
|
node_ref* _definitionFileNodeRef, entry_ref* _definitionFileEntryRef)
|
|
{
|
|
Info* info = _InfoForNodeRef(definitionFileNodeRef);
|
|
if (info == NULL) {
|
|
status_t error = _ResolveUnknownDefinitionFile(definitionFileNodeRef,
|
|
definitionFileEntryRef, info);
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
|
|
const BString& subDirectory = info->Path();
|
|
const BStringList& rootDirectoryPaths = info->Root()->DirectoryPaths();
|
|
if (subDirectory.IsEmpty()) {
|
|
_directoryPaths = rootDirectoryPaths;
|
|
} else {
|
|
_directoryPaths.MakeEmpty();
|
|
int32 count = rootDirectoryPaths.CountStrings();
|
|
for (int32 i = 0; i < count; i++) {
|
|
BString path = rootDirectoryPaths.StringAt(i);
|
|
_directoryPaths.Add(path << '/' << subDirectory);
|
|
}
|
|
}
|
|
|
|
if (_definitionFileEntryRef != NULL) {
|
|
*_definitionFileEntryRef = info->DefinitionFileEntryRef();
|
|
if (_definitionFileEntryRef->name == NULL)
|
|
return B_NO_MEMORY;
|
|
}
|
|
|
|
if (_definitionFileNodeRef != NULL)
|
|
*_definitionFileNodeRef = info->DefinitionFileNodeRef();
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
bool
|
|
VirtualDirectoryManager::GetDefinitionFileChangeTime(
|
|
const node_ref& definitionFileRef, bigtime_t& _time) const
|
|
{
|
|
Info* info = _InfoForNodeRef(definitionFileRef);
|
|
if (info == NULL)
|
|
return false;
|
|
|
|
_time = info->Root()->LastChangeTime();
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
VirtualDirectoryManager::GetRootDefinitionFile(
|
|
const node_ref& definitionFileRef, node_ref& _rootDefinitionFileRef)
|
|
{
|
|
Info* info = _InfoForNodeRef(definitionFileRef);
|
|
if (info == NULL)
|
|
return false;
|
|
|
|
_rootDefinitionFileRef = info->Root()->Info()->DefinitionFileNodeRef();
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
VirtualDirectoryManager::GetSubDirectoryDefinitionFile(
|
|
const node_ref& baseDefinitionRef, const char* subDirName,
|
|
entry_ref& _entryRef, node_ref& _nodeRef)
|
|
{
|
|
Info* parentInfo = _InfoForNodeRef(baseDefinitionRef);
|
|
if (parentInfo == NULL)
|
|
return false;
|
|
|
|
Info* info = parentInfo->GetChild(subDirName);
|
|
if (info == NULL)
|
|
return false;
|
|
|
|
_entryRef = info->DefinitionFileEntryRef();
|
|
_nodeRef = info->DefinitionFileNodeRef();
|
|
return _entryRef.name != NULL;
|
|
}
|
|
|
|
|
|
bool
|
|
VirtualDirectoryManager::GetParentDirectoryDefinitionFile(
|
|
const node_ref& subDirDefinitionRef, entry_ref& _entryRef,
|
|
node_ref& _nodeRef)
|
|
{
|
|
Info* info = _InfoForNodeRef(subDirDefinitionRef);
|
|
if (info == NULL)
|
|
return false;
|
|
|
|
Info* parentInfo = info->Parent();
|
|
if (parentInfo == NULL)
|
|
return false;
|
|
|
|
_entryRef = parentInfo->DefinitionFileEntryRef();
|
|
_nodeRef = parentInfo->DefinitionFileNodeRef();
|
|
return _entryRef.name != NULL;
|
|
}
|
|
|
|
|
|
status_t
|
|
VirtualDirectoryManager::TranslateDirectoryEntry(
|
|
const node_ref& definitionFileRef, dirent* buffer)
|
|
{
|
|
NotOwningEntryRef entryRef(buffer->d_pdev, buffer->d_pino, buffer->d_name);
|
|
node_ref nodeRef(buffer->d_dev, buffer->d_ino);
|
|
|
|
status_t result = TranslateDirectoryEntry(definitionFileRef, entryRef,
|
|
nodeRef);
|
|
if (result != B_OK)
|
|
return result;
|
|
|
|
buffer->d_pdev = entryRef.device;
|
|
buffer->d_pino = entryRef.directory;
|
|
buffer->d_dev = nodeRef.device;
|
|
buffer->d_ino = nodeRef.node;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
VirtualDirectoryManager::TranslateDirectoryEntry(
|
|
const node_ref& definitionFileRef, entry_ref& _entryRef, node_ref& _nodeRef)
|
|
{
|
|
Info* parentInfo = _InfoForNodeRef(definitionFileRef);
|
|
if (parentInfo == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
// get the info for the entry
|
|
Info* info = parentInfo->GetChild(_entryRef.name);
|
|
if (info == NULL) {
|
|
// If not done yet, create a directory for the parent, where we can
|
|
// place the new definition file.
|
|
if (parentInfo->Id().IsEmpty()) {
|
|
BString id = BUuid().SetToRandom().ToString();
|
|
if (id.IsEmpty())
|
|
return B_NO_MEMORY;
|
|
|
|
BPath path(kTemporaryDefinitionFileBaseDirectoryPath, id);
|
|
status_t error = path.InitCheck();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
error = create_directory(path.Path(),
|
|
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
struct stat st;
|
|
if (lstat(path.Path(), &st) != 0)
|
|
return errno;
|
|
|
|
parentInfo->SetId(id);
|
|
parentInfo->SetChildDefinitionsDirectoryRef(
|
|
node_ref(st.st_dev, st.st_ino));
|
|
}
|
|
|
|
// create the definition file
|
|
const node_ref& directoryRef
|
|
= parentInfo->ChildDefinitionsDirectoryRef();
|
|
NotOwningEntryRef entryRef(directoryRef, _entryRef.name);
|
|
|
|
BFile definitionFile;
|
|
status_t error = definitionFile.SetTo(&entryRef,
|
|
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
node_ref nodeRef;
|
|
error = definitionFile.GetNodeRef(&nodeRef);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
BNodeInfo nodeInfo(&definitionFile);
|
|
error = nodeInfo.SetType(kVirtualDirectoryMimeType);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create the info
|
|
info = parentInfo->CreateChild(nodeRef, entryRef);
|
|
if (info == NULL || !_AddInfo(info))
|
|
return B_NO_MEMORY;
|
|
|
|
// Write some info into the definition file that helps us to find the
|
|
// root definition file. This is only necessary when definition file
|
|
// entry refs are transferred between applications. Then the receiving
|
|
// application may need to find the root definition file and resolve
|
|
// the subdirectories.
|
|
const entry_ref& rootEntryRef
|
|
= parentInfo->Root()->Info()->DefinitionFileEntryRef();
|
|
BString definitionFileContent;
|
|
definitionFileContent.SetToFormat(
|
|
"root {\n"
|
|
" device %" B_PRIdDEV "\n"
|
|
" directory %" B_PRIdINO "\n"
|
|
" name \"%s\"\n"
|
|
"}\n"
|
|
"subdir \"%s\"\n",
|
|
rootEntryRef.device, rootEntryRef.directory, rootEntryRef.name,
|
|
info->Path().String());
|
|
// failure is not nice, but not mission critical for this application
|
|
if (!definitionFileContent.IsEmpty()) {
|
|
definitionFile.WriteAt(0,
|
|
definitionFileContent.String(), definitionFileContent.Length());
|
|
}
|
|
}
|
|
|
|
const entry_ref& entryRef = info->DefinitionFileEntryRef();
|
|
_nodeRef = info->DefinitionFileNodeRef();
|
|
_entryRef.device = entryRef.device;
|
|
_entryRef.directory = entryRef.directory;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
bool
|
|
VirtualDirectoryManager::DefinitionFileChanged(
|
|
const node_ref& definitionFileRef)
|
|
{
|
|
Info* info = _InfoForNodeRef(definitionFileRef);
|
|
if (info == NULL)
|
|
return false;
|
|
|
|
_UpdateTree(info->Root());
|
|
|
|
return _InfoForNodeRef(definitionFileRef) != NULL;
|
|
}
|
|
|
|
|
|
status_t
|
|
VirtualDirectoryManager::DirectoryRemoved(const node_ref& definitionFileRef)
|
|
{
|
|
Info* info = _InfoForNodeRef(definitionFileRef);
|
|
if (info == NULL)
|
|
return B_ENTRY_NOT_FOUND;
|
|
|
|
_RemoveDirectory(info);
|
|
|
|
// delete the info
|
|
if (info->Parent() == NULL)
|
|
delete info->Root();
|
|
else
|
|
info->Parent()->DeleteChild(info);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
/*static*/ bool
|
|
VirtualDirectoryManager::GetEntry(const BStringList& directoryPaths,
|
|
const char* name, entry_ref* _ref, struct stat* _st)
|
|
{
|
|
int32 count = directoryPaths.CountStrings();
|
|
for (int32 i = 0; i < count; i++) {
|
|
BPath path;
|
|
if (path.SetTo(directoryPaths.StringAt(i), name) != B_OK)
|
|
continue;
|
|
|
|
struct stat st;
|
|
if (lstat(path.Path(), &st) == 0) {
|
|
if (_ref != NULL) {
|
|
if (get_ref_for_path(path.Path(), _ref) != B_OK)
|
|
return false;
|
|
}
|
|
if (_st != NULL)
|
|
*_st = st;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
VirtualDirectoryManager::Info*
|
|
VirtualDirectoryManager::_InfoForNodeRef(const node_ref& nodeRef) const
|
|
{
|
|
NodeRefInfoMap::const_iterator it = fInfos.find(nodeRef);
|
|
return it != fInfos.end() ? it->second : NULL;
|
|
}
|
|
|
|
|
|
bool
|
|
VirtualDirectoryManager::_AddInfo(Info* info)
|
|
{
|
|
try {
|
|
fInfos[info->DefinitionFileNodeRef()] = info;
|
|
return true;
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
VirtualDirectoryManager::_RemoveInfo(Info* info)
|
|
{
|
|
NodeRefInfoMap::iterator it = fInfos.find(info->DefinitionFileNodeRef());
|
|
if (it != fInfos.end())
|
|
fInfos.erase(it);
|
|
}
|
|
|
|
|
|
void
|
|
VirtualDirectoryManager::_UpdateTree(RootInfo* root)
|
|
{
|
|
bool changed = false;
|
|
status_t result = root->ReadDefinition(&changed);
|
|
if (result != B_OK) {
|
|
DirectoryRemoved(root->Info()->DefinitionFileNodeRef());
|
|
return;
|
|
}
|
|
|
|
if (!changed)
|
|
return;
|
|
|
|
_UpdateTree(root->Info());
|
|
}
|
|
|
|
|
|
void
|
|
VirtualDirectoryManager::_UpdateTree(Info* info)
|
|
{
|
|
const BStringList& directoryPaths = info->Root()->DirectoryPaths();
|
|
|
|
int32 childCount = info->Children().CountItems();
|
|
for (int32 i = childCount -1; i >= 0; i--) {
|
|
Info* childInfo = info->Children().ItemAt(i);
|
|
struct stat st;
|
|
if (GetEntry(directoryPaths, childInfo->Path(), NULL, &st)
|
|
&& S_ISDIR(st.st_mode)) {
|
|
_UpdateTree(childInfo);
|
|
} else {
|
|
_RemoveDirectory(childInfo);
|
|
info->DeleteChildAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
VirtualDirectoryManager::_RemoveDirectory(Info* info)
|
|
{
|
|
// recursively remove the subdirectories
|
|
for (int32 i = 0; Info* child = info->Children().ItemAt(i); i++)
|
|
_RemoveDirectory(child);
|
|
|
|
// remove the directory for the child definition file
|
|
if (!info->Id().IsEmpty()) {
|
|
BPath path(kTemporaryDefinitionFileBaseDirectoryPath, info->Id());
|
|
if (path.InitCheck() == B_OK)
|
|
rmdir(path.Path());
|
|
}
|
|
|
|
// unless this is the root directory, remove the definition file
|
|
if (info != info->Root()->Info())
|
|
BEntry(&info->DefinitionFileEntryRef()).Remove();
|
|
|
|
_RemoveInfo(info);
|
|
}
|
|
|
|
|
|
status_t
|
|
VirtualDirectoryManager::_ResolveUnknownDefinitionFile(
|
|
const node_ref& definitionFileNodeRef,
|
|
const entry_ref& definitionFileEntryRef, Info*& _info)
|
|
{
|
|
// This is either a root definition file or a subdir definition file
|
|
// created by another application. We'll just try to read the info from the
|
|
// file that a subdir definition file would contain. If that fails, we
|
|
// assume a root definition file.
|
|
entry_ref entryRef;
|
|
BString subDirPath;
|
|
if (_ReadSubDirectoryDefinitionFileInfo(definitionFileEntryRef, entryRef,
|
|
subDirPath) != B_OK) {
|
|
return _CreateRootInfo(definitionFileNodeRef, definitionFileEntryRef,
|
|
_info);
|
|
}
|
|
|
|
if (subDirPath.IsEmpty())
|
|
return B_BAD_VALUE;
|
|
|
|
// get the root definition file node ref
|
|
node_ref nodeRef;
|
|
status_t error = BEntry(&entryRef).GetNodeRef(&nodeRef);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// resolve/create the root info
|
|
Info* info = _InfoForNodeRef(nodeRef);
|
|
if (info == NULL) {
|
|
error = _CreateRootInfo(nodeRef, entryRef, info);
|
|
if (error != B_OK)
|
|
return error;
|
|
} else if (info->Root()->Info() != info)
|
|
return B_BAD_VALUE;
|
|
|
|
const BStringList& rootDirectoryPaths = info->Root()->DirectoryPaths();
|
|
|
|
// now we can traverse the subdir path and resolve all infos along the way
|
|
int32 nextComponentOffset = 0;
|
|
while (nextComponentOffset < subDirPath.Length()) {
|
|
int32 componentEnd = subDirPath.FindFirst('/', nextComponentOffset);
|
|
if (componentEnd >= 0) {
|
|
// skip duplicate '/'s
|
|
if (componentEnd == nextComponentOffset + 1) {
|
|
nextComponentOffset = componentEnd;
|
|
continue;
|
|
}
|
|
nextComponentOffset = componentEnd + 1;
|
|
} else {
|
|
componentEnd = subDirPath.Length();
|
|
nextComponentOffset = componentEnd;
|
|
}
|
|
|
|
BString entryPath(subDirPath, componentEnd);
|
|
if (entryPath.IsEmpty())
|
|
return B_NO_MEMORY;
|
|
|
|
struct stat st;
|
|
if (!GetEntry(rootDirectoryPaths, entryPath, &entryRef, &st))
|
|
return B_ENTRY_NOT_FOUND;
|
|
|
|
if (!S_ISDIR(st.st_mode))
|
|
return B_BAD_VALUE;
|
|
|
|
error = TranslateDirectoryEntry(info->DefinitionFileNodeRef(), entryRef,
|
|
nodeRef);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
info = _InfoForNodeRef(nodeRef);
|
|
}
|
|
|
|
_info = info;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
VirtualDirectoryManager::_CreateRootInfo(const node_ref& definitionFileNodeRef,
|
|
const entry_ref& definitionFileEntryRef, Info*& _info)
|
|
{
|
|
RootInfo* root = new(std::nothrow) RootInfo(definitionFileNodeRef,
|
|
definitionFileEntryRef);
|
|
if (root == NULL || root->InitCheck() != B_OK) {
|
|
delete root;
|
|
return B_NO_MEMORY;
|
|
}
|
|
ObjectDeleter<RootInfo> rootDeleter(root);
|
|
|
|
status_t error = root->ReadDefinition();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
if (!_AddInfo(root->Info()))
|
|
return B_NO_MEMORY;
|
|
|
|
rootDeleter.Detach();
|
|
_info = root->Info();
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
VirtualDirectoryManager::_ReadSubDirectoryDefinitionFileInfo(
|
|
const entry_ref& entryRef, entry_ref& _rootDefinitionFileEntryRef,
|
|
BString& _subDirPath)
|
|
{
|
|
BDriverSettings driverSettings;
|
|
status_t error = driverSettings.Load(entryRef);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
const char* subDirPath = driverSettings.GetParameterValue("subdir");
|
|
if (subDirPath == NULL || subDirPath[0] == '\0')
|
|
return B_BAD_DATA;
|
|
|
|
BDriverParameter rootParameter;
|
|
if (!driverSettings.FindParameter("root", &rootParameter))
|
|
return B_BAD_DATA;
|
|
|
|
const char* name = rootParameter.GetParameterValue("name");
|
|
dev_t device = rootParameter.GetInt32ParameterValue("device", -1, -1);
|
|
ino_t directory = rootParameter.GetInt64ParameterValue("directory");
|
|
if (name == NULL || name[0] == '\0' || device < 0)
|
|
return B_BAD_DATA;
|
|
|
|
_rootDefinitionFileEntryRef = entry_ref(device, directory, name);
|
|
_subDirPath = subDirPath;
|
|
|
|
return !_subDirPath.IsEmpty() && _rootDefinitionFileEntryRef.name != NULL
|
|
? B_OK : B_NO_MEMORY;
|
|
}
|
|
|
|
} // namespace BPrivate
|