#include <new>
#include <stdio.h>
#include <string.h>
#include <AutoLocker.h>
#include "Compatibility.h"
#include "DebugSupport.h"
#include "QueryIterator.h"
#include "QueryManager.h"
#include "VirtualDir.h"
#include "VirtualVolume.h"
#include "VolumeManager.h"
#include "VolumeSupport.h"
VirtualVolume::VirtualVolume(VolumeManager* volumeManager)
: Volume(volumeManager),
fRootNode(NULL)
{
}
VirtualVolume::~VirtualVolume()
{
delete fRootNode;
}
status_t
VirtualVolume::Init(const char* name)
{
status_t error = Volume::Init(name);
if (error != B_OK)
return error;
vnode_id rootNodeID = fVolumeManager->NewNodeID(this);
if (rootNodeID < 0) {
Uninit();
return rootNodeID;
}
fRootNode = new(std::nothrow) VirtualDir(this, rootNodeID);
if (!fRootNode) {
Uninit();
fVolumeManager->RemoveNodeID(rootNodeID);
return B_NO_MEMORY;
}
error = fRootNode->InitCheck();
if (error != B_OK) {
Uninit();
return error;
}
return B_OK;
}
void
VirtualVolume::Uninit()
{
if (fRootNode) {
fVolumeManager->RemoveNodeID(fRootNode->GetID());
delete fRootNode;
fRootNode = NULL;
}
Volume::Uninit();
}
Node*
VirtualVolume::GetRootNode() const
{
return fRootNode;
}
void
VirtualVolume::PrepareToUnmount()
{
Volume::PrepareToUnmount();
fLock.Lock();
VirtualDirIterator iterator;
iterator.SetDirectory(fRootNode, true);
const char* name;
Node* node;
while (iterator.GetCurrentEntry(&name, &node)) {
iterator.NextEntry();
Volume* volume = fVolumeManager->GetVolume(node->GetID());
fLock.Unlock();
if (volume) {
RemoveChildVolume(volume);
volume->SetUnmounting(true);
volume->PutVolume();
}
fLock.Lock();
}
iterator.SetDirectory(NULL);
vnode_id rootNodeID = fRootNode->GetID();
fLock.Unlock();
if (GetVNode(rootNodeID, &node) == B_OK) {
Volume::RemoveVNode(rootNodeID);
PutVNode(rootNodeID);
}
}
status_t
VirtualVolume::AddChildVolume(Volume* volume)
{
if (!volume)
return B_BAD_VALUE;
AutoLocker<Locker> locker(fLock);
if (fUnmounting)
return B_BAD_VALUE;
char name[B_FILE_NAME_LENGTH];
int32 nameLen = strlen(volume->GetName());
if (nameLen == 0 || nameLen >= B_FILE_NAME_LENGTH)
return B_BAD_VALUE;
strcpy(name, volume->GetName());
status_t error = fRootNode->AddEntry(name, volume->GetRootNode());
if (error != B_OK)
return error;
volume->SetParentVolume(this);
AcquireReference();
vnode_id dirID = fRootNode->GetID();
vnode_id nodeID = volume->GetRootID();
locker.Unlock();
NotifyListener(B_ENTRY_CREATED, fVolumeManager->GetID(), dirID, 0, nodeID,
name);
return B_OK;
}
void
VirtualVolume::RemoveChildVolume(Volume* volume)
{
if (!volume)
return;
AutoLocker<Locker> locker(fLock);
Node* node = fRootNode->GetChildNode(volume->GetName());
if (!node)
return;
if (node != volume->GetRootNode())
return;
fRootNode->RemoveEntry(volume->GetName());
volume->SetParentVolume(NULL);
vnode_id dirID = fRootNode->GetID();
vnode_id nodeID = volume->GetRootID();
char name[B_FILE_NAME_LENGTH];
strcpy(name, volume->GetName());
locker.Unlock();
PutVolume();
locker.Unlock();
NotifyListener(B_ENTRY_REMOVED, fVolumeManager->GetID(), dirID, 0, nodeID,
name);
}
Volume*
VirtualVolume::GetChildVolume(const char* name)
{
if (!name)
return NULL;
AutoLocker<Locker> locker(fLock);
Node* node = fRootNode->GetChildNode(name);
if (!node)
return NULL;
Volume* volume = node->GetVolume();
if (volume->GetRootNode() != node)
return NULL;
locker.Unlock();
return fVolumeManager->GetVolume(node->GetID());
}
status_t
VirtualVolume::GetUniqueEntryName(const char* baseName, char* buffer)
{
if (!baseName || !buffer)
return B_BAD_VALUE;
int32 baseLen = strlen(baseName);
if (baseLen == 0 || baseLen >= B_FILE_NAME_LENGTH)
return B_BAD_VALUE;
strcpy(buffer, baseName);
AutoLocker<Locker> _(fLock);
int32 suffixNumber = 2;
while (fRootNode->GetChildNode(baseName)) {
char suffix[13];
sprintf(suffix, " %" B_PRId32, suffixNumber);
suffixNumber++;
int32 suffixLen = strlen(suffix);
if (baseLen + suffixLen >= B_FILE_NAME_LENGTH)
return B_NAME_TOO_LONG;
strcpy(buffer + baseLen, suffix);
}
return B_OK;
}
status_t
VirtualVolume::Unmount()
{
return B_OK;
}
status_t
VirtualVolume::Sync()
{
return B_BAD_VALUE;
}
status_t
VirtualVolume::ReadVNode(vnode_id vnid, char reenter, Node** node)
{
if (vnid != GetRootID())
return B_BAD_VALUE;
AutoLocker<Locker> _(fLock);
fRootNode->SetKnownToVFS(true);
*node = fRootNode;
AcquireReference();
return B_OK;
}
status_t
VirtualVolume::WriteVNode(Node* node, char reenter)
{
if (node != fRootNode)
return B_BAD_VALUE;
AutoLocker<Locker> locker(fLock);
fRootNode->SetKnownToVFS(false);
locker.Unlock();
PutVolume();
return B_OK;
}
status_t
VirtualVolume::RemoveVNode(Node* node, char reenter)
{
if (node != fRootNode)
return B_BAD_VALUE;
AutoLocker<Locker> locker(fLock);
fRootNode->SetKnownToVFS(false);
locker.Unlock();
PutVolume();
return B_OK;
}
status_t
VirtualVolume::FSync(Node* node)
{
return B_OK;
}
status_t
VirtualVolume::ReadStat(Node* node, struct stat* st)
{
if (node != fRootNode)
return B_BAD_VALUE;
AutoLocker<Locker> _(fLock);
st->st_ino = node->GetID();
st->st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH
| S_IXOTH;
st->st_nlink = 1;
st->st_size = 1;
st->st_blksize = 1024;
st->st_crtime = fRootNode->GetCreationTime();
st->st_ctime = st->st_mtime = st->st_atime = st->st_crtime;
st->st_dev = fVolumeManager->GetID();
st->st_uid = fVolumeManager->GetMountUID();
st->st_gid = fVolumeManager->GetMountGID();
return B_OK;
}
status_t
VirtualVolume::WriteStat(Node* node, struct stat *st, uint32 mask)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::Access(Node* node, int mode)
{
return B_OK;
}
status_t
VirtualVolume::Create(Node* dir, const char* name, int openMode, int mode,
vnode_id* vnid, void** cookie)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::Open(Node* node, int openMode, void** cookie)
{
if (node != fRootNode)
return B_BAD_VALUE;
if ((openMode & O_RWMASK) == O_WRONLY)
return B_PERMISSION_DENIED;
if (openMode & O_TRUNC)
return B_PERMISSION_DENIED;
if ((openMode & O_RWMASK) == O_RDWR)
openMode = (openMode & ~O_RWMASK) | O_RDONLY;
*cookie = NULL;
return B_OK;
}
status_t
VirtualVolume::Close(Node* node, void* cookie)
{
return B_OK;
}
status_t
VirtualVolume::FreeCookie(Node* node, void* cookie)
{
return B_OK;
}
status_t
VirtualVolume::Read(Node* node, void* cookie, off_t pos, void* _buffer,
size_t bufferSize, size_t* _bytesRead)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::Write(Node* node, void* cookie, off_t pos, const void* _buffer,
size_t bufferSize, size_t* bytesWritten)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::IOCtl(Node* node, void* cookie, int cmd, void* buffer,
size_t bufferSize)
{
return B_BAD_VALUE;
}
status_t
VirtualVolume::SetFlags(Node* node, void* cookie, int flags)
{
return B_BAD_VALUE;
}
status_t
VirtualVolume::Link(Node* dir, const char* name, Node* node)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::Unlink(Node* dir, const char* name)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::Symlink(Node* dir, const char* name, const char* target)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::ReadLink(Node* node, char* buffer, size_t bufferSize,
size_t* bytesRead)
{
return B_BAD_VALUE;
}
status_t
VirtualVolume::Rename(Node* oldDir, const char* oldName, Node* newDir,
const char* newName)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::MkDir(Node* dir, const char* name, int mode)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::RmDir(Node* dir, const char* name)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::OpenDir(Node* node, void** cookie)
{
if (node != fRootNode)
return B_BAD_VALUE;
VirtualDirIterator* iterator = new(std::nothrow) VirtualDirIterator;
if (!iterator)
return B_NO_MEMORY;
AutoLocker<Locker> locker(fLock);
iterator->SetDirectory(fRootNode);
*cookie = iterator;
return B_OK;
}
status_t
VirtualVolume::CloseDir(Node* node, void* cookie)
{
return B_OK;
}
status_t
VirtualVolume::FreeDirCookie(Node* node, void* cookie)
{
if (node != fRootNode)
return B_BAD_VALUE;
AutoLocker<Locker> locker(fLock);
VirtualDirIterator* iterator = (VirtualDirIterator*)cookie;
delete iterator;
return B_OK;
}
status_t
VirtualVolume::ReadDir(Node* node, void* cookie, struct dirent* buffer,
size_t bufferSize, int32 count, int32* countRead)
{
if (node != fRootNode)
return B_BAD_VALUE;
*countRead = 0;
AutoLocker<Locker> locker(fLock);
VirtualDirIterator* iterator = (VirtualDirIterator*)cookie;
const char* name;
Node* child;
if (!iterator->GetCurrentEntry(&name, &child))
return B_OK;
status_t error = set_dirent_name(buffer, bufferSize, name, strlen(name));
if (error != B_OK)
RETURN_ERROR(error);
buffer->d_pdev = fVolumeManager->GetID();
buffer->d_pino = fRootNode->GetID();
buffer->d_dev = fVolumeManager->GetID();
buffer->d_ino = child->GetID();
*countRead = 1;
if (strcmp(name, "..") == 0) {
if (Volume* parentVolume = GetParentVolume())
buffer->d_ino = parentVolume->GetRootID();
}
iterator->NextEntry();
return B_OK;
}
status_t
VirtualVolume::RewindDir(Node* node, void* cookie)
{
if (node != fRootNode)
return B_BAD_VALUE;
AutoLocker<Locker> locker(fLock);
VirtualDirIterator* iterator = (VirtualDirIterator*)cookie;
iterator->Rewind();
return B_OK;
}
status_t
VirtualVolume::Walk(Node* dir, const char* entryName, char** resolvedPath,
vnode_id* vnid)
{
if (dir != fRootNode)
return B_BAD_VALUE;
AutoLocker<Locker> locker(fLock);
if (strcmp(entryName, ".") == 0) {
*vnid = dir->GetID();
} else if (strcmp(entryName, "..") == 0) {
if (Volume* parentVolume = GetParentVolume())
*vnid = parentVolume->GetRootID();
else
*vnid = dir->GetID();
} else {
Node* node = fRootNode->GetChildNode(entryName);
if (!node)
return B_ENTRY_NOT_FOUND;
*vnid = node->GetID();
}
locker.Unlock();
Node* dummyNode;
status_t error = GetVNode(*vnid, &dummyNode);
if (error != B_OK)
return error;
return B_OK;
}
status_t
VirtualVolume::OpenAttrDir(Node* node, void** cookie)
{
if (node != fRootNode)
return B_BAD_VALUE;
*cookie = NULL;
return B_OK;
}
status_t
VirtualVolume::CloseAttrDir(Node* node, void* cookie)
{
return B_OK;
}
status_t
VirtualVolume::FreeAttrDirCookie(Node* node, void* _cookie)
{
return B_OK;
}
status_t
VirtualVolume::ReadAttrDir(Node* node, void* _cookie, struct dirent* buffer,
size_t bufferSize, int32 count, int32* countRead)
{
*countRead = 0;
return B_OK;
}
status_t
VirtualVolume::RewindAttrDir(Node* node, void* _cookie)
{
return B_OK;
}
status_t
VirtualVolume::ReadAttr(Node* node, const char* name, int type, off_t pos,
void* _buffer, size_t bufferSize, size_t* bytesRead)
{
*bytesRead = 0;
return B_ENTRY_NOT_FOUND;
}
status_t
VirtualVolume::WriteAttr(Node* node, const char* name, int type, off_t pos,
const void* _buffer, size_t bufferSize, size_t* bytesWritten)
{
*bytesWritten = 0;
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::RemoveAttr(Node* node, const char* name)
{
return B_NOT_ALLOWED;
}
status_t
VirtualVolume::RenameAttr(Node* node, const char* oldName, const char* newName)
{
return B_ENTRY_NOT_FOUND;
}
status_t
VirtualVolume::StatAttr(Node* node, const char* name, struct attr_info* attrInfo)
{
return B_ENTRY_NOT_FOUND;
}
status_t
VirtualVolume::OpenQuery(const char* queryString, uint32 flags, port_id port,
int32 token, QueryIterator** _iterator)
{
QueryManager* queryManager = fVolumeManager->GetQueryManager();
HierarchicalQueryIterator* iterator
= new(std::nothrow) HierarchicalQueryIterator(this);
if (!iterator)
return B_NO_MEMORY;
status_t error = queryManager->AddIterator(iterator);
if (error != B_OK) {
delete iterator;
return error;
}
QueryIteratorPutter iteratorPutter(queryManager, iterator);
fLock.Lock();
VirtualDirIterator dirIterator;
dirIterator.SetDirectory(fRootNode, true);
const char* name;
Node* node;
while (dirIterator.GetCurrentEntry(&name, &node)) {
dirIterator.NextEntry();
Volume* volume = fVolumeManager->GetVolume(node->GetID());
fLock.Unlock();
QueryIterator* subIterator;
if (volume->OpenQuery(queryString, flags, port, token,
&subIterator) == B_OK) {
if (queryManager->AddSubIterator(iterator, subIterator) != B_OK)
queryManager->PutIterator(subIterator);
}
volume->PutVolume();
fLock.Lock();
}
dirIterator.SetDirectory(NULL);
fLock.Unlock();
*_iterator = iterator;
iteratorPutter.Detach();
return B_OK;
}
void
VirtualVolume::FreeQueryIterator(QueryIterator* iterator)
{
delete iterator;
}
status_t
VirtualVolume::ReadQuery(QueryIterator* _iterator, struct dirent* buffer,
size_t bufferSize, int32 count, int32* countRead)
{
HierarchicalQueryIterator* iterator
= dynamic_cast<HierarchicalQueryIterator*>(_iterator);
QueryManager* queryManager = fVolumeManager->GetQueryManager();
while (QueryIterator* subIterator
= queryManager->GetCurrentSubIterator(iterator)) {
QueryIteratorPutter _(queryManager, subIterator);
status_t error = subIterator->GetVolume()->ReadQuery(subIterator,
buffer, bufferSize, count, countRead);
if (error != B_OK)
return error;
if (*countRead > 0)
return B_OK;
queryManager->NextSubIterator(iterator, subIterator);
}
*countRead = 0;
return B_OK;
}