#include "RootVolume.h"
#include <new>
#include <AutoLocker.h>
#include "Compatibility.h"
#include "DebugSupport.h"
#include "ExtendedServerInfo.h"
#include "NetAddress.h"
#include "netfs_ioctl.h"
#include "ServerVolume.h"
#include "TaskManager.h"
#include "VolumeManager.h"
#include "VolumeSupport.h"
static const int32 kOptimalIOSize = 64 * 1024;
static const char* kFSName = "netfs";
RootVolume::RootVolume(VolumeManager* volumeManager)
: VirtualVolume(volumeManager)
{
}
RootVolume::~RootVolume()
{
}
status_t
RootVolume::Init()
{
status_t error = VirtualVolume::Init("Network");
if (error != B_OK)
return error;
fServerManager = new(std::nothrow) ServerManager(this);
if (!fServerManager)
RETURN_ERROR(B_NO_MEMORY);
error = fServerManager->Init();
if (error != B_OK)
RETURN_ERROR(error);
return B_OK;
}
void
RootVolume::Uninit()
{
delete fServerManager;
fServerManager = NULL;
VirtualVolume::Uninit();
}
void
RootVolume::PrepareToUnmount()
{
VirtualVolume::PrepareToUnmount();
}
status_t
RootVolume::Mount(const char* device, uint32 flags, const char* parameters,
int32 len)
{
status_t error = NewVNode(fRootNode->GetID(), fRootNode);
if (error != B_OK)
RETURN_ERROR(error);
fServerManager->Run();
return B_OK;
}
status_t
RootVolume::Unmount()
{
Uninit();
return B_OK;
}
status_t
RootVolume::Sync()
{
return B_BAD_VALUE;
}
status_t
RootVolume::ReadFSStat(fs_info* info)
{
info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME | B_FS_HAS_ATTR
| B_FS_IS_SHARED | B_FS_HAS_QUERY;
if (fVolumeManager->GetMountFlags() & B_MOUNT_READ_ONLY)
info->flags |= B_FS_IS_READONLY;
info->block_size = 1024;
info->io_size = kOptimalIOSize;
info->total_blocks = 0;
info->free_blocks = LONGLONG_MAX / info->block_size;
strcpy(info->device_name, "");
strcpy(info->volume_name, GetName());
strcpy(info->fsh_name, kFSName);
return B_OK;
}
status_t
RootVolume::WriteFSStat(struct fs_info* info, int32 mask)
{
return B_BAD_VALUE;
}
status_t
RootVolume::IOCtl(Node* node, void* cookie, int cmd, void* buffer,
size_t bufferSize)
{
if (node != fRootNode)
return B_BAD_VALUE;
switch (cmd) {
case NET_FS_IOCTL_ADD_SERVER:
{
if (!buffer)
return B_BAD_VALUE;
netfs_ioctl_add_server* params = (netfs_ioctl_add_server*)buffer;
int32 serverNameLen = strnlen(params->serverName,
sizeof(params->serverName));
if (serverNameLen == 0
|| serverNameLen == sizeof(params->serverName)) {
return B_BAD_VALUE;
}
PRINT("RootVolume::IOCtl(): NET_FS_IOCTL_ADD_SERVER: "
"`%s'\n", params->serverName);
NetAddress netAddress;
NetAddressResolver resolver;
status_t error = resolver.GetHostAddress(params->serverName, &netAddress);
if (error != B_OK)
return error;
return fServerManager->AddServer(netAddress);
}
case NET_FS_IOCTL_REMOVE_SERVER:
{
if (!buffer)
return B_BAD_VALUE;
netfs_ioctl_remove_server* params
= (netfs_ioctl_remove_server*)buffer;
int32 serverNameLen = strnlen(params->serverName,
sizeof(params->serverName));
if (serverNameLen == 0
|| serverNameLen == sizeof(params->serverName)) {
return B_BAD_VALUE;
}
PRINT("RootVolume::IOCtl(): NET_FS_IOCTL_REMOVE_SERVER:"
" `%s'\n", params->serverName);
ServerVolume* serverVolume = _GetServerVolume(params->serverName);
if (!serverVolume)
return B_ENTRY_NOT_FOUND;
VolumePutter volumePutter(serverVolume);
fServerManager->RemoveServer(serverVolume->GetServerAddress());
return B_OK;
}
default:
PRINT("RootVolume::IOCtl(): unknown ioctl: %d\n", cmd);
return B_BAD_VALUE;
break;
}
return B_BAD_VALUE;
}
void
RootVolume::ServerAdded(ExtendedServerInfo* serverInfo)
{
PRINT("RootVolume::ServerAdded(%s)\n", serverInfo->GetServerName());
ServerVolume* serverVolume = _GetServerVolume(serverInfo->GetAddress());
if (serverVolume) {
WARN("RootVolume::ServerAdded(): WARNING: ServerVolume does "
"already exist.\n");
serverVolume->PutVolume();
return;
}
AutoLocker<Locker> locker(fLock);
char serverName[B_FILE_NAME_LENGTH];
status_t error = GetUniqueEntryName(serverInfo->GetServerName(),
serverName);
if (error != B_OK)
return;
serverVolume = new(std::nothrow) ServerVolume(fVolumeManager, serverInfo);
if (!serverVolume)
return;
error = serverVolume->Init(serverName);
if (error != B_OK) {
delete serverVolume;
return;
}
error = fVolumeManager->AddVolume(serverVolume);
if (error != B_OK) {
delete serverVolume;
return;
}
VolumePutter volumePutter(serverVolume);
locker.Unlock();
error = AddChildVolume(serverVolume);
if (error != B_OK) {
serverVolume->SetUnmounting(true);
return;
}
}
void
RootVolume::ServerUpdated(ExtendedServerInfo* oldInfo,
ExtendedServerInfo* newInfo)
{
PRINT("RootVolume::ServerUpdated(%s)\n", newInfo->GetServerName());
ServerVolume* serverVolume = _GetServerVolume(newInfo->GetAddress());
if (!serverVolume)
return;
VolumePutter _(serverVolume);
serverVolume->SetServerInfo(newInfo);
}
void
RootVolume::ServerRemoved(ExtendedServerInfo* serverInfo)
{
PRINT("RootVolume::ServerRemoved(%s)\n", serverInfo->GetServerName());
ServerVolume* serverVolume = _GetServerVolume(serverInfo->GetAddress());
if (!serverVolume)
return;
VolumePutter _(serverVolume);
serverVolume->SetUnmounting(true);
}
ServerVolume*
RootVolume::_GetServerVolume(const char* name)
{
Volume* volume = GetChildVolume(name);
if (!volume)
return NULL;
if (ServerVolume* serverVolume = dynamic_cast<ServerVolume*>(volume))
return serverVolume;
fVolumeManager->PutVolume(volume);
return NULL;
}
ServerVolume*
RootVolume::_GetServerVolume(const NetAddress& address)
{
AutoLocker<Locker> locker(fLock);
VirtualDirIterator iterator;
iterator.SetDirectory(fRootNode, true);
const char* name;
Node* node;
while (iterator.GetCurrentEntry(&name, &node)) {
iterator.NextEntry();
ServerVolume* volume = dynamic_cast<ServerVolume*>(node->GetVolume());
if (volume && volume->GetServerAddress().GetIP() == address.GetIP()) {
return dynamic_cast<ServerVolume*>(
fVolumeManager->GetVolume(node->GetID()));
}
}
return NULL;
}