/*
* Copyright 2001-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "Volume.h"
#include <util/AutoLock.h>
#include <fs/node_monitor.h>
#include <Notifications.h>
#include "AutoLocker.h"
#include "Compatibility.h"
#include "Debug.h"
#include "FileSystem.h"
#include "HashMap.h"
#include "kernel_interface.h"
#include "KernelRequestHandler.h"
#include "PortReleaser.h"
#include "RequestAllocator.h"
#include "Requests.h"
#include "Settings.h"
#include "SingleReplyRequestHandler.h"
// The time after which the notification thread times out at the port and
// restarts the loop. Of interest only when the FS is deleted. It is the
// maximal time the destructor has to wait for the thread.
static const bigtime_t kNotificationRequestTimeout = 50000; // 50 ms
// #pragma mark - SelectSyncMap
struct FileSystem::SelectSyncMap
: public SynchronizedHashMap<HashKeyPointer<selectsync*>, int32*, Locker> {
};
// #pragma mark - NodeListenerKey
struct FileSystem::NodeListenerKey {
NodeListenerKey(void* clientListener, dev_t device, ino_t node)
:
fClientListener(clientListener),
fDevice(device),
fNode(node)
{
}
void* ClientListener() const
{
return fClientListener;
}
dev_t Device() const
{
return fDevice;
}
ino_t Node() const
{
return fNode;
}
uint32 HashValue() const
{
return (uint32)(addr_t)fClientListener ^ (uint32)fDevice
^ (uint32)fNode ^ (uint32)(fNode >> 32);
}
bool operator==(const NodeListenerKey& other) const
{
return fClientListener == other.fClientListener
&& fDevice == other.fDevice && fNode == other.fNode;
}
protected:
void* fClientListener;
dev_t fDevice;
ino_t fNode;
};
// #pragma mark - NodeListenerProxy
struct FileSystem::NodeListenerProxy : NodeListenerKey, NotificationListener {
NodeListenerProxy(FileSystem* fileSystem, void* clientListener,
dev_t device, ino_t node)
:
NodeListenerKey(clientListener, device, node),
fFileSystem(fileSystem)
{
}
virtual void EventOccurred(NotificationService& service,
const KMessage* event)
{
fFileSystem->_NodeListenerEventOccurred(this, event);
}
NodeListenerProxy*& HashTableLink()
{
return fHashTableLink;
}
status_t StartListening(uint32 flags)
{
return add_node_listener(fDevice, fNode, flags, *this);
}
status_t StopListening()
{
return remove_node_listener(fDevice, fNode, *this);
}
private:
FileSystem* fFileSystem;
NodeListenerProxy* fHashTableLink;
};
// #pragma mark - NodeListenerHashDefinition
struct FileSystem::NodeListenerHashDefinition {
typedef NodeListenerKey KeyType;
typedef NodeListenerProxy ValueType;
size_t HashKey(const NodeListenerKey& key) const
{
return key.HashValue();
}
size_t Hash(const NodeListenerProxy* value) const
{
return value->HashValue();
}
bool Compare(const NodeListenerKey& key,
const NodeListenerProxy* value) const
{
return key == *value;
}
NodeListenerProxy*& GetLink(NodeListenerProxy* value) const
{
return value->HashTableLink();
}
};
// #pragma mark - FileSystem
// constructor
FileSystem::FileSystem()
:
fVolumes(),
fName(),
fTeam(-1),
fNotificationPort(NULL),
fNotificationThread(-1),
fPortPool(),
fSelectSyncs(NULL),
fSettings(NULL),
fUserlandServerTeam(-1),
fInitialized(false),
fTerminating(false)
{
mutex_init(&fVolumeLock, "userlandfs volumes");
mutex_init(&fVNodeOpsLock, "userlandfs vnode ops");
mutex_init(&fNodeListenersLock, "userlandfs node listeners");
}
// destructor
FileSystem::~FileSystem()
{
fTerminating = true;
// wait for the notification thread to terminate
if (fNotificationThread >= 0) {
int32 result;
wait_for_thread(fNotificationThread, &result);
}
// delete our data structures
if (fNodeListeners != NULL) {
MutexLocker nodeListenersLocker(fNodeListenersLock);
NodeListenerProxy* proxy = fNodeListeners->Clear(true);
while (proxy != NULL) {
NodeListenerProxy* next = proxy->HashTableLink();
proxy->StopListening();
delete proxy;
proxy = next;
}
}
if (fSelectSyncs) {
for (SelectSyncMap::Iterator it = fSelectSyncs->GetIterator();
it.HasNext();) {
SelectSyncMap::Entry entry = it.Next();
delete entry.value;
}
delete fSelectSyncs;
}
delete fSettings;
// delete vnode ops vectors -- there shouldn't be any left, though
VNodeOps* ops = fVNodeOps.Clear();
int32 count = 0;
while (ops != NULL) {
count++;
VNodeOps* next = ops->hash_link;
free(ops);
ops = next;
}
if (count > 0)
WARN(("Deleted %" B_PRId32 " vnode ops vectors!\n", count));
mutex_destroy(&fVolumeLock);
mutex_destroy(&fVNodeOpsLock);
mutex_destroy(&fNodeListenersLock);
}
// Init
status_t
FileSystem::Init(const char* name, team_id team, Port::Info* infos, int32 count,
const FSCapabilities& capabilities)
{
PRINT(("FileSystem::Init(\"%s\", %p, %" B_PRId32 ")\n", name, infos,
count));
capabilities.Dump();
// check parameters
if (!name || !infos || count < 2)
RETURN_ERROR(B_BAD_VALUE);
// set the name
if (!fName.SetTo(name))
return B_NO_MEMORY;
// init VNodeOps map
status_t error = fVNodeOps.Init();
if (error != B_OK)
return error;
fTeam = team;
fCapabilities = capabilities;
// create the select sync entry map
fSelectSyncs = new(nothrow) SelectSyncMap;
if (!fSelectSyncs)
return B_NO_MEMORY;
// create the node listener proxy map
fNodeListeners = new(std::nothrow) NodeListenerMap;
if (fNodeListeners == NULL || fNodeListeners->Init() != B_OK)
return B_NO_MEMORY;
// create the request ports
// the notification port
fNotificationPort = new(nothrow) RequestPort(infos);
if (!fNotificationPort)
RETURN_ERROR(B_NO_MEMORY);
error = fNotificationPort->InitCheck();
if (error != B_OK)
return error;
// the other request ports
for (int32 i = 1; i < count; i++) {
RequestPort* port = new(nothrow) RequestPort(infos + i);
if (!port)
RETURN_ERROR(B_NO_MEMORY);
error = port->InitCheck();
if (error == B_OK)
error = fPortPool.AddPort(port);
if (error != B_OK) {
delete port;
RETURN_ERROR(error);
}
}
// get the userland team
port_info portInfo;
error = get_port_info(infos[0].owner_port, &portInfo);
if (error != B_OK)
RETURN_ERROR(error);
fUserlandServerTeam = portInfo.team;
// print some info about the userland team
D(
PRINT((" userland team is: %" B_PRId32 "\n", fUserlandServerTeam));
int32 cookie = 0;
thread_info threadInfo;
while (get_next_thread_info(fUserlandServerTeam, &cookie, &threadInfo)
== B_OK) {
PRINT((" userland thread: %" B_PRId32 ": `%s'\n",
threadInfo.thread, threadInfo.name));
}
);
// load the settings
fSettings = new(nothrow) Settings;
if (fSettings) {
status_t settingsError = fSettings->SetTo(fName.GetString());
if (settingsError != B_OK) {
PRINT(("Failed to load settings: %s\n", strerror(settingsError)));
delete fSettings;
fSettings = NULL;
} else
fSettings->Dump();
} else
ERROR(("Failed to allocate settings.\n"));
// spawn the notification thread
#if USER
fNotificationThread = spawn_thread(_NotificationThreadEntry,
"UFS notification thread", B_NORMAL_PRIORITY, this);
#else
fNotificationThread = spawn_kernel_thread(_NotificationThreadEntry,
"UFS notification thread", B_NORMAL_PRIORITY, this);
#endif
if (fNotificationThread < 0)
RETURN_ERROR(fNotificationThread);
resume_thread(fNotificationThread);
fInitialized = (error == B_OK);
RETURN_ERROR(error);
}
// GetName
const char*
FileSystem::GetName() const
{
return fName.GetString();
}
// GetCapabilities
const FSCapabilities&
FileSystem::GetCapabilities() const
{
return fCapabilities;
}
// GetPortPool
RequestPortPool*
FileSystem::GetPortPool()
{
return &fPortPool;
}
// Mount
status_t
FileSystem::Mount(fs_volume* fsVolume, const char* device, uint32 flags,
const char* parameters, Volume** _volume)
{
// check initialization and parameters
if (!fInitialized || !_volume)
return B_BAD_VALUE;
// create volume
Volume* volume = new(nothrow) Volume(this, fsVolume);
if (!volume)
return B_NO_MEMORY;
// add volume to the volume list
MutexLocker locker(fVolumeLock);
status_t error = fVolumes.PushBack(volume);
locker.Unlock();
if (error != B_OK)
return error;
// mount volume
error = volume->Mount(device, flags, parameters);
if (error != B_OK) {
MutexLocker locker(fVolumeLock);
fVolumes.Remove(volume);
locker.Unlock();
volume->ReleaseReference();
return error;
}
*_volume = volume;
return error;
}
// Initialize
/*status_t
FileSystem::Initialize(const char* deviceName, const char* parameters,
size_t len)
{
// get a free port
RequestPort* port = fPortPool.AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(&fPortPool, port);
// prepare the request
RequestAllocator allocator(port->GetPort());
MountVolumeRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
error = allocator.AllocateString(request->device, deviceName);
if (error == B_OK)
error = allocator.AllocateData(request->parameters, parameters, len, 1);
if (error != B_OK)
return error;
// send the request
SingleReplyRequestHandler handler(MOUNT_VOLUME_REPLY);
InitializeVolumeReply* reply;
error = port->SendRequest(&allocator, &handler, (Request**)&reply);
if (error != B_OK)
return error;
RequestReleaser requestReleaser(port, reply);
// process the reply
if (reply->error != B_OK)
return reply->error;
return error;
}*/
// VolumeUnmounted
void
FileSystem::VolumeUnmounted(Volume* volume)
{
MutexLocker locker(fVolumeLock);
fVolumes.Remove(volume);
}
// GetVolume
Volume*
FileSystem::GetVolume(dev_t id)
{
MutexLocker _(fVolumeLock);
for (Vector<Volume*>::Iterator it = fVolumes.Begin();
it != fVolumes.End();
it++) {
Volume* volume = *it;
if (volume->GetID() == id) {
volume->AcquireReference();
return volume;
}
}
return NULL;
}
// GetIOCtlInfo
const IOCtlInfo*
FileSystem::GetIOCtlInfo(int command) const
{
return (fSettings ? fSettings->GetIOCtlInfo(command) : NULL);
}
// AddSelectSyncEntry
status_t
FileSystem::AddSelectSyncEntry(selectsync* sync)
{
AutoLocker<SelectSyncMap> _(fSelectSyncs);
int32* count = fSelectSyncs->Get(sync);
if (!count) {
count = new(nothrow) int32(0);
if (!count)
return B_NO_MEMORY;
status_t error = fSelectSyncs->Put(sync, count);
if (error != B_OK) {
delete count;
return error;
}
}
(*count)++;
return B_OK;
}
// RemoveSelectSyncEntry
void
FileSystem::RemoveSelectSyncEntry(selectsync* sync)
{
AutoLocker<SelectSyncMap> _(fSelectSyncs);
if (int32* count = fSelectSyncs->Get(sync)) {
if (--(*count) <= 0) {
fSelectSyncs->Remove(sync);
delete count;
}
}
}
// KnowsSelectSyncEntry
bool
FileSystem::KnowsSelectSyncEntry(selectsync* sync)
{
return fSelectSyncs->ContainsKey(sync);
}
// AddNodeListener
status_t
FileSystem::AddNodeListener(dev_t device, ino_t node, uint32 flags,
void* listener)
{
MutexLocker nodeListenersLocker(fNodeListenersLock);
// lookup the proxy
NodeListenerProxy* proxy = fNodeListeners->Lookup(
NodeListenerKey(listener, device, node));
if (proxy != NULL)
return proxy->StartListening(flags);
// it doesn't exist yet -- create it
proxy = new(std::nothrow) NodeListenerProxy(this, listener, device, node);
if (proxy == NULL)
return B_NO_MEMORY;
// start listening
status_t error = proxy->StartListening(flags);
if (error != B_OK) {
delete proxy;
return error;
}
fNodeListeners->Insert(proxy);
return B_OK;
}
// RemoveNodeListener
status_t
FileSystem::RemoveNodeListener(dev_t device, ino_t node, void* listener)
{
MutexLocker nodeListenersLocker(fNodeListenersLock);
// lookup the proxy
NodeListenerProxy* proxy = fNodeListeners->Lookup(
NodeListenerKey(listener, device, node));
if (proxy == NULL)
return B_BAD_VALUE;
status_t error = proxy->StopListening();
fNodeListeners->Remove(proxy);
delete proxy;
return error;
}
// GetVNodeOps
VNodeOps*
FileSystem::GetVNodeOps(const FSVNodeCapabilities& capabilities)
{
MutexLocker locker(fVNodeOpsLock);
// do we already have ops for those capabilities
VNodeOps* ops = fVNodeOps.Lookup(capabilities);
if (ops != NULL) {
ops->refCount++;
return ops;
}
// no, create a new object
fs_vnode_ops* opsVector = new(std::nothrow) fs_vnode_ops;
if (opsVector == NULL)
return NULL;
// set the operations
_InitVNodeOpsVector(opsVector, capabilities);
// create the VNodeOps object
ops = new(std::nothrow) VNodeOps(capabilities, opsVector);
if (ops == NULL) {
delete opsVector;
return NULL;
}
fVNodeOps.Insert(ops);
return ops;
}
// PutVNodeOps
void
FileSystem::PutVNodeOps(VNodeOps* ops)
{
MutexLocker locker(fVNodeOpsLock);
if (--ops->refCount == 0) {
fVNodeOps.Remove(ops);
delete ops;
}
}
// IsUserlandServerThread
bool
FileSystem::IsUserlandServerThread() const
{
thread_info info;
get_thread_info(find_thread(NULL), &info);
return (info.team == fUserlandServerTeam);
}
// _InitVNodeOpsVector
void
FileSystem::_InitVNodeOpsVector(fs_vnode_ops* ops,
const FSVNodeCapabilities& capabilities)
{
memcpy(ops, &gUserlandFSVnodeOps, sizeof(fs_vnode_ops));
#undef CLEAR_UNSUPPORTED
#define CLEAR_UNSUPPORTED(capability, op) \
if (!capabilities.Get(capability)) \
ops->op = NULL
// vnode operations
// FS_VNODE_CAPABILITY_LOOKUP: lookup
// FS_VNODE_CAPABILITY_GET_VNODE_NAME: get_vnode_name
// emulated in userland
// FS_VNODE_CAPABILITY_PUT_VNODE: put_vnode
// FS_VNODE_CAPABILITY_REMOVE_VNODE: remove_vnode
// needed by Volume to clean up
// asynchronous I/O
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_IO, io);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CANCEL_IO, cancel_io);
// cache file access
ops->get_file_map = NULL; // never used
// common operations
// FS_VNODE_CAPABILITY_IOCTL: ioctl
// needed by Volume
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_SET_FLAGS, set_flags);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_SELECT, select);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_DESELECT, deselect);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_FSYNC, fsync);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_SYMLINK, read_symlink);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_SYMLINK, create_symlink);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_LINK, link);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_UNLINK, unlink);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_RENAME, rename);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_ACCESS, access);
// FS_VNODE_CAPABILITY_READ_STAT: read_stat
// needed by Volume
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_STAT, write_stat);
// file operations
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE, create);
// FS_VNODE_CAPABILITY_OPEN: open
// mandatory
// FS_VNODE_CAPABILITY_CLOSE: close
// needed by Volume
// FS_VNODE_CAPABILITY_FREE_COOKIE: free_cookie
// needed by Volume
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ, read);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE, write);
// directory operations
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_DIR, create_dir);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REMOVE_DIR, remove_dir);
// FS_VNODE_CAPABILITY_OPEN_DIR: open_dir
// mandatory
// FS_VNODE_CAPABILITY_CLOSE_DIR: close_dir
// needed by Volume
// FS_VNODE_CAPABILITY_FREE_DIR_COOKIE: free_dir_cookie
// needed by Volume
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_DIR, read_dir);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REWIND_DIR, rewind_dir);
// attribute directory operations
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, open_attr_dir);
// FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR: close_attr_dir
// needed by Volume
// FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE: free_attr_dir_cookie
// needed by Volume
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR_DIR, read_attr_dir);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, rewind_attr_dir);
// attribute operations
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_ATTR, create_attr);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_OPEN_ATTR, open_attr);
// FS_VNODE_CAPABILITY_CLOSE_ATTR: close_attr
// needed by Volume
// FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE: free_attr_cookie
// needed by Volume
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR, read_attr);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_ATTR, write_attr);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR_STAT, read_attr_stat);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_ATTR_STAT, write_attr_stat);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_RENAME_ATTR, rename_attr);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REMOVE_ATTR, remove_attr);
// support for node and FS layers
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_SPECIAL_NODE,
create_special_node);
CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_GET_SUPER_VNODE, get_super_vnode);
#undef CLEAR_UNSUPPORTED
}
// _NodeListenerEventOccurred
void
FileSystem::_NodeListenerEventOccurred(NodeListenerProxy* proxy,
const KMessage* event)
{
// get a free port
RequestPort* port = fPortPool.AcquirePort();
if (port == NULL)
return;
PortReleaser _(&fPortPool, port);
// prepare the request
RequestAllocator allocator(port->GetPort());
NodeMonitoringEventRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return;
error = allocator.AllocateData(request->event, event->Buffer(),
event->ContentSize(), 1);
if (error != B_OK)
return;
Thread* thread = thread_get_current_thread();
request->team = thread->team->id;
request->thread = thread->id;
request->user = geteuid();
request->group = getegid();
request->listener = proxy->ClientListener();
// send the request
KernelRequestHandler handler(this, NODE_MONITORING_EVENT_REPLY);
port->SendRequest(&allocator, &handler);
}
// _NotificationThreadEntry
int32
FileSystem::_NotificationThreadEntry(void* data)
{
return ((FileSystem*)data)->_NotificationThread();
}
// _NotificationThread
int32
FileSystem::_NotificationThread()
{
// process the notification requests until the FS is deleted
while (!fTerminating) {
if (fNotificationPort->InitCheck() != B_OK)
return fNotificationPort->InitCheck();
KernelRequestHandler handler(this, NO_REQUEST);
fNotificationPort->HandleRequests(&handler, NULL,
kNotificationRequestTimeout);
}
// We eat all remaining notification requests, so that they aren't
// presented to the file system, when it is mounted next time.
// TODO: We should probably use a special handler that sends an ack reply,
// but ignores the requests otherwise.
KernelRequestHandler handler(this, NO_REQUEST);
fNotificationPort->HandleRequests(&handler, NULL, 0);
return 0;
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fNodeListeners.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fHashTableLink.
↑ V547 Expression 'error == ((int) 0)' is always true.