/*
* Copyright 2001-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "Volume.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <algorithm>
#include <fs_cache.h>
#include <util/AutoLock.h>
#include <util/OpenHashTable.h>
#include <fs/fd.h> // kernel private
#include <io_requests.h>
#include <thread.h>
#include "IORequest.h" // kernel internal
#include "Compatibility.h"
#include "Debug.h"
#include "FileSystem.h"
#include "IOCtlInfo.h"
#include "kernel_interface.h"
#include "KernelRequestHandler.h"
#include "PortReleaser.h"
#include "RequestAllocator.h"
#include "RequestPort.h"
#include "Requests.h"
#include "userlandfs_ioctl.h"
// missing ioctl()s
// TODO: Place somewhere else.
#define IOCTL_FILE_UNCACHED_IO 10000
#define IOCTL_CREATE_TIME 10002
#define IOCTL_MODIFIED_TIME 10003
// If a thread of the userland server enters userland FS kernel code and
// is sending a request, this is the time after which it shall time out
// waiting for a reply.
static const bigtime_t kUserlandServerlandPortTimeout = 10000000; // 10s
// VNode
struct Volume::VNode {
ino_t id;
void* clientNode;
void* fileCache;
VNodeOps* ops;
int32 useCount;
bool valid;
bool published;
VNode* hash_link;
VNode(ino_t id, void* clientNode, VNodeOps* ops)
:
id(id),
clientNode(clientNode),
fileCache(NULL),
ops(ops),
useCount(0),
valid(true),
published(true)
{
}
void Delete(Volume* volume)
{
if (ops != NULL)
volume->GetFileSystem()->PutVNodeOps(ops);
delete this;
}
protected: // should be private, but gcc 2.95.3 issues a warning
~VNode()
{
if (fileCache != NULL) {
ERROR(("VNode %" B_PRId64 " still has a file cache!\n", id));
file_cache_delete(fileCache);
}
}
};
// VNodeHashDefinition
struct Volume::VNodeHashDefinition {
typedef ino_t KeyType;
typedef VNode ValueType;
size_t HashKey(ino_t key) const
{ return (uint32)key ^ (uint32)(key >> 32); }
size_t Hash(const VNode* value) const
{ return HashKey(value->id); }
bool Compare(ino_t key, const VNode* value) const
{ return value->id == key; }
VNode*& GetLink(VNode* value) const
{ return value->hash_link; }
};
// VNodeMap
struct Volume::VNodeMap
: public BOpenHashTable<VNodeHashDefinition> {
};
// IORequestInfo
struct Volume::IORequestInfo {
io_request* request;
int32 id;
IORequestInfo* idLink;
IORequestInfo* structLink;
IORequestInfo(io_request* request, int32 id)
:
request(request),
id(id)
{
}
};
// IORequestIDHashDefinition
struct Volume::IORequestIDHashDefinition {
typedef int32 KeyType;
typedef IORequestInfo ValueType;
size_t HashKey(int32 key) const
{ return key; }
size_t Hash(const IORequestInfo* value) const
{ return HashKey(value->id); }
bool Compare(int32 key, const IORequestInfo* value) const
{ return value->id == key; }
IORequestInfo*& GetLink(IORequestInfo* value) const
{ return value->idLink; }
};
// IORequestStructHashDefinition
struct Volume::IORequestStructHashDefinition {
typedef io_request* KeyType;
typedef IORequestInfo ValueType;
size_t HashKey(io_request* key) const
{ return (size_t)(addr_t)key; }
size_t Hash(const IORequestInfo* value) const
{ return HashKey(value->request); }
bool Compare(io_request* key, const IORequestInfo* value) const
{ return value->request == key; }
IORequestInfo*& GetLink(IORequestInfo* value) const
{ return value->structLink; }
};
// IORequestIDMap
struct Volume::IORequestIDMap
: public BOpenHashTable<IORequestIDHashDefinition> {
};
// IORequestStructMap
struct Volume::IORequestStructMap
: public BOpenHashTable<IORequestStructHashDefinition> {
};
// IterativeFDIOCookie
struct Volume::IterativeFDIOCookie : public BReferenceable {
Volume* volume;
int fd;
int32 requestID;
void* clientCookie;
off_t offset;
const file_io_vec* vecs;
uint32 vecCount;
IterativeFDIOCookie(Volume* volume, int fd, int32 requestID,
void* clientCookie, off_t offset, const file_io_vec* vecs,
uint32 vecCount)
:
volume(volume),
fd(fd),
requestID(requestID),
clientCookie(clientCookie),
offset(offset),
vecs(vecs),
vecCount(vecCount)
{
}
~IterativeFDIOCookie()
{
if (fd >= 0)
close(fd);
}
};
// AutoIncrementer
class Volume::AutoIncrementer {
public:
AutoIncrementer(int32* variable)
: fVariable(variable)
{
if (fVariable)
atomic_add(fVariable, 1);
}
~AutoIncrementer()
{
if (fVariable)
atomic_add(fVariable, -1);
}
void Keep()
{
fVariable = NULL;
}
private:
int32* fVariable;
};
// IORequestRemover
class Volume::IORequestRemover {
public:
IORequestRemover(Volume* volume, int32 requestID)
:
fVolume(volume),
fRequestID(requestID)
{
}
~IORequestRemover()
{
if (fVolume != NULL)
fVolume->_UnregisterIORequest(fRequestID);
}
void Detach()
{
fVolume = NULL;
}
private:
Volume* fVolume;
int32 fRequestID;
};
// VNodeRemover
class Volume::VNodeRemover {
public:
VNodeRemover(Volume* volume, VNode* node)
:
fVolume(volume),
fNode(node)
{
}
~VNodeRemover()
{
if (fNode != NULL) {
MutexLocker locker(fVolume->fLock);
fVolume->fVNodes->Remove(fNode);
locker.Unlock();
fNode->Delete(fVolume);
}
}
private:
Volume* fVolume;
VNode* fNode;
};
// HasVNodeCapability
inline bool
Volume::HasVNodeCapability(VNode* vnode, int capability) const
{
return vnode->ops->capabilities.Get(capability);
}
// constructor
Volume::Volume(FileSystem* fileSystem, fs_volume* fsVolume)
:
BReferenceable(),
fFileSystem(fileSystem),
fFSVolume(fsVolume),
fUserlandVolume(NULL),
fRootID(0),
fRootNode(NULL),
fOpenFiles(0),
fOpenDirectories(0),
fOpenAttributeDirectories(0),
fOpenAttributes(0),
fOpenIndexDirectories(0),
fOpenQueries(0),
fVNodes(NULL),
fIORequestInfosByID(NULL),
fIORequestInfosByStruct(NULL),
fLastIORequestID(0),
fVNodeCountingEnabled(false)
{
mutex_init(&fLock, "userlandfs volume");
}
// destructor
Volume::~Volume()
{
mutex_destroy(&fLock);
delete fIORequestInfosByID;
delete fIORequestInfosByStruct;
delete fVNodes;
}
// #pragma mark - client methods
// GetVNode
status_t
Volume::GetVNode(ino_t vnid, void** _node)
{
PRINT(("get_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid));
void* vnode;
status_t error = get_vnode(fFSVolume, vnid, &vnode);
if (error == B_OK) {
_IncrementVNodeCount(vnid);
*_node = ((VNode*)vnode)->clientNode;
}
return error;
}
// PutVNode
status_t
Volume::PutVNode(ino_t vnid)
{
PRINT(("put_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid));
// Decrement the count first. We might not have another chance, since
// put_vnode() could put the last reference, thus causing the node to be
// removed from our map. This is all not very dramatic, but this way we
// avoid an erroneous error message from _DecrementVNodeCount().
_DecrementVNodeCount(vnid);
return put_vnode(fFSVolume, vnid);
}
// AcquireVNode
status_t
Volume::AcquireVNode(ino_t vnid)
{
PRINT(("acquire_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid));
status_t error = acquire_vnode(fFSVolume, vnid);
if (error == B_OK)
_IncrementVNodeCount(vnid);
return error;
}
// NewVNode
status_t
Volume::NewVNode(ino_t vnid, void* clientNode,
const FSVNodeCapabilities& capabilities)
{
PRINT(("new_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid));
// lookup the node
MutexLocker locker(fLock);
VNode* node = fVNodes->Lookup(vnid);
if (node != NULL) {
// The node is already known -- this is an error.
RETURN_ERROR(B_BAD_VALUE);
}
// get the ops vector for the node
VNodeOps* ops = fFileSystem->GetVNodeOps(capabilities);
if (ops == NULL)
RETURN_ERROR(B_NO_MEMORY);
// create the node
node = new(std::nothrow) VNode(vnid, clientNode, ops);
if (node == NULL) {
fFileSystem->PutVNodeOps(ops);
RETURN_ERROR(B_NO_MEMORY);
}
node->published = false;
locker.Unlock();
// tell the VFS
status_t error = new_vnode(fFSVolume, vnid, node, node->ops->ops);
if (error != B_OK) {
node->Delete(this);
RETURN_ERROR(error);
}
// add the node to our map
locker.Lock();
fVNodes->Insert(node);
// Increment its use count. After new_vnode() the caller has a reference,
// but a subsequent publish_vnode() won't get another one. We handle that
// there.
if (fVNodeCountingEnabled)
node->useCount++;
return B_OK;
}
// PublishVNode
status_t
Volume::PublishVNode(ino_t vnid, void* clientNode, int type, uint32 flags,
const FSVNodeCapabilities& capabilities)
{
PRINT(("publish_vnode(%" B_PRId32 ", %" B_PRId64 ", %p)\n", GetID(), vnid,
clientNode));
// lookup the node
MutexLocker locker(fLock);
VNode* node = fVNodes->Lookup(vnid);
bool nodeKnown = node != NULL;
if (nodeKnown) {
if (node->published) {
WARN(("publish_vnode(): vnode (%" B_PRId32 ", %" B_PRId64
") already published!\n", GetID(), vnid));
RETURN_ERROR(B_BAD_VALUE);
}
} else if (!nodeKnown) {
// The node is not yet known -- create it.
// get the ops vector for the node
VNodeOps* ops = fFileSystem->GetVNodeOps(capabilities);
if (ops == NULL)
RETURN_ERROR(B_NO_MEMORY);
// create the node
node = new(std::nothrow) VNode(vnid, clientNode, ops);
if (node == NULL) {
fFileSystem->PutVNodeOps(ops);
RETURN_ERROR(B_NO_MEMORY);
}
}
locker.Unlock();
// tell the VFS
status_t error = publish_vnode(fFSVolume, vnid, node, node->ops->ops,
type, flags);
if (error != B_OK) {
if (nodeKnown) {
// The node was known, i.e. it had been made known via new_vnode()
// and thus already had a use count of 1. Decrement that use count
// and remove the node completely.
_DecrementVNodeCount(vnid);
_RemoveInvalidVNode(vnid);
} else
node->Delete(this);
RETURN_ERROR(error);
}
// add the node to our map, if not known yet
locker.Lock();
if (nodeKnown) {
// The node is now published. Don't increment its use count. It already
// has 1 from new_vnode() and this publish_vnode() didn't increment it.
node->published = true;
} else {
// New node: increment its use count and add it to the map.
if (fVNodeCountingEnabled)
node->useCount++;
fVNodes->Insert(node);
}
return B_OK;
}
// RemoveVNode
status_t
Volume::RemoveVNode(ino_t vnid)
{
PRINT(("remove_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid));
return remove_vnode(fFSVolume, vnid);
}
// UnremoveVNode
status_t
Volume::UnremoveVNode(ino_t vnid)
{
PRINT(("unremove_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid));
return unremove_vnode(fFSVolume, vnid);
}
// GetVNodeRemoved
status_t
Volume::GetVNodeRemoved(ino_t vnid, bool* removed)
{
PRINT(("get_vnode_removed(%" B_PRId32 ", %" B_PRId64 ", %p)\n", GetID(),
vnid, removed));
return get_vnode_removed(fFSVolume, vnid, removed);
}
// CreateFileCache
status_t
Volume::CreateFileCache(ino_t vnodeID, off_t size)
{
// lookup the node
MutexLocker locker(fLock);
VNode* vnode = fVNodes->Lookup(vnodeID);
if (vnode == NULL)
RETURN_ERROR(B_BAD_VALUE);
// does the node already have a file cache?
if (vnode->fileCache != NULL)
RETURN_ERROR(B_BAD_VALUE);
// create the file cache
locker.Unlock();
void* fileCache = file_cache_create(GetID(), vnodeID, size);
locker.Lock();
// re-check whether the node still lives
vnode = fVNodes->Lookup(vnodeID);
if (vnode == NULL) {
file_cache_delete(fileCache);
RETURN_ERROR(B_BAD_VALUE);
}
vnode->fileCache = fileCache;
return B_OK;
}
// DeleteFileCache
status_t
Volume::DeleteFileCache(ino_t vnodeID)
{
// lookup the node
MutexLocker locker(fLock);
VNode* vnode = fVNodes->Lookup(vnodeID);
if (vnode == NULL)
RETURN_ERROR(B_BAD_VALUE);
// does the node have a file cache
if (vnode->fileCache == NULL)
RETURN_ERROR(B_BAD_VALUE);
void* fileCache = vnode->fileCache;
vnode->fileCache = NULL;
locker.Unlock();
file_cache_delete(fileCache);
return B_OK;
}
// SetFileCacheEnabled
status_t
Volume::SetFileCacheEnabled(ino_t vnodeID, bool enabled)
{
// lookup the node
MutexLocker locker(fLock);
VNode* vnode = fVNodes->Lookup(vnodeID);
if (vnode == NULL)
RETURN_ERROR(B_BAD_VALUE);
// does the node have a file cache
if (vnode->fileCache == NULL)
RETURN_ERROR(B_BAD_VALUE);
void* fileCache = vnode->fileCache;
locker.Unlock();
// TODO: We should use some kind of ref counting to avoid that another thread
// deletes the file cache now that we have dropped the lock. Applies to the
// other file cache operations as well.
// enable/disable the file cache
if (enabled) {
file_cache_enable(fileCache);
return B_OK;
}
return file_cache_disable(fileCache);
}
// SetFileCacheSize
status_t
Volume::SetFileCacheSize(ino_t vnodeID, off_t size)
{
// lookup the node
MutexLocker locker(fLock);
VNode* vnode = fVNodes->Lookup(vnodeID);
if (vnode == NULL)
RETURN_ERROR(B_BAD_VALUE);
// does the node have a file cache
if (vnode->fileCache == NULL)
RETURN_ERROR(B_BAD_VALUE);
void* fileCache = vnode->fileCache;
locker.Unlock();
// set the size
return file_cache_set_size(fileCache, size);
}
// SyncFileCache
status_t
Volume::SyncFileCache(ino_t vnodeID)
{
// lookup the node
MutexLocker locker(fLock);
VNode* vnode = fVNodes->Lookup(vnodeID);
if (vnode == NULL)
RETURN_ERROR(B_BAD_VALUE);
// does the node have a file cache
if (vnode->fileCache == NULL)
RETURN_ERROR(B_BAD_VALUE);
void* fileCache = vnode->fileCache;
locker.Unlock();
// sync
return file_cache_sync(fileCache);
}
// ReadFileCache
status_t
Volume::ReadFileCache(ino_t vnodeID, void* cookie,
off_t offset, void* buffer, size_t* _size)
{
// lookup the node
MutexLocker locker(fLock);
VNode* vnode = fVNodes->Lookup(vnodeID);
if (vnode == NULL)
RETURN_ERROR(B_BAD_VALUE);
// does the node have a file cache
if (vnode->fileCache == NULL)
RETURN_ERROR(B_BAD_VALUE);
void* fileCache = vnode->fileCache;
locker.Unlock();
// read
return file_cache_read(fileCache, cookie, offset, buffer, _size);
}
// WriteFileCache
status_t
Volume::WriteFileCache(ino_t vnodeID, void* cookie,
off_t offset, const void* buffer, size_t* _size)
{
// lookup the node
MutexLocker locker(fLock);
VNode* vnode = fVNodes->Lookup(vnodeID);
if (vnode == NULL)
RETURN_ERROR(B_BAD_VALUE);
// does the node have a file cache
if (vnode->fileCache == NULL)
RETURN_ERROR(B_BAD_VALUE);
void* fileCache = vnode->fileCache;
locker.Unlock();
// read
return file_cache_write(fileCache, cookie, offset, buffer, _size);
}
status_t
Volume::ReadFromIORequest(int32 requestID, void* buffer, size_t size)
{
// get the request
io_request* request;
status_t error = _FindIORequest(requestID, &request);
if (error != B_OK)
RETURN_ERROR(error);
return read_from_io_request(request, buffer, size);
}
status_t
Volume::WriteToIORequest(int32 requestID, const void* buffer, size_t size)
{
// get the request
io_request* request;
status_t error = _FindIORequest(requestID, &request);
if (error != B_OK)
RETURN_ERROR(error);
return write_to_io_request(request, buffer, size);
}
// DoIterativeFDIO
status_t
Volume::DoIterativeFDIO(int fd, int32 requestID, void* clientCookie,
const file_io_vec* vecs, uint32 vecCount)
{
// get the request
io_request* request;
status_t error = _FindIORequest(requestID, &request);
if (error != B_OK)
RETURN_ERROR(error);
// copy the FD into the kernel
fd = dup_foreign_fd(fFileSystem->GetTeam(), fd, true);
if (fd < 0)
RETURN_ERROR(fd);
// create a cookie
IterativeFDIOCookie* cookie = new(std::nothrow) IterativeFDIOCookie(
this, fd, requestID, clientCookie, request->Offset(), vecs, vecCount);
if (cookie == NULL) {
close(fd);
RETURN_ERROR(B_NO_MEMORY);
}
// we need another reference, so we can still access the cookie below
cookie->AcquireReference();
// TODO: Up to this point we're responsible for calling the finished hook on
// error!
// call the kernel function
error = do_iterative_fd_io(fd, request, &_IterativeFDIOGetVecs,
&_IterativeFDIOFinished, cookie);
// unset the vecs -- they are on the stack an will become invalid when we
// return
MutexLocker _(fLock);
cookie->vecs = NULL;
cookie->vecCount = 0;
cookie->ReleaseReference();
return error;
}
status_t
Volume::NotifyIORequest(int32 requestID, status_t status)
{
// get the request
io_request* request;
status_t error = _FindIORequest(requestID, &request);
if (error != B_OK)
RETURN_ERROR(error);
notify_io_request(request, status);
return B_OK;
}
// #pragma mark - FS
// Mount
status_t
Volume::Mount(const char* device, uint32 flags, const char* parameters)
{
// create the maps
fVNodes = new(std::nothrow) VNodeMap;
fIORequestInfosByID = new(std::nothrow) IORequestIDMap;
fIORequestInfosByStruct = new(std::nothrow) IORequestStructMap;
if (fVNodes == NULL || fIORequestInfosByID == NULL
|| fIORequestInfosByStruct == NULL
|| fVNodes->Init() != B_OK
|| fIORequestInfosByID->Init() != B_OK
|| fIORequestInfosByStruct->Init() != B_OK) {
return B_NO_MEMORY;
}
// enable vnode counting
fVNodeCountingEnabled = true;
// init IORequest ID's
fLastIORequestID = 0;
// mount
status_t error = _Mount(device, flags, parameters);
if (error != B_OK)
RETURN_ERROR(error);
MutexLocker locker(fLock);
// fetch the root node, so that we can serve Walk() requests on it,
// after the connection to the userland server is gone
fRootNode = fVNodes->Lookup(fRootID);
if (fRootNode == NULL) {
// The root node was not added while mounting. That's a serious
// problem -- not only because we don't have it, but also because
// the VFS requires publish_vnode() to be invoked for the root node.
ERROR(("Volume::Mount(): new_vnode() was not called for root node! "
"Unmounting...\n"));
locker.Unlock();
Unmount();
return B_ERROR;
}
// Decrement the root node use count. The publish_vnode() the client FS
// did will be balanced by the VFS.
if (fVNodeCountingEnabled)
fRootNode->useCount--;
// init the volume ops vector we'll give the VFS
_InitVolumeOps();
return B_OK;
}
// Unmount
status_t
Volume::Unmount()
{
status_t error = _Unmount();
// free the memory associated with the maps
{
// vnodes
MutexLocker _(fLock);
if (fVNodes != NULL) {
VNode* node = fVNodes->Clear(true);
while (node != NULL) {
VNode* nextNode = node->hash_link;
node->Delete(this);
node = nextNode;
}
delete fVNodes;
fVNodes = NULL;
}
// io request infos
if (fIORequestInfosByID != NULL) {
fIORequestInfosByID->Clear();
delete fIORequestInfosByID;
fIORequestInfosByID = NULL;
}
if (fIORequestInfosByStruct != NULL) {
IORequestInfo* info = fIORequestInfosByStruct->Clear(true);
while (info != NULL) {
IORequestInfo* nextInfo = info->structLink;
delete info;
// TODO: We should probably also notify the request, if that
// hasn't happened yet.
info = nextInfo;
}
delete fIORequestInfosByStruct;
fIORequestInfosByStruct = NULL;
}
}
fFileSystem->VolumeUnmounted(this);
return error;
}
// Sync
status_t
Volume::Sync()
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_SYNC))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
SyncVolumeRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
// send the request
KernelRequestHandler handler(this, SYNC_VOLUME_REPLY);
SyncVolumeReply* reply;
error = _SendRequest(port, &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;
}
// ReadFSInfo
status_t
Volume::ReadFSInfo(fs_info* info)
{
// When the connection to the userland server is lost, we serve
// read_fs_info() requests manually.
status_t error = _ReadFSInfo(info);
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
WARN(("Volume::ReadFSInfo(): connection lost, emulating lookup `.'\n"));
info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
info->block_size = 512;
info->io_size = 512;
info->total_blocks = 0;
info->free_blocks = 0;
strlcpy(info->volume_name, fFileSystem->GetName(),
sizeof(info->volume_name));
strlcat(info->volume_name, ":disconnected", sizeof(info->volume_name));
error = B_OK;
}
return error;
}
// WriteFSInfo
status_t
Volume::WriteFSInfo(const struct fs_info *info, uint32 mask)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_WRITE_FS_INFO))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
WriteFSInfoRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->info = *info;
request->mask = mask;
// send the request
KernelRequestHandler handler(this, WRITE_FS_INFO_REPLY);
WriteFSInfoReply* reply;
error = _SendRequest(port, &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;
}
// #pragma mark - vnodes
// Lookup
status_t
Volume::Lookup(void* dir, const char* entryName, ino_t* vnid)
{
// When the connection to the userland server is lost, we serve
// lookup(fRootNode, `.') requests manually to allow clean unmounting.
status_t error = _Lookup(dir, entryName, vnid);
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()
&& dir == fRootNode && strcmp(entryName, ".") == 0) {
WARN(("Volume::Lookup(): connection lost, emulating lookup `.'\n"));
void* entryNode;
if (GetVNode(fRootID, &entryNode) != B_OK)
RETURN_ERROR(B_BAD_VALUE);
*vnid = fRootID;
// The VFS will balance the get_vnode() call for the FS.
_DecrementVNodeCount(*vnid);
return B_OK;
}
return error;
}
// GetVNodeName
status_t
Volume::GetVNodeName(void* _node, char* buffer, size_t bufferSize)
{
// We don't check the capability -- if not implemented by the client FS,
// the functionality is emulated in userland.
VNode* vnode = (VNode*)_node;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
GetVNodeNameRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->size = bufferSize;
// send the request
KernelRequestHandler handler(this, GET_VNODE_NAME_REPLY);
GetVNodeNameReply* reply;
error = _SendRequest(port, &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;
char* readBuffer = (char*)reply->buffer.GetData();
size_t nameLen = reply->buffer.GetSize();
nameLen = strnlen(readBuffer, nameLen);
if (nameLen <= 1 || nameLen >= bufferSize)
RETURN_ERROR(B_BAD_DATA);
memcpy(buffer, readBuffer, nameLen);
buffer[nameLen] = '\0';
_SendReceiptAck(port);
return error;
}
// ReadVNode
status_t
Volume::ReadVNode(ino_t vnid, bool reenter, void** _node, fs_vnode_ops** _ops,
int* type, uint32* flags)
{
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadVNodeRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->vnid = vnid;
request->reenter = reenter;
// add the uninitialized node to our map
VNode* vnode = new(std::nothrow) VNode(vnid, NULL, NULL);
if (vnode == NULL)
RETURN_ERROR(B_NO_MEMORY);
vnode->valid = false;
MutexLocker locker(fLock);
fVNodes->Insert(vnode);
locker.Unlock();
// send the request
KernelRequestHandler handler(this, READ_VNODE_REPLY);
ReadVNodeReply* reply;
error = _SendRequest(port, &allocator, &handler, (Request**)&reply);
if (error != B_OK) {
_RemoveInvalidVNode(vnid);
return error;
}
RequestReleaser requestReleaser(port, reply);
// process the reply
if (reply->error != B_OK) {
_RemoveInvalidVNode(vnid);
return reply->error;
}
// get the ops vector for the node
VNodeOps* ops = fFileSystem->GetVNodeOps(reply->capabilities);
if (ops == NULL) {
_RemoveInvalidVNode(vnid);
RETURN_ERROR(B_NO_MEMORY);
}
// everything went fine -- mark the node valid
locker.Lock();
vnode->clientNode = reply->node;
vnode->ops = ops;
vnode->valid = true;
*_node = vnode;
*type = reply->type;
*flags = reply->flags;
*_ops = ops->ops;
return B_OK;
}
// WriteVNode
status_t
Volume::WriteVNode(void* node, bool reenter)
{
status_t error = _WriteVNode(node, reenter);
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, since the VFS basically ignores the
// return value -- at least Haiku. The fshell panic()s; didn't check
// BeOS. It doesn't harm to appear to behave nicely. :-)
WARN(("Volume::WriteVNode(): connection lost, forcing write vnode\n"));
return B_OK;
}
return error;
}
// RemoveVNode
status_t
Volume::RemoveVNode(void* _node, bool reenter)
{
VNode* vnode = (VNode*)_node;
// At any rate remove the vnode from our map and delete it. We don't do that
// right now, though, since we might still need to serve file cache requests
// from the client FS.
VNodeRemover nodeRemover(this, vnode);
void* clientNode = vnode->clientNode;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
FSRemoveVNodeRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = clientNode;
request->reenter = reenter;
// send the request
KernelRequestHandler handler(this, FS_REMOVE_VNODE_REPLY);
FSRemoveVNodeReply* reply;
error = _SendRequest(port, &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;
}
// #pragma mark - asynchronous I/O
// DoIO
status_t
Volume::DoIO(void* _node, void* cookie, io_request* ioRequest)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_IO))
return B_UNSUPPORTED;
// register the IO request
int32 requestID;
status_t error = _RegisterIORequest(ioRequest, &requestID);
if (error != B_OK) {
notify_io_request(ioRequest, error);
return error;
}
IORequestRemover requestRemover(this, requestID);
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port) {
notify_io_request(ioRequest, B_ERROR);
return B_ERROR;
}
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
DoIORequest* request;
error = AllocateRequest(allocator, &request);
if (error != B_OK) {
notify_io_request(ioRequest, error);
return error;
}
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->fileCookie = cookie;
request->request = requestID;
request->offset = ioRequest->Offset();
request->length = ioRequest->Length();
request->isWrite = ioRequest->IsWrite();
request->isVIP = (ioRequest->Flags() & B_VIP_IO_REQUEST) != 0;
// send the request
KernelRequestHandler handler(this, DO_IO_REPLY);
DoIOReply* reply;
// TODO: when to notify the io_request?
error = _SendRequest(port, &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;
requestRemover.Detach();
return B_OK;
}
// CancelIO
status_t
Volume::CancelIO(void* _node, void* cookie, io_request* ioRequest)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CANCEL_IO))
return B_BAD_VALUE;
// find the request
int32 requestID;
status_t error = _FindIORequest(ioRequest, &requestID);
if (error != B_OK)
return error;
IORequestRemover requestRemover(this, requestID);
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
CancelIORequest* request;
error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->fileCookie = cookie;
request->request = requestID;
// send the request
KernelRequestHandler handler(this, CANCEL_IO_REPLY);
CancelIOReply* reply;
error = _SendRequest(port, &allocator, &handler, (Request**)&reply);
if (error != B_OK) {
_UnregisterIORequest(requestID);
return error;
}
RequestReleaser requestReleaser(port, reply);
// process the reply
if (reply->error != B_OK) {
_UnregisterIORequest(requestID);
return reply->error;
}
return B_OK;
}
// #pragma mark - nodes
// IOCtl
status_t
Volume::IOCtl(void* _node, void* cookie, uint32 command, void *buffer,
size_t len)
{
VNode* vnode = (VNode*)_node;
// check the command and its parameters
bool isBuffer = false;
int32 bufferSize = 0;
int32 writeSize = 0;
switch (command) {
case IOCTL_FILE_UNCACHED_IO:
buffer = NULL;
break;
case IOCTL_CREATE_TIME:
case IOCTL_MODIFIED_TIME:
isBuffer = 0;
bufferSize = 0;
writeSize = sizeof(bigtime_t);
break;
case USERLANDFS_IOCTL:
area_id area;
area_info info;
PRINT(("Volume::IOCtl(): USERLANDFS_IOCTL\n"));
if ((area = area_for(buffer)) >= 0) {
if (get_area_info(area, &info) == B_OK) {
if ((uint8*)buffer - (uint8*)info.address
+ sizeof(userlandfs_ioctl) <= info.size) {
if (strncmp(((userlandfs_ioctl*)buffer)->magic,
kUserlandFSIOCtlMagic,
USERLAND_IOCTL_MAGIC_LENGTH) == 0) {
return _InternalIOCtl((userlandfs_ioctl*)buffer,
bufferSize);
} else
PRINT(("Volume::IOCtl(): bad magic\n"));
} else
PRINT(("Volume::IOCtl(): bad buffer size\n"));
} else
PRINT(("Volume::IOCtl(): failed to get area info\n"));
} else
PRINT(("Volume::IOCtl(): bad area\n"));
// fall through...
default:
{
// We don't know the command. Check whether the FileSystem knows
// about it.
const IOCtlInfo* info = fFileSystem->GetIOCtlInfo(command);
if (!info) {
PRINT(("Volume::IOCtl(): unknown command\n"));
return B_BAD_VALUE;
}
isBuffer = info->isBuffer;
bufferSize = info->bufferSize;
writeSize = info->writeBufferSize;
// If the buffer shall indeed specify a buffer, check it.
if (info->isBuffer) {
if (!buffer) {
PRINT(("Volume::IOCtl(): buffer is NULL\n"));
return B_BAD_VALUE;
}
area_id area = area_for(buffer);
if (area < 0) {
PRINT(("Volume::IOCtl(): bad area\n"));
return B_BAD_VALUE;
}
area_info info;
if (get_area_info(area, &info) != B_OK) {
PRINT(("Volume::IOCtl(): failed to get area info\n"));
return B_BAD_VALUE;
}
int32 areaSize = info.size - ((uint8*)buffer
- (uint8*)info.address);
if (bufferSize > areaSize || writeSize > areaSize) {
PRINT(("Volume::IOCtl(): bad buffer size\n"));
return B_BAD_VALUE;
}
if (writeSize > 0 && !(info.protection & B_WRITE_AREA)) {
PRINT(("Volume::IOCtl(): buffer not writable\n"));
return B_BAD_VALUE;
}
}
break;
}
}
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_IOCTL))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
IOCtlRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->fileCookie = cookie;
request->command = command;
request->bufferParameter = buffer;
request->isBuffer = isBuffer;
request->lenParameter = len;
request->writeSize = writeSize;
if (isBuffer && bufferSize > 0) {
error = allocator.AllocateData(request->buffer, buffer, bufferSize, 8);
if (error != B_OK)
return error;
}
// send the request
KernelRequestHandler handler(this, IOCTL_REPLY);
IOCtlReply* reply;
error = _SendRequest(port, &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;
// Copy back the buffer even if the result is not B_OK. The protocol
// is defined by the FS developer and may include writing data into
// the buffer in some error cases.
if (isBuffer && writeSize > 0 && reply->buffer.GetData()) {
if (writeSize > reply->buffer.GetSize())
writeSize = reply->buffer.GetSize();
memcpy(buffer, reply->buffer.GetData(), writeSize);
_SendReceiptAck(port);
}
return reply->ioctlError;
}
// SetFlags
status_t
Volume::SetFlags(void* _node, void* cookie, int flags)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_SET_FLAGS))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
SetFlagsRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->fileCookie = cookie;
request->flags = flags;
// send the request
KernelRequestHandler handler(this, SET_FLAGS_REPLY);
SetFlagsReply* reply;
error = _SendRequest(port, &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;
}
// Select
status_t
Volume::Select(void* _node, void* cookie, uint8 event, selectsync* sync)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_SELECT)) {
notify_select_event(sync, event);
return B_OK;
}
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
SelectRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->fileCookie = cookie;
request->event = event;
request->sync = sync;
// add a selectsync entry
error = fFileSystem->AddSelectSyncEntry(sync);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, SELECT_REPLY);
SelectReply* reply;
error = _SendRequest(port, &allocator, &handler, (Request**)&reply);
if (error != B_OK) {
fFileSystem->RemoveSelectSyncEntry(sync);
return error;
}
RequestReleaser requestReleaser(port, reply);
// process the reply
if (reply->error != B_OK) {
fFileSystem->RemoveSelectSyncEntry(sync);
return reply->error;
}
return error;
}
// Deselect
status_t
Volume::Deselect(void* _node, void* cookie, uint8 event, selectsync* sync)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_DESELECT))
return B_OK;
struct SyncRemover {
SyncRemover(FileSystem* fs, selectsync* sync)
: fs(fs), sync(sync) {}
~SyncRemover() { fs->RemoveSelectSyncEntry(sync); }
FileSystem* fs;
selectsync* sync;
} syncRemover(fFileSystem, sync);
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
DeselectRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->fileCookie = cookie;
request->event = event;
request->sync = sync;
// send the request
KernelRequestHandler handler(this, DESELECT_REPLY);
DeselectReply* reply;
error = _SendRequest(port, &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;
}
// FSync
status_t
Volume::FSync(void* _node)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_FSYNC))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
FSyncRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
// send the request
KernelRequestHandler handler(this, FSYNC_REPLY);
FSyncReply* reply;
error = _SendRequest(port, &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;
}
// ReadSymlink
status_t
Volume::ReadSymlink(void* _node, char* buffer, size_t bufferSize,
size_t* bytesRead)
{
VNode* vnode = (VNode*)_node;
*bytesRead = 0;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_SYMLINK))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadSymlinkRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->size = bufferSize;
// send the request
KernelRequestHandler handler(this, READ_SYMLINK_REPLY);
ReadSymlinkReply* reply;
error = _SendRequest(port, &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;
void* readBuffer = reply->buffer.GetData();
if (reply->bytesRead > (uint32)reply->buffer.GetSize()
|| reply->bytesRead > bufferSize) {
return B_BAD_DATA;
}
if (reply->bytesRead > 0)
memcpy(buffer, readBuffer, reply->bytesRead);
*bytesRead = reply->bytesRead;
_SendReceiptAck(port);
return error;
}
// CreateSymlink
status_t
Volume::CreateSymlink(void* _dir, const char* name, const char* target,
int mode)
{
VNode* vnode = (VNode*)_dir;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CREATE_SYMLINK))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
CreateSymlinkRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
error = allocator.AllocateString(request->name, name);
if (error == B_OK)
error = allocator.AllocateString(request->target, target);
if (error != B_OK)
return error;
request->mode = mode;
// send the request
KernelRequestHandler handler(this, CREATE_SYMLINK_REPLY);
CreateSymlinkReply* reply;
error = _SendRequest(port, &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;
}
// Link
status_t
Volume::Link(void* _dir, const char* name, void* node)
{
VNode* vnode = (VNode*)_dir;
VNode* targetVnode = (VNode*)node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_LINK))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
LinkRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
error = allocator.AllocateString(request->name, name);
request->target = targetVnode->clientNode;
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, LINK_REPLY);
LinkReply* reply;
error = _SendRequest(port, &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;
}
// Unlink
status_t
Volume::Unlink(void* _dir, const char* name)
{
VNode* vnode = (VNode*)_dir;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_UNLINK))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
UnlinkRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
error = allocator.AllocateString(request->name, name);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, UNLINK_REPLY);
UnlinkReply* reply;
error = _SendRequest(port, &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;
}
// Rename
status_t
Volume::Rename(void* _oldDir, const char* oldName, void* _newDir,
const char* newName)
{
VNode* oldVNode = (VNode*)_oldDir;
VNode* newVNode = (VNode*)_newDir;
// check capability
if (!HasVNodeCapability(oldVNode, FS_VNODE_CAPABILITY_RENAME))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
RenameRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->oldDir = oldVNode->clientNode;
request->newDir = newVNode->clientNode;
error = allocator.AllocateString(request->oldName, oldName);
if (error == B_OK)
error = allocator.AllocateString(request->newName, newName);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, RENAME_REPLY);
RenameReply* reply;
error = _SendRequest(port, &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;
}
// Access
status_t
Volume::Access(void* _node, int mode)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_ACCESS))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
AccessRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->mode = mode;
// send the request
KernelRequestHandler handler(this, ACCESS_REPLY);
AccessReply* reply;
error = _SendRequest(port, &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;
}
// ReadStat
status_t
Volume::ReadStat(void* node, struct stat* st)
{
// When the connection to the userland server is lost, we serve
// read_stat(fRootNode) requests manually to allow clean unmounting.
status_t error = _ReadStat(node, st);
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()
&& node == fRootNode) {
WARN(("Volume::ReadStat(): connection lost, emulating stat for the "
"root node\n"));
st->st_dev = GetID();
st->st_ino = fRootID;
st->st_mode = ACCESSPERMS;
st->st_nlink = 1;
st->st_uid = 0;
st->st_gid = 0;
st->st_size = 512;
st->st_blksize = 512;
st->st_atime = 0;
st->st_mtime = 0;
st->st_ctime = 0;
st->st_crtime = 0;
error = B_OK;
}
return error;
}
// WriteStat
status_t
Volume::WriteStat(void* _node, const struct stat* st, uint32 mask)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_WRITE_STAT))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
WriteStatRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->st = *st;
request->mask = mask;
// send the request
KernelRequestHandler handler(this, WRITE_STAT_REPLY);
WriteStatReply* reply;
error = _SendRequest(port, &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;
}
// #pragma mark - files
// Create
status_t
Volume::Create(void* _dir, const char* name, int openMode, int mode,
void** cookie, ino_t* vnid)
{
VNode* vnode = (VNode*)_dir;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CREATE))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
AutoIncrementer incrementer(&fOpenFiles);
// prepare the request
RequestAllocator allocator(port->GetPort());
CreateRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
error = allocator.AllocateString(request->name, name);
request->openMode = openMode;
request->mode = mode;
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, CREATE_REPLY);
CreateReply* reply;
error = _SendRequest(port, &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;
incrementer.Keep();
*vnid = reply->vnid;
*cookie = reply->fileCookie;
// The VFS will balance the publish_vnode() call for the FS.
if (error == B_OK)
_DecrementVNodeCount(*vnid);
return error;
}
// Open
status_t
Volume::Open(void* _node, int openMode, void** cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_OPEN))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
AutoIncrementer incrementer(&fOpenFiles);
// prepare the request
RequestAllocator allocator(port->GetPort());
OpenRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->openMode = openMode;
// send the request
KernelRequestHandler handler(this, OPEN_REPLY);
OpenReply* reply;
error = _SendRequest(port, &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;
incrementer.Keep();
*cookie = reply->fileCookie;
return error;
}
// Close
status_t
Volume::Close(void* node, void* cookie)
{
status_t error = _Close(node, cookie);
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. OBOS ignores it completely. The fsshell returns it to the
// userland, but considers the node closed anyway.
WARN(("Volume::Close(): connection lost, forcing close\n"));
return B_OK;
}
return error;
}
// FreeCookie
status_t
Volume::FreeCookie(void* node, void* cookie)
{
status_t error = _FreeCookie(node, cookie);
bool disconnected = false;
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. It's completely ignored by OBOS as well as by the fsshell.
WARN(("Volume::FreeCookie(): connection lost, forcing free cookie\n"));
error = B_OK;
disconnected = true;
}
int32 openFiles = atomic_add(&fOpenFiles, -1);
if (openFiles <= 1 && disconnected)
_PutAllPendingVNodes();
return error;
}
// Read
status_t
Volume::Read(void* _node, void* cookie, off_t pos, void* buffer,
size_t bufferSize, size_t* bytesRead)
{
VNode* vnode = (VNode*)_node;
*bytesRead = 0;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->fileCookie = cookie;
request->pos = pos;
request->size = bufferSize;
// send the request
KernelRequestHandler handler(this, READ_REPLY);
ReadReply* reply;
error = _SendRequest(port, &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;
void* readBuffer = reply->buffer.GetData();
if (reply->bytesRead > (uint32)reply->buffer.GetSize()
|| reply->bytesRead > bufferSize) {
return B_BAD_DATA;
}
if (reply->bytesRead > 0
&& user_memcpy(buffer, readBuffer, reply->bytesRead) < B_OK) {
return B_BAD_ADDRESS;
}
*bytesRead = reply->bytesRead;
_SendReceiptAck(port);
return error;
}
// Write
status_t
Volume::Write(void* _node, void* cookie, off_t pos, const void* buffer,
size_t size, size_t* bytesWritten)
{
VNode* vnode = (VNode*)_node;
*bytesWritten = 0;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_WRITE))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
WriteRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->fileCookie = cookie;
request->pos = pos;
error = allocator.AllocateData(request->buffer, buffer, size, 1);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, WRITE_REPLY);
WriteReply* reply;
error = _SendRequest(port, &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;
*bytesWritten = reply->bytesWritten;
return error;
}
// #pragma mark - directories
// CreateDir
status_t
Volume::CreateDir(void* _dir, const char* name, int mode)
{
VNode* vnode = (VNode*)_dir;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CREATE_DIR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
CreateDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
error = allocator.AllocateString(request->name, name);
request->mode = mode;
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, CREATE_DIR_REPLY);
CreateDirReply* reply;
error = _SendRequest(port, &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;
}
// RemoveDir
status_t
Volume::RemoveDir(void* _dir, const char* name)
{
VNode* vnode = (VNode*)_dir;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_REMOVE_DIR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
RemoveDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
error = allocator.AllocateString(request->name, name);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, REMOVE_DIR_REPLY);
RemoveDirReply* reply;
error = _SendRequest(port, &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;
}
// OpenDir
status_t
Volume::OpenDir(void* _node, void** cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_OPEN_DIR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
AutoIncrementer incrementer(&fOpenDirectories);
// prepare the request
RequestAllocator allocator(port->GetPort());
OpenDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
// send the request
KernelRequestHandler handler(this, OPEN_DIR_REPLY);
OpenDirReply* reply;
error = _SendRequest(port, &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;
incrementer.Keep();
*cookie = reply->dirCookie;
return error;
}
// CloseDir
status_t
Volume::CloseDir(void* node, void* cookie)
{
status_t error = _CloseDir(node, cookie);
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. OBOS ignores it completely. The fsshell returns it to the
// userland, but considers the node closed anyway.
WARN(("Volume::CloseDir(): connection lost, forcing close dir\n"));
return B_OK;
}
return error;
}
// FreeDirCookie
status_t
Volume::FreeDirCookie(void* node, void* cookie)
{
status_t error = _FreeDirCookie(node, cookie);
bool disconnected = false;
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. It's completely ignored by OBOS as well as by the fsshell.
WARN(("Volume::FreeDirCookie(): connection lost, forcing free dir "
"cookie\n"));
error = B_OK;
disconnected = true;
}
int32 openDirs = atomic_add(&fOpenDirectories, -1);
if (openDirs <= 1 && disconnected)
_PutAllPendingVNodes();
return error;
}
// ReadDir
status_t
Volume::ReadDir(void* _node, void* cookie, void* buffer, size_t bufferSize,
uint32 count, uint32* countRead)
{
VNode* vnode = (VNode*)_node;
*countRead = 0;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_DIR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->dirCookie = cookie;
request->bufferSize = bufferSize;
request->count = count;
// send the request
KernelRequestHandler handler(this, READ_DIR_REPLY);
ReadDirReply* reply;
error = _SendRequest(port, &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;
if (reply->count < 0 || reply->count > count)
return B_BAD_DATA;
if ((int32)bufferSize < reply->buffer.GetSize())
return B_BAD_DATA;
PRINT(("Volume::ReadDir(): buffer returned: %" B_PRId32 " bytes\n",
reply->buffer.GetSize()));
*countRead = reply->count;
if (*countRead > 0) {
// copy the buffer -- limit the number of bytes to copy
uint32 maxBytes = *countRead
* (sizeof(struct dirent) + B_FILE_NAME_LENGTH);
uint32 copyBytes = reply->buffer.GetSize();
if (copyBytes > maxBytes)
copyBytes = maxBytes;
memcpy(buffer, reply->buffer.GetData(), copyBytes);
}
_SendReceiptAck(port);
return error;
}
// RewindDir
status_t
Volume::RewindDir(void* _node, void* cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_REWIND_DIR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
RewindDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->dirCookie = cookie;
// send the request
KernelRequestHandler handler(this, REWIND_DIR_REPLY);
RewindDirReply* reply;
error = _SendRequest(port, &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;
}
// #pragma mark - attribute directories
// OpenAttrDir
status_t
Volume::OpenAttrDir(void* _node, void** cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_OPEN_ATTR_DIR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
AutoIncrementer incrementer(&fOpenAttributeDirectories);
// prepare the request
RequestAllocator allocator(port->GetPort());
OpenAttrDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
// send the request
KernelRequestHandler handler(this, OPEN_ATTR_DIR_REPLY);
OpenAttrDirReply* reply;
error = _SendRequest(port, &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;
incrementer.Keep();
*cookie = reply->attrDirCookie;
return error;
}
// CloseAttrDir
status_t
Volume::CloseAttrDir(void* node, void* cookie)
{
status_t error = _CloseAttrDir(node, cookie);
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. OBOS ignores it completely. The fsshell returns it to the
// userland, but considers the node closed anyway.
WARN(("Volume::CloseAttrDir(): connection lost, forcing close attr "
"dir\n"));
return B_OK;
}
return error;
}
// FreeAttrDirCookie
status_t
Volume::FreeAttrDirCookie(void* node, void* cookie)
{
status_t error = _FreeAttrDirCookie(node, cookie);
bool disconnected = false;
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. It's completely ignored by OBOS as well as by the fsshell.
WARN(("Volume::FreeAttrDirCookie(): connection lost, forcing free attr "
"dir cookie\n"));
error = B_OK;
disconnected = true;
}
int32 openAttrDirs = atomic_add(&fOpenAttributeDirectories, -1);
if (openAttrDirs <= 1 && disconnected)
_PutAllPendingVNodes();
return error;
}
// ReadAttrDir
status_t
Volume::ReadAttrDir(void* _node, void* cookie, void* buffer,
size_t bufferSize, uint32 count, uint32* countRead)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_ATTR_DIR))
return B_BAD_VALUE;
*countRead = 0;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadAttrDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->attrDirCookie = cookie;
request->bufferSize = bufferSize;
request->count = count;
// send the request
KernelRequestHandler handler(this, READ_ATTR_DIR_REPLY);
ReadAttrDirReply* reply;
error = _SendRequest(port, &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;
if (reply->count < 0 || reply->count > count)
return B_BAD_DATA;
if ((int32)bufferSize < reply->buffer.GetSize())
return B_BAD_DATA;
*countRead = reply->count;
if (*countRead > 0) {
// copy the buffer -- limit the number of bytes to copy
uint32 maxBytes = *countRead
* (sizeof(struct dirent) + B_ATTR_NAME_LENGTH);
uint32 copyBytes = reply->buffer.GetSize();
if (copyBytes > maxBytes)
copyBytes = maxBytes;
memcpy(buffer, reply->buffer.GetData(), copyBytes);
}
_SendReceiptAck(port);
return error;
}
// RewindAttrDir
status_t
Volume::RewindAttrDir(void* _node, void* cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_REWIND_ATTR_DIR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
RewindAttrDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->attrDirCookie = cookie;
// send the request
KernelRequestHandler handler(this, REWIND_ATTR_DIR_REPLY);
RewindAttrDirReply* reply;
error = _SendRequest(port, &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;
}
// #pragma mark - attributes
// CreateAttr
status_t
Volume::CreateAttr(void* _node, const char* name, uint32 type, int openMode,
void** cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CREATE_ATTR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
AutoIncrementer incrementer(&fOpenAttributes);
// prepare the request
RequestAllocator allocator(port->GetPort());
CreateAttrRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
error = allocator.AllocateString(request->name, name);
request->type = type;
request->openMode = openMode;
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, CREATE_ATTR_REPLY);
CreateAttrReply* reply;
error = _SendRequest(port, &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;
incrementer.Keep();
*cookie = reply->attrCookie;
return error;
}
// OpenAttr
status_t
Volume::OpenAttr(void* _node, const char* name, int openMode,
void** cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_OPEN_ATTR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
AutoIncrementer incrementer(&fOpenAttributes);
// prepare the request
RequestAllocator allocator(port->GetPort());
OpenAttrRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
error = allocator.AllocateString(request->name, name);
request->openMode = openMode;
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, OPEN_ATTR_REPLY);
OpenAttrReply* reply;
error = _SendRequest(port, &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;
incrementer.Keep();
*cookie = reply->attrCookie;
return error;
}
// CloseAttr
status_t
Volume::CloseAttr(void* node, void* cookie)
{
status_t error = _CloseAttr(node, cookie);
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. OBOS ignores it completely. The fsshell returns it to the
// userland, but considers the node closed anyway.
WARN(("Volume::CloseAttr(): connection lost, forcing close attr\n"));
return B_OK;
}
return error;
}
// FreeAttrCookie
status_t
Volume::FreeAttrCookie(void* node, void* cookie)
{
status_t error = _FreeAttrCookie(node, cookie);
bool disconnected = false;
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. It's completely ignored by OBOS as well as by the fsshell.
WARN(("Volume::FreeAttrCookie(): connection lost, forcing free attr "
"cookie\n"));
error = B_OK;
disconnected = true;
}
int32 openAttributes = atomic_add(&fOpenAttributes, -1);
if (openAttributes <= 1 && disconnected)
_PutAllPendingVNodes();
return error;
}
// ReadAttr
status_t
Volume::ReadAttr(void* _node, void* cookie, off_t pos,
void* buffer, size_t bufferSize, size_t* bytesRead)
{
VNode* vnode = (VNode*)_node;
*bytesRead = 0;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_ATTR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadAttrRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->attrCookie = cookie;
request->pos = pos;
request->size = bufferSize;
// send the request
KernelRequestHandler handler(this, READ_ATTR_REPLY);
ReadAttrReply* reply;
error = _SendRequest(port, &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;
void* readBuffer = reply->buffer.GetData();
if (reply->bytesRead > (uint32)reply->buffer.GetSize()
|| reply->bytesRead > bufferSize) {
return B_BAD_DATA;
}
if (reply->bytesRead > 0)
memcpy(buffer, readBuffer, reply->bytesRead);
*bytesRead = reply->bytesRead;
_SendReceiptAck(port);
return error;
}
// WriteAttr
status_t
Volume::WriteAttr(void* _node, void* cookie, off_t pos,
const void* buffer, size_t bufferSize, size_t* bytesWritten)
{
VNode* vnode = (VNode*)_node;
*bytesWritten = 0;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_WRITE_ATTR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
WriteAttrRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->attrCookie = cookie;
request->pos = pos;
error = allocator.AllocateData(request->buffer, buffer, bufferSize, 1);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, WRITE_ATTR_REPLY);
WriteAttrReply* reply;
error = _SendRequest(port, &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;
*bytesWritten = reply->bytesWritten;
return error;
}
// ReadAttrStat
status_t
Volume::ReadAttrStat(void* _node, void* cookie, struct stat *st)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_ATTR_STAT))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadAttrStatRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->attrCookie = cookie;
// send the request
KernelRequestHandler handler(this, READ_ATTR_STAT_REPLY);
ReadAttrStatReply* reply;
error = _SendRequest(port, &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;
*st = reply->st;
return error;
}
// WriteAttrStat
status_t
Volume::WriteAttrStat(void* _node, void* cookie, const struct stat *st,
int statMask)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_WRITE_ATTR_STAT))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
WriteAttrStatRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->attrCookie = cookie;
request->st = *st;
request->mask = statMask;
// send the request
KernelRequestHandler handler(this, WRITE_ATTR_STAT_REPLY);
WriteAttrStatReply* reply;
error = _SendRequest(port, &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;
}
// RenameAttr
status_t
Volume::RenameAttr(void* _oldNode, const char* oldName, void* _newNode,
const char* newName)
{
VNode* oldVNode = (VNode*)_oldNode;
VNode* newVNode = (VNode*)_newNode;
// check capability
if (!HasVNodeCapability(oldVNode, FS_VNODE_CAPABILITY_RENAME_ATTR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
RenameAttrRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->oldNode = oldVNode->clientNode;
request->newNode = newVNode->clientNode;
error = allocator.AllocateString(request->oldName, oldName);
if (error == B_OK)
error = allocator.AllocateString(request->newName, newName);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, RENAME_ATTR_REPLY);
RenameAttrReply* reply;
error = _SendRequest(port, &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;
}
// RemoveAttr
status_t
Volume::RemoveAttr(void* _node, const char* name)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_REMOVE_ATTR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
RemoveAttrRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
error = allocator.AllocateString(request->name, name);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, REMOVE_ATTR_REPLY);
RemoveAttrReply* reply;
error = _SendRequest(port, &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;
}
// #pragma mark - indices
// OpenIndexDir
status_t
Volume::OpenIndexDir(void** cookie)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_OPEN_INDEX_DIR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
AutoIncrementer incrementer(&fOpenIndexDirectories);
// prepare the request
RequestAllocator allocator(port->GetPort());
OpenIndexDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
// send the request
KernelRequestHandler handler(this, OPEN_INDEX_DIR_REPLY);
OpenIndexDirReply* reply;
error = _SendRequest(port, &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;
incrementer.Keep();
*cookie = reply->indexDirCookie;
return error;
}
// CloseIndexDir
status_t
Volume::CloseIndexDir(void* cookie)
{
status_t error = _CloseIndexDir(cookie);
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. OBOS ignores it completely. The fsshell returns it to the
// userland, but considers the node closed anyway.
WARN(("Volume::CloseIndexDir(): connection lost, forcing close "
"index dir\n"));
return B_OK;
}
return error;
}
// FreeIndexDirCookie
status_t
Volume::FreeIndexDirCookie(void* cookie)
{
status_t error = _FreeIndexDirCookie(cookie);
bool disconnected = false;
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. It's completely ignored by OBOS as well as by the fsshell.
WARN(("Volume::FreeIndexDirCookie(): connection lost, forcing free "
"index dir cookie\n"));
error = B_OK;
disconnected = true;
}
int32 openIndexDirs = atomic_add(&fOpenIndexDirectories, -1);
if (openIndexDirs <= 1 && disconnected)
_PutAllPendingVNodes();
return error;
}
// ReadIndexDir
status_t
Volume::ReadIndexDir(void* cookie, void* buffer, size_t bufferSize,
uint32 count, uint32* countRead)
{
*countRead = 0;
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_READ_INDEX_DIR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadIndexDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->indexDirCookie = cookie;
request->bufferSize = bufferSize;
request->count = count;
// send the request
KernelRequestHandler handler(this, READ_INDEX_DIR_REPLY);
ReadIndexDirReply* reply;
error = _SendRequest(port, &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;
if (reply->count < 0 || reply->count > count)
return B_BAD_DATA;
if ((int32)bufferSize < reply->buffer.GetSize())
return B_BAD_DATA;
*countRead = reply->count;
if (*countRead > 0) {
// copy the buffer -- limit the number of bytes to copy
uint32 maxBytes = *countRead
* (sizeof(struct dirent) + B_FILE_NAME_LENGTH);
uint32 copyBytes = reply->buffer.GetSize();
if (copyBytes > maxBytes)
copyBytes = maxBytes;
memcpy(buffer, reply->buffer.GetData(), copyBytes);
}
_SendReceiptAck(port);
return error;
}
// RewindIndexDir
status_t
Volume::RewindIndexDir(void* cookie)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_REWIND_INDEX_DIR))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
RewindIndexDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->indexDirCookie = cookie;
// send the request
KernelRequestHandler handler(this, REWIND_INDEX_DIR_REPLY);
RewindIndexDirReply* reply;
error = _SendRequest(port, &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;
}
// CreateIndex
status_t
Volume::CreateIndex(const char* name, uint32 type, uint32 flags)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_CREATE_INDEX))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
CreateIndexRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
error = allocator.AllocateString(request->name, name);
request->type = type;
request->flags = flags;
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, CREATE_INDEX_REPLY);
CreateIndexReply* reply;
error = _SendRequest(port, &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;
}
// RemoveIndex
status_t
Volume::RemoveIndex(const char* name)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_REMOVE_INDEX))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
RemoveIndexRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
error = allocator.AllocateString(request->name, name);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, REMOVE_INDEX_REPLY);
RemoveIndexReply* reply;
error = _SendRequest(port, &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;
}
// ReadIndexStat
status_t
Volume::ReadIndexStat(const char* name, struct stat *st)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_READ_INDEX_STAT))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadIndexStatRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
error = allocator.AllocateString(request->name, name);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, READ_INDEX_STAT_REPLY);
ReadIndexStatReply* reply;
error = _SendRequest(port, &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;
*st = reply->st;
return error;
}
// #pragma mark - queries
// OpenQuery
status_t
Volume::OpenQuery(const char* queryString, uint32 flags, port_id targetPort,
uint32 token, void** cookie)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_OPEN_QUERY))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
AutoIncrementer incrementer(&fOpenQueries);
// prepare the request
RequestAllocator allocator(port->GetPort());
OpenQueryRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
error = allocator.AllocateString(request->queryString, queryString);
if (error != B_OK)
return error;
request->flags = flags;
request->port = targetPort;
request->token = token;
// send the request
KernelRequestHandler handler(this, OPEN_QUERY_REPLY);
OpenQueryReply* reply;
error = _SendRequest(port, &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;
incrementer.Keep();
*cookie = reply->queryCookie;
return error;
}
// CloseQuery
status_t
Volume::CloseQuery(void* cookie)
{
status_t error = _CloseQuery(cookie);
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. OBOS ignores it completely. The fsshell returns it to the
// userland, but considers the node closed anyway.
WARN(("Volume::CloseQuery(): connection lost, forcing close query\n"));
return B_OK;
}
return error;
}
// FreeQueryCookie
status_t
Volume::FreeQueryCookie(void* cookie)
{
status_t error = _FreeQueryCookie(cookie);
bool disconnected = false;
if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) {
// This isn't really necessary, as the return value is irrelevant to
// the VFS. It's completely ignored by OBOS as well as by the fsshell.
WARN(("Volume::FreeQueryCookie(): connection lost, forcing free "
"query cookie\n"));
error = B_OK;
disconnected = true;
}
int32 openQueries = atomic_add(&fOpenQueries, -1);
if (openQueries <= 1 && disconnected)
_PutAllPendingVNodes();
return error;
}
// ReadQuery
status_t
Volume::ReadQuery(void* cookie, void* buffer, size_t bufferSize,
uint32 count, uint32* countRead)
{
*countRead = 0;
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_READ_QUERY))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadQueryRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->queryCookie = cookie;
request->bufferSize = bufferSize;
request->count = count;
// send the request
KernelRequestHandler handler(this, READ_QUERY_REPLY);
ReadQueryReply* reply;
error = _SendRequest(port, &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;
if (reply->count < 0 || reply->count > count)
return B_BAD_DATA;
if ((int32)bufferSize < reply->buffer.GetSize())
return B_BAD_DATA;
*countRead = reply->count;
if (*countRead > 0) {
// copy the buffer -- limit the number of bytes to copy
uint32 maxBytes = *countRead
* (sizeof(struct dirent) + B_FILE_NAME_LENGTH);
uint32 copyBytes = reply->buffer.GetSize();
if (copyBytes > maxBytes)
copyBytes = maxBytes;
memcpy(buffer, reply->buffer.GetData(), copyBytes);
}
_SendReceiptAck(port);
return error;
}
// RewindQuery
status_t
Volume::RewindQuery(void* cookie)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_REWIND_QUERY))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
RewindQueryRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->queryCookie = cookie;
// send the request
KernelRequestHandler handler(this, REWIND_QUERY_REPLY);
RewindQueryReply* reply;
error = _SendRequest(port, &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;
}
// #pragma mark -
// #pragma mark ----- private implementations -----
// _InitVolumeOps
void
Volume::_InitVolumeOps()
{
memcpy(&fVolumeOps, &gUserlandFSVolumeOps, sizeof(fs_volume_ops));
#undef CLEAR_UNSUPPORTED
#define CLEAR_UNSUPPORTED(capability, op) \
if (!fCapabilities.Get(capability)) \
fVolumeOps.op = NULL
// FS operations
// FS_VOLUME_CAPABILITY_UNMOUNT: unmount
// always needed
// FS_VOLUME_CAPABILITY_READ_FS_INFO: read_fs_info
// always needed
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_WRITE_FS_INFO, write_fs_info);
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_SYNC, sync);
// vnode operations
// FS_VOLUME_CAPABILITY_GET_VNODE: get_vnode
// always needed
// index directory & index operations
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_OPEN_INDEX_DIR, open_index_dir);
// FS_VOLUME_CAPABILITY_CLOSE_INDEX_DIR: close_index_dir
// always needed
// FS_VOLUME_CAPABILITY_FREE_INDEX_DIR_COOKIE: free_index_dir_cookie
// always needed
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_READ_INDEX_DIR, read_index_dir);
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_REWIND_INDEX_DIR, rewind_index_dir);
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_CREATE_INDEX, create_index);
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_REMOVE_INDEX, remove_index);
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_READ_INDEX_STAT, read_index_stat);
// query operations
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_OPEN_QUERY, open_query);
// FS_VOLUME_CAPABILITY_CLOSE_QUERY: close_query
// always needed
// FS_VOLUME_CAPABILITY_FREE_QUERY_COOKIE: free_query_cookie
// always needed
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_READ_QUERY, read_query);
CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_REWIND_QUERY, rewind_query);
#undef CLEAR_UNSUPPORTED
}
// #pragma mark -
// _Mount
status_t
Volume::_Mount(const char* device, uint32 flags, const char* parameters)
{
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// get the current working directory
char cwd[B_PATH_NAME_LENGTH];
if (!getcwd(cwd, sizeof(cwd)))
return errno;
// prepare the request
RequestAllocator allocator(port->GetPort());
MountVolumeRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->nsid = GetID();
error = allocator.AllocateString(request->cwd, cwd);
if (error == B_OK)
error = allocator.AllocateString(request->device, device);
request->flags = flags;
if (error == B_OK)
error = allocator.AllocateString(request->parameters, parameters);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, MOUNT_VOLUME_REPLY);
MountVolumeReply* reply;
error = _SendRequest(port, &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;
fRootID = reply->rootID;
fUserlandVolume = reply->volume;
fCapabilities = reply->capabilities;
return error;
}
// _Unmount
status_t
Volume::_Unmount()
{
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
UnmountVolumeRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
// send the request
KernelRequestHandler handler(this, UNMOUNT_VOLUME_REPLY);
UnmountVolumeReply* reply;
error = _SendRequest(port, &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;
}
// _ReadFSInfo
status_t
Volume::_ReadFSInfo(fs_info* info)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_READ_FS_INFO))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadFSInfoRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
// send the request
KernelRequestHandler handler(this, READ_FS_INFO_REPLY);
ReadFSInfoReply* reply;
error = _SendRequest(port, &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;
*info = reply->info;
return error;
}
// _Lookup
status_t
Volume::_Lookup(void* _dir, const char* entryName, ino_t* vnid)
{
VNode* vnode = (VNode*)_dir;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
LookupRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
error = allocator.AllocateString(request->entryName, entryName);
if (error != B_OK)
return error;
// send the request
KernelRequestHandler handler(this, LOOKUP_REPLY);
LookupReply* reply;
error = _SendRequest(port, &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;
*vnid = reply->vnid;
// The VFS will balance the get_vnode() call for the FS.
_DecrementVNodeCount(*vnid);
return error;
}
// _WriteVNode
status_t
Volume::_WriteVNode(void* _node, bool reenter)
{
VNode* vnode = (VNode*)_node;
// At any rate remove the vnode from our map and delete it. We don't do that
// right now, though, since we might still need to serve file cache requests
// from the client FS.
VNodeRemover nodeRemover(this, vnode);
void* clientNode = vnode->clientNode;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
WriteVNodeRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = clientNode;
request->reenter = reenter;
// send the request
KernelRequestHandler handler(this, WRITE_VNODE_REPLY);
WriteVNodeReply* reply;
error = _SendRequest(port, &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;
}
// _ReadStat
status_t
Volume::_ReadStat(void* _node, struct stat* st)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_STAT))
return B_BAD_VALUE;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
ReadStatRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
// send the request
KernelRequestHandler handler(this, READ_STAT_REPLY);
ReadStatReply* reply;
error = _SendRequest(port, &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;
*st = reply->st;
return error;
}
// _Close
status_t
Volume::_Close(void* _node, void* cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CLOSE))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
CloseRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->fileCookie = cookie;
// send the request
KernelRequestHandler handler(this, CLOSE_REPLY);
CloseReply* reply;
error = _SendRequest(port, &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;
}
// _FreeCookie
status_t
Volume::_FreeCookie(void* _node, void* cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_FREE_COOKIE))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
FreeCookieRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->fileCookie = cookie;
// send the request
KernelRequestHandler handler(this, FREE_COOKIE_REPLY);
FreeCookieReply* reply;
error = _SendRequest(port, &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;
}
// _CloseDir
status_t
Volume::_CloseDir(void* _node, void* cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CLOSE_DIR))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
CloseDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->dirCookie = cookie;
// send the request
KernelRequestHandler handler(this, CLOSE_DIR_REPLY);
CloseDirReply* reply;
error = _SendRequest(port, &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;
}
// _FreeDirCookie
status_t
Volume::_FreeDirCookie(void* _node, void* cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_FREE_DIR_COOKIE))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
FreeDirCookieRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->dirCookie = cookie;
// send the request
KernelRequestHandler handler(this, FREE_DIR_COOKIE_REPLY);
FreeDirCookieReply* reply;
error = _SendRequest(port, &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;
}
// _CloseAttrDir
status_t
Volume::_CloseAttrDir(void* _node, void* cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
CloseAttrDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->attrDirCookie = cookie;
// send the request
KernelRequestHandler handler(this, CLOSE_ATTR_DIR_REPLY);
CloseAttrDirReply* reply;
error = _SendRequest(port, &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;
}
// _FreeAttrDirCookie
status_t
Volume::_FreeAttrDirCookie(void* _node, void* cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
FreeAttrDirCookieRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->attrDirCookie = cookie;
// send the request
KernelRequestHandler handler(this, FREE_ATTR_DIR_COOKIE_REPLY);
FreeAttrDirCookieReply* reply;
error = _SendRequest(port, &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;
}
// _CloseAttr
status_t
Volume::_CloseAttr(void* _node, void* cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CLOSE_ATTR))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
CloseAttrRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->attrCookie = cookie;
// send the request
KernelRequestHandler handler(this, CLOSE_ATTR_REPLY);
CloseAttrReply* reply;
error = _SendRequest(port, &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;
}
// _FreeAttrCookie
status_t
Volume::_FreeAttrCookie(void* _node, void* cookie)
{
VNode* vnode = (VNode*)_node;
// check capability
if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
FreeAttrCookieRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->node = vnode->clientNode;
request->attrCookie = cookie;
// send the request
KernelRequestHandler handler(this, FREE_ATTR_COOKIE_REPLY);
FreeAttrCookieReply* reply;
error = _SendRequest(port, &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;
}
// _CloseIndexDir
status_t
Volume::_CloseIndexDir(void* cookie)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_CLOSE_INDEX_DIR))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
CloseIndexDirRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->indexDirCookie = cookie;
// send the request
KernelRequestHandler handler(this, CLOSE_INDEX_DIR_REPLY);
CloseIndexDirReply* reply;
error = _SendRequest(port, &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;
}
// _FreeIndexDirCookie
status_t
Volume::_FreeIndexDirCookie(void* cookie)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_FREE_INDEX_DIR_COOKIE))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
FreeIndexDirCookieRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->indexDirCookie = cookie;
// send the request
KernelRequestHandler handler(this, FREE_INDEX_DIR_COOKIE_REPLY);
FreeIndexDirCookieReply* reply;
error = _SendRequest(port, &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;
}
// _CloseQuery
status_t
Volume::_CloseQuery(void* cookie)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_CLOSE_QUERY))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
CloseQueryRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->queryCookie = cookie;
// send the request
KernelRequestHandler handler(this, CLOSE_QUERY_REPLY);
CloseQueryReply* reply;
error = _SendRequest(port, &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;
}
// _FreeQueryCookie
status_t
Volume::_FreeQueryCookie(void* cookie)
{
// check capability
if (!HasCapability(FS_VOLUME_CAPABILITY_FREE_QUERY_COOKIE))
return B_OK;
// get a free port
RequestPort* port = fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
FreeQueryCookieRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = fUserlandVolume;
request->queryCookie = cookie;
// send the request
KernelRequestHandler handler(this, FREE_QUERY_COOKIE_REPLY);
FreeQueryCookieReply* reply;
error = _SendRequest(port, &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;
}
// _SendRequest
status_t
Volume::_SendRequest(RequestPort* port, RequestAllocator* allocator,
RequestHandler* handler, Request** reply)
{
// fill in the caller info
KernelRequest* request = static_cast<KernelRequest*>(
allocator->GetRequest());
Thread* thread = thread_get_current_thread();
request->team = thread->team->id;
request->thread = thread->id;
request->user = geteuid();
request->group = getegid();
if (!fFileSystem->IsUserlandServerThread())
return port->SendRequest(allocator, handler, reply);
// Here it gets dangerous: a thread of the userland server team being here
// calls for trouble. We try receiving the request with a timeout, and
// close the port -- which will disconnect the whole FS.
status_t error = port->SendRequest(allocator, handler, reply,
kUserlandServerlandPortTimeout);
if (error == B_TIMED_OUT || error == B_WOULD_BLOCK)
port->Close();
return error;
}
// _SendReceiptAck
status_t
Volume::_SendReceiptAck(RequestPort* port)
{
RequestAllocator allocator(port->GetPort());
ReceiptAckReply* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
return port->SendRequest(&allocator);
}
// _IncrementVNodeCount
void
Volume::_IncrementVNodeCount(ino_t vnid)
{
MutexLocker _(fLock);
if (!fVNodeCountingEnabled)
return;
VNode* vnode = fVNodes->Lookup(vnid);
if (vnode == NULL) {
ERROR(("Volume::_IncrementVNodeCount(): Node with ID %" B_PRId64
" not known!\n", vnid));
return;
}
vnode->useCount++;
//PRINT(("_IncrementVNodeCount(%Ld): count: %ld, fVNodeCountMap size: %ld\n", vnid, *count, fVNodeCountMap->Size()));
}
// _DecrementVNodeCount
void
Volume::_DecrementVNodeCount(ino_t vnid)
{
MutexLocker _(fLock);
if (!fVNodeCountingEnabled)
return;
VNode* vnode = fVNodes->Lookup(vnid);
if (vnode == NULL) {
ERROR(("Volume::_DecrementVNodeCount(): Node with ID %" B_PRId64 " not "
"known!\n", vnid));
return;
}
vnode->useCount--;
//PRINT(("_DecrementVNodeCount(%Ld): count: %ld, fVNodeCountMap size: %ld\n", vnid, tmpCount, fVNodeCountMap->Size()));
}
// _RemoveInvalidVNode
void
Volume::_RemoveInvalidVNode(ino_t vnid)
{
MutexLocker locker(fLock);
VNode* vnode = fVNodes->Lookup(vnid);
if (vnode == NULL) {
ERROR(("Volume::_RemoveInvalidVNode(): Node with ID %" B_PRId64
" not known!\n", vnid));
return;
}
fVNodes->Remove(vnode);
locker.Unlock();
// release all references acquired so far
if (fVNodeCountingEnabled) {
for (; vnode->useCount > 0; vnode->useCount--)
put_vnode(fFSVolume, vnid);
}
vnode->Delete(this);
}
// _InternalIOCtl
status_t
Volume::_InternalIOCtl(userlandfs_ioctl* buffer, int32 bufferSize)
{
if (buffer->version != USERLAND_IOCTL_CURRENT_VERSION)
return B_BAD_VALUE;
status_t result = B_OK;
switch (buffer->command) {
case USERLAND_IOCTL_PUT_ALL_PENDING_VNODES:
result = _PutAllPendingVNodes();
break;
default:
return B_BAD_VALUE;
}
buffer->error = result;
return B_OK;
}
// _PutAllPendingVNodes
status_t
Volume::_PutAllPendingVNodes()
{
PRINT(("Volume::_PutAllPendingVNodes()\n"));
if (!fFileSystem->GetPortPool()->IsDisconnected()) {
PRINT(("Volume::_PutAllPendingVNodes() failed: still connected\n"));
return USERLAND_IOCTL_STILL_CONNECTED;
}
MutexLocker locker(fLock);
if (!fVNodeCountingEnabled) {
PRINT(("Volume::_PutAllPendingVNodes() failed: vnode counting "
"disabled\n"));
return USERLAND_IOCTL_VNODE_COUNTING_DISABLED;
}
// Check whether there are open entities at the moment.
if (atomic_get(&fOpenFiles) > 0) {
PRINT(("Volume::_PutAllPendingVNodes() failed: open files\n"));
return USERLAND_IOCTL_OPEN_FILES;
}
if (atomic_get(&fOpenDirectories) > 0) {
PRINT(("Volume::_PutAllPendingVNodes() failed: open dirs\n"));
return USERLAND_IOCTL_OPEN_DIRECTORIES;
}
if (atomic_get(&fOpenAttributeDirectories) > 0) {
PRINT(("Volume::_PutAllPendingVNodes() failed: open attr dirs\n"));
return USERLAND_IOCTL_OPEN_ATTRIBUTE_DIRECTORIES;
}
if (atomic_get(&fOpenAttributes) > 0) {
PRINT(("Volume::_PutAllPendingVNodes() failed: open attributes\n"));
return USERLAND_IOCTL_OPEN_ATTRIBUTES;
}
if (atomic_get(&fOpenIndexDirectories) > 0) {
PRINT(("Volume::_PutAllPendingVNodes() failed: open index dirs\n"));
return USERLAND_IOCTL_OPEN_INDEX_DIRECTORIES;
}
if (atomic_get(&fOpenQueries) > 0) {
PRINT(("Volume::_PutAllPendingVNodes() failed: open queries\n"));
return USERLAND_IOCTL_OPEN_QUERIES;
}
// No open entities. Since the port pool is disconnected, no new
// entities can be opened. Disable node counting and put all pending
// vnodes.
fVNodeCountingEnabled = false;
int32 putVNodeCount = 0;
// Since the vnode map can still change, we need to iterate to the first
// node we need to put, drop the lock, put the node, and restart from the
// beginning.
// TODO: Optimize by extracting batches of relevant nodes to an on-stack
// array.
bool nodeFound;
do {
nodeFound = false;
// get the next node to put
for (VNodeMap::Iterator it = fVNodes->GetIterator();
VNode* vnode = it.Next();) {
if (vnode->useCount > 0) {
ino_t vnid = vnode->id;
int32 count = vnode->useCount;
vnode->useCount = 0;
fs_vnode_ops* ops = vnode->ops->ops;
bool published = vnode->published;
locker.Unlock();
// If the node has not yet been published, we have to do that
// before putting otherwise the VFS will complain that the node
// is busy when the last reference is gone.
if (!published)
publish_vnode(fFSVolume, vnid, vnode, ops, S_IFDIR, 0);
for (int32 i = 0; i < count; i++) {
PutVNode(vnid);
putVNodeCount++;
}
locker.Lock();
nodeFound = true;
break;
}
}
} while (nodeFound);
PRINT(("Volume::_PutAllPendingVNodes() successful: Put %" B_PRId32
" vnodes\n", putVNodeCount));
return B_OK;
}
// _RegisterIORequest
status_t
Volume::_RegisterIORequest(io_request* request, int32* requestID)
{
MutexLocker _(fLock);
// get the next free ID
while (fIORequestInfosByID->Lookup(++fLastIORequestID) != NULL) {
}
// allocate the info
IORequestInfo* info = new(std::nothrow) IORequestInfo(request,
++fLastIORequestID);
if (info == NULL)
return B_NO_MEMORY;
// add the info to the maps
fIORequestInfosByID->Insert(info);
fIORequestInfosByStruct->Insert(info);
*requestID = info->id;
return B_OK;
}
// _UnregisterIORequest
status_t
Volume::_UnregisterIORequest(int32 requestID)
{
MutexLocker _(fLock);
if (IORequestInfo* info = fIORequestInfosByID->Lookup(requestID)) {
fIORequestInfosByID->Remove(info);
fIORequestInfosByStruct->Remove(info);
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
// _FindIORequest
status_t
Volume::_FindIORequest(int32 requestID, io_request** request)
{
MutexLocker _(fLock);
if (IORequestInfo* info = fIORequestInfosByID->Lookup(requestID)) {
*request = info->request;
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
// _FindIORequest
status_t
Volume::_FindIORequest(io_request* request, int32* requestID)
{
MutexLocker _(fLock);
if (IORequestInfo* info = fIORequestInfosByStruct->Lookup(request)) {
*requestID = info->id;
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
/*static*/ status_t
Volume::_IterativeFDIOGetVecs(void* _cookie, io_request* ioRequest,
off_t offset, size_t size, struct file_io_vec* vecs, size_t* _count)
{
IterativeFDIOCookie* cookie = (IterativeFDIOCookie*)_cookie;
Volume* volume = cookie->volume;
MutexLocker locker(volume->fLock);
// If there are vecs cached in the cookie and the offset matches, return
// those.
if (cookie->vecs != NULL) {
size_t vecCount = 0;
if (offset == cookie->offset) {
// good, copy the vecs
while (size > 0 && vecCount < cookie->vecCount
&& vecCount < *_count) {
off_t maxSize = std::min((off_t)size,
cookie->vecs[vecCount].length);
vecs[vecCount].offset = cookie->vecs[vecCount].offset;
vecs[vecCount].length = maxSize;
size -= maxSize;
vecCount++;
}
}
cookie->vecs = NULL;
cookie->vecCount = 0;
// got some vecs? -- then we're done
if (vecCount > 0) {
*_count = vecCount;
return B_OK;
}
}
// we have to ask the client FS
int32 requestID = cookie->requestID;
void* clientCookie = cookie->clientCookie;
locker.Unlock();
// get a free port
RequestPort* port = volume->fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _(volume->fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
IterativeIOGetVecsRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = volume->fUserlandVolume;
request->cookie = clientCookie;
request->offset = offset;
request->request = requestID;
request->size = size;
size_t maxVecs = std::min(*_count,
(size_t)IterativeIOGetVecsReply::MAX_VECS);
request->vecCount = maxVecs;
// send the request
KernelRequestHandler handler(volume, ITERATIVE_IO_GET_VECS_REPLY);
IterativeIOGetVecsReply* reply;
error = volume->_SendRequest(port, &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;
uint32 vecCount = reply->vecCount;
if (vecCount < 0 || vecCount > maxVecs)
return B_BAD_DATA;
memcpy(vecs, reply->vecs, vecCount * sizeof(file_io_vec));
*_count = vecCount;
return B_OK;
}
/*static*/ status_t
Volume::_IterativeFDIOFinished(void* _cookie, io_request* ioRequest,
status_t status, bool partialTransfer, size_t bytesTransferred)
{
IterativeFDIOCookie* cookie = (IterativeFDIOCookie*)_cookie;
Volume* volume = cookie->volume;
// At any rate, we're done with the cookie after this call -- it will not
// be used anymore.
BReference<IterativeFDIOCookie> _(cookie, true);
// We also want to dispose of the request.
IORequestRemover _2(volume, cookie->requestID);
// get a free port
RequestPort* port = volume->fFileSystem->GetPortPool()->AcquirePort();
if (!port)
return B_ERROR;
PortReleaser _3(volume->fFileSystem->GetPortPool(), port);
// prepare the request
RequestAllocator allocator(port->GetPort());
IterativeIOFinishedRequest* request;
status_t error = AllocateRequest(allocator, &request);
if (error != B_OK)
return error;
request->volume = volume->fUserlandVolume;
request->cookie = cookie->clientCookie;
request->request = cookie->requestID;
request->status = status;
request->partialTransfer = partialTransfer;
request->bytesTransferred = bytesTransferred;
// send the request
KernelRequestHandler handler(volume, ITERATIVE_IO_FINISHED_REPLY);
IterativeIOFinishedReply* reply;
error = volume->_SendRequest(port, &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 B_OK;
}
↑ V547 Expression 'error == ((int) 0)' is always true.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fVolumeOps.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: hash_link.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: idLink, structLink.