#include "ServerVolume.h"
#include <new>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include "Compatibility.h"
#include "DebugSupport.h"
#include "ExtendedServerInfo.h"
#include "QueryManager.h"
#include "SendReceiveRequest.h"
#include "ServerConnection.h"
#include "ServerConnectionProvider.h"
#include "ServerQueryIterator.h"
#include "ShareVolume.h"
#include "VolumeEvent.h"
#include "VolumeManager.h"
#include "VolumeSupport.h"
ServerVolume::ServerVolume(VolumeManager* volumeManager,
ExtendedServerInfo* serverInfo)
: VirtualVolume(volumeManager),
fServerInfo(serverInfo),
fConnectionProvider(NULL)
{
fServerInfo->AcquireReference();
}
ServerVolume::~ServerVolume()
{
if (fConnectionProvider)
fConnectionProvider->ReleaseReference();
if (fServerInfo)
fServerInfo->ReleaseReference();
}
NetAddress
ServerVolume::GetServerAddress()
{
AutoLocker<Locker> _(fLock);
return fServerInfo->GetAddress();
}
void
ServerVolume::SetServerInfo(ExtendedServerInfo* serverInfo)
{
if (!serverInfo)
return;
fLock.Lock();
fServerInfo->ReleaseReference();
fServerInfo = serverInfo;
fServerInfo->AcquireReference();
BReference<ExtendedServerInfo> newReference(fServerInfo);
VirtualDirIterator iterator;
iterator.SetDirectory(fRootNode, true);
const char* name;
Node* node;
while (iterator.GetCurrentEntry(&name, &node)) {
iterator.NextEntry();
bool remove = (!serverInfo->GetShareInfo(name));
fLock.Unlock();
if (remove) {
PRINT(" removing share: %s\n", name);
if (Volume* volume = GetChildVolume(name)) {
volume->SetUnmounting(true);
volume->PutVolume();
}
}
fLock.Lock();
}
iterator.SetDirectory(NULL);
fLock.Unlock();
int32 count = serverInfo->CountShares();
for (int32 i = 0; i < count; i++) {
ExtendedShareInfo* shareInfo = serverInfo->ShareInfoAt(i);
const char* shareName = shareInfo->GetShareName();
Volume* volume = GetChildVolume(shareName);
if (volume) {
volume->PutVolume();
} else {
PRINT(" adding share: %s\n",
shareInfo->GetShareName());
status_t error = _AddShare(shareInfo);
if (error != B_OK) {
ERROR("ServerVolume::SetServerInfo(): ERROR: Failed to add "
"share `%s': %s\n", shareName, strerror(error));
}
}
}
}
status_t
ServerVolume::Init(const char* name)
{
status_t error = VirtualVolume::Init(name);
if (error != B_OK)
return error;
fConnectionProvider = new ServerConnectionProvider(fVolumeManager,
fServerInfo, GetRootID());
if (!fConnectionProvider) {
Uninit();
return B_NO_MEMORY;
}
error = fConnectionProvider->Init();
if (error != B_OK) {
Uninit();
return error;
}
int32 count = fServerInfo->CountShares();
for (int32 i = 0; i < count; i++) {
ExtendedShareInfo* shareInfo = fServerInfo->ShareInfoAt(i);
error = _AddShare(shareInfo);
if (error != B_OK) {
ERROR("ServerVolume::Init(): ERROR: Failed to add share `%s': "
"%s\n", shareInfo->GetShareName(), strerror(error));
}
}
return B_OK;
}
void
ServerVolume::Uninit()
{
if (fConnectionProvider)
fConnectionProvider->CloseServerConnection();
VirtualVolume::Uninit();
}
void
ServerVolume::PrepareToUnmount()
{
VirtualVolume::PrepareToUnmount();
}
void
ServerVolume::HandleEvent(VolumeEvent* event)
{
if (event->GetType() == CONNECTION_BROKEN_EVENT) {
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 (ShareVolume* shareVolume = dynamic_cast<ShareVolume*>(volume))
shareVolume->ConnectionClosed();
if (volume)
volume->PutVolume();
fLock.Lock();
}
iterator.SetDirectory(NULL);
SetUnmounting(true);
fLock.Unlock();
}
}
status_t
ServerVolume::Unmount()
{
return B_OK;
}
status_t
ServerVolume::OpenQuery(const char* queryString, uint32 flags, port_id port,
int32 token, QueryIterator** _iterator)
{
ServerConnection* serverConnection
= fConnectionProvider->GetExistingServerConnection();
if (!serverConnection)
return ERROR_NOT_CONNECTED;
RequestConnection* connection = serverConnection->GetRequestConnection();
ServerQueryIterator* iterator = new(std::nothrow) ServerQueryIterator(this);
if (!iterator)
return B_NO_MEMORY;
QueryManager* queryManager = fVolumeManager->GetQueryManager();
status_t error = queryManager->AddIterator(iterator);
if (error != B_OK) {
delete iterator;
return error;
}
QueryIteratorPutter iteratorPutter(queryManager, iterator);
OpenQueryRequest request;
request.queryString.SetTo(queryString);
request.flags = flags;
request.port = port;
request.token = token;
OpenQueryReply* reply;
error = SendRequest(connection, &request, &reply);
if (error != B_OK)
RETURN_ERROR(error);
ObjectDeleter<Request> replyDeleter(reply);
if (reply->error != B_OK)
RETURN_ERROR(reply->error);
iterator->SetRemoteCookie(reply->cookie);
*_iterator = iterator;
iteratorPutter.Detach();
return B_OK;
}
void
ServerVolume::FreeQueryIterator(QueryIterator* _iterator)
{
ServerQueryIterator* iterator
= dynamic_cast<ServerQueryIterator*>(_iterator);
int32 cookie = iterator->GetRemoteCookie();
if (cookie >= 0) {
CloseRequest request;
request.volumeID = -1;
request.cookie = cookie;
ServerConnection* serverConnection
= fConnectionProvider->GetExistingServerConnection();
if (serverConnection && serverConnection->IsConnected()) {
CloseReply* reply;
status_t error = SendRequest(
serverConnection->GetRequestConnection(), &request, &reply);
if (error == B_OK)
delete reply;
}
}
delete iterator;
}
status_t
ServerVolume::ReadQuery(QueryIterator* _iterator, struct dirent* buffer,
size_t bufferSize, int32 count, int32* countRead)
{
ServerConnection* serverConnection
= fConnectionProvider->GetExistingServerConnection();
if (!serverConnection)
return ERROR_NOT_CONNECTED;
RequestConnection* connection = serverConnection->GetRequestConnection();
ServerQueryIterator* iterator
= dynamic_cast<ServerQueryIterator*>(_iterator);
*countRead = 0;
for (;;) {
if (!iterator->HasNextShareVolumeID()) {
ReadQueryRequest request;
request.cookie = iterator->GetRemoteCookie();
request.count = 1;
ReadQueryReply* reply;
status_t error = SendRequest(connection, &request, &reply);
if (error != B_OK)
RETURN_ERROR(error);
ObjectDeleter<Request> replyDeleter(reply);
if (reply->error != B_OK)
RETURN_ERROR(reply->error);
if (reply->count == 0) {
*countRead = 0;
return B_OK;
}
error = iterator->SetEntry(reply->clientVolumeIDs.GetElements(),
reply->clientVolumeIDs.CountElements(), reply->dirInfo,
reply->entryInfo);
if (error != B_OK)
return error;
}
int32 volumeID = iterator->NextShareVolumeID();
ShareVolume* shareVolume = _GetShareVolume(volumeID);
if (!shareVolume)
continue;
VolumePutter volumePutter(shareVolume);
return shareVolume->GetQueryEntry(iterator->GetEntryInfo(),
iterator->GetDirectoryInfo(), buffer, bufferSize, countRead);
}
}
status_t
ServerVolume::_AddShare(ExtendedShareInfo* shareInfo)
{
ShareVolume* shareVolume = new(std::nothrow) ShareVolume(fVolumeManager,
fConnectionProvider, fServerInfo, shareInfo);
if (!shareVolume)
return B_NO_MEMORY;
status_t error = shareVolume->Init(shareInfo->GetShareName());
if (error != B_OK) {
delete shareVolume;
return error;
}
error = fVolumeManager->AddVolume(shareVolume);
if (error != B_OK) {
delete shareVolume;
return error;
}
VolumePutter volumePutter(shareVolume);
error = AddChildVolume(shareVolume);
if (error != B_OK) {
shareVolume->SetUnmounting(true);
return error;
}
return B_OK;
}
ShareVolume*
ServerVolume::_GetShareVolume(int32 volumeID)
{
AutoLocker<Locker> locker(fLock);
VirtualDirIterator dirIterator;
dirIterator.SetDirectory(fRootNode, true);
const char* name;
Node* node;
while (dirIterator.GetCurrentEntry(&name, &node)) {
Volume* volume = fVolumeManager->GetVolume(node->GetID());
ShareVolume* shareVolume = dynamic_cast<ShareVolume*>(volume);
if (shareVolume && shareVolume->GetID() == volumeID)
return shareVolume;
volume->PutVolume();
dirIterator.NextEntry();
}
return NULL;
}