Files
haiku-beta6/src/add-ons/kernel/file_systems/userlandfs/server/BeOSKernelVolume.cpp
T

879 lines
19 KiB
C++
Raw Normal View History

2007-02-26 16:26:02 +00:00
// BeOSKernelVolume.cpp
2007-02-26 16:26:02 +00:00
#include "BeOSKernelVolume.h"
#include <new>
#include <fcntl.h>
#include <unistd.h>
#include "beos_fs_interface.h"
#include "Debug.h"
#include "kernel_emu.h"
using std::nothrow;
static int open_mode_to_access(int openMode);
// AttributeCookie
class BeOSKernelVolume::AttributeCookie {
public:
AttributeCookie(const char* name, uint32 type, int openMode, bool exists,
bool create)
: fType(type),
fOpenMode(openMode),
fExists(exists),
fCreate(create)
{
strcpy(fName, name);
}
char fName[B_ATTR_NAME_LENGTH];
uint32 fType;
int fOpenMode;
bool fExists;
bool fCreate;
};
// constructor
BeOSKernelVolume::BeOSKernelVolume(FileSystem* fileSystem, dev_t id,
beos_vnode_ops* fsOps)
2007-02-26 16:26:02 +00:00
: Volume(fileSystem, id),
fFSOps(fsOps),
fVolumeCookie(NULL)
{
}
// destructor
2007-02-26 16:26:02 +00:00
BeOSKernelVolume::~BeOSKernelVolume()
{
}
// #pragma mark -
// #pragma mark ----- FS -----
// Mount
status_t
BeOSKernelVolume::Mount(const char* device, uint32 flags,
const char* parameters, ino_t* rootID)
{
if (!fFSOps->mount)
return B_BAD_VALUE;
size_t len = (parameters ? strlen(parameters) : 0);
return fFSOps->mount(GetID(), device, flags, (void*)parameters, len,
&fVolumeCookie, rootID);
}
// Unmount
status_t
2007-02-26 16:26:02 +00:00
BeOSKernelVolume::Unmount()
{
if (!fFSOps->unmount)
return B_BAD_VALUE;
return fFSOps->unmount(fVolumeCookie);
}
// Sync
status_t
2007-02-26 16:26:02 +00:00
BeOSKernelVolume::Sync()
{
if (!fFSOps->sync)
return B_BAD_VALUE;
return fFSOps->sync(fVolumeCookie);
}
// ReadFSInfo
status_t
BeOSKernelVolume::ReadFSInfo(fs_info* info)
{
if (!fFSOps->rfsstat)
return B_BAD_VALUE;
// Haiku's fs_info equals BeOS's version
return fFSOps->rfsstat(fVolumeCookie, (beos_fs_info*)info);
}
// WriteFSInfo
status_t
BeOSKernelVolume::WriteFSInfo(const struct fs_info* info, uint32 mask)
{
if (!fFSOps->wfsstat)
return B_BAD_VALUE;
// Haiku's fs_info equals BeOS's version
return fFSOps->wfsstat(fVolumeCookie, (beos_fs_info*)info, (long)mask);
}
// #pragma mark - vnodes
// Lookup
status_t
BeOSKernelVolume::Lookup(fs_vnode dir, const char* entryName, ino_t* vnid,
int* type)
{
if (!fFSOps->walk)
return B_BAD_VALUE;
status_t error = fFSOps->walk(fVolumeCookie, dir, entryName, NULL, vnid);
if (error != B_OK)
return error;
// We need to get the node stat to return the node's type.
// TODO: This is quite expensive. get_vnode() and put_vnode() cause trips to
// the kernel. If it is implemented properly walk() has already called
// get_vnode() for our node. Introducing a mechanism that would allow us to
// temporarily track the {get,put,new}_vnode() calls of our thread would save
// those two kernel trips.
// get the vnode
fs_vnode node;
error = UserlandFS::KernelEmu::get_vnode(GetID(), *vnid, &node);
if (error != B_OK) {
// walk() has called get_vnode() for the caller, so we need to put the
// node
UserlandFS::KernelEmu::put_vnode(GetID(), *vnid);
return error;
}
// get the node's stat
struct stat st;
error = ReadStat(node, &st);
if (error == B_OK)
*type = (st.st_mode & S_IFMT);
// put the node for our get_vnode()
UserlandFS::KernelEmu::put_vnode(GetID(), *vnid);
// on error put the node for walk()'s get_vnode()
if (error != B_OK)
UserlandFS::KernelEmu::put_vnode(GetID(), *vnid);
return error;
}
// LookupNoType
status_t
BeOSKernelVolume::LookupNoType(fs_vnode dir, const char* entryName,
ino_t* vnid)
{
if (!fFSOps->walk)
return B_BAD_VALUE;
return fFSOps->walk(fVolumeCookie, dir, entryName, NULL, vnid);
}
// ReadVNode
status_t
BeOSKernelVolume::ReadVNode(ino_t vnid, bool reenter, fs_vnode* node)
{
if (!fFSOps->read_vnode)
return B_BAD_VALUE;
return fFSOps->read_vnode(fVolumeCookie, vnid, (char)reenter, node);
}
// WriteVNode
status_t
BeOSKernelVolume::WriteVNode(fs_vnode node, bool reenter)
{
if (!fFSOps->write_vnode)
return B_BAD_VALUE;
return fFSOps->write_vnode(fVolumeCookie, node, (char)reenter);
}
// RemoveVNode
status_t
BeOSKernelVolume::RemoveVNode(fs_vnode node, bool reenter)
{
if (!fFSOps->remove_vnode)
return B_BAD_VALUE;
return fFSOps->remove_vnode(fVolumeCookie, node, (char)reenter);
}
// #pragma mark - nodes
// IOCtl
status_t
BeOSKernelVolume::IOCtl(fs_vnode node, fs_cookie cookie, uint32 command,
void* buffer, size_t size)
{
if (!fFSOps->ioctl)
return B_BAD_VALUE;
return fFSOps->ioctl(fVolumeCookie, node, cookie, (int)command, buffer,
size);
}
// SetFlags
status_t
BeOSKernelVolume::SetFlags(fs_vnode node, fs_cookie cookie, int flags)
{
if (!fFSOps->setflags)
return B_BAD_VALUE;
return fFSOps->setflags(fVolumeCookie, node, cookie, flags);
}
// Select
status_t
BeOSKernelVolume::Select(fs_vnode node, fs_cookie cookie, uint8 event,
uint32 ref, selectsync* sync)
{
if (!fFSOps->select) {
UserlandFS::KernelEmu::notify_select_event(sync, event, false);
return B_OK;
}
return fFSOps->select(fVolumeCookie, node, cookie, event, ref, sync);
}
// Deselect
status_t
BeOSKernelVolume::Deselect(fs_vnode node, fs_cookie cookie, uint8 event,
selectsync* sync)
{
if (!fFSOps->select || !fFSOps->deselect)
return B_OK;
return fFSOps->deselect(fVolumeCookie, node, cookie, event, sync);
}
// FSync
status_t
BeOSKernelVolume::FSync(fs_vnode node)
{
if (!fFSOps->fsync)
return B_BAD_VALUE;
return fFSOps->fsync(fVolumeCookie, node);
}
// ReadSymlink
status_t
BeOSKernelVolume::ReadSymlink(fs_vnode node, char* buffer, size_t bufferSize,
size_t* bytesRead)
{
if (!fFSOps->readlink)
return B_BAD_VALUE;
*bytesRead = bufferSize;
return fFSOps->readlink(fVolumeCookie, node, buffer, bytesRead);
}
// CreateSymlink
status_t
BeOSKernelVolume::CreateSymlink(fs_vnode dir, const char* name,
const char* target, int mode)
{
if (!fFSOps->symlink)
return B_BAD_VALUE;
// TODO: Don't ignore mode?
return fFSOps->symlink(fVolumeCookie, dir, name, target);
}
// Link
status_t
BeOSKernelVolume::Link(fs_vnode dir, const char* name, fs_vnode node)
{
if (!fFSOps->link)
return B_BAD_VALUE;
return fFSOps->link(fVolumeCookie, dir, name, node);
}
// Unlink
status_t
BeOSKernelVolume::Unlink(fs_vnode dir, const char* name)
{
if (!fFSOps->unlink)
return B_BAD_VALUE;
return fFSOps->unlink(fVolumeCookie, dir, name);
}
// Rename
status_t
BeOSKernelVolume::Rename(fs_vnode oldDir, const char* oldName, fs_vnode newDir,
const char* newName)
{
if (!fFSOps->rename)
return B_BAD_VALUE;
return fFSOps->rename(fVolumeCookie, oldDir, oldName, newDir, newName);
}
// Access
status_t
BeOSKernelVolume::Access(fs_vnode node, int mode)
{
if (!fFSOps->access)
return B_OK;
return fFSOps->access(fVolumeCookie, node, mode);
}
// ReadStat
status_t
BeOSKernelVolume::ReadStat(fs_vnode node, struct stat* st)
{
if (!fFSOps->rstat)
return B_BAD_VALUE;
// Haiku's struct stat has an additional st_type field (for an attribute
// type), but that doesn't matter here
return fFSOps->rstat(fVolumeCookie, node, (struct beos_stat*)st);
}
// WriteStat
status_t
BeOSKernelVolume::WriteStat(fs_vnode node, const struct stat *st, uint32 mask)
{
if (!fFSOps->wstat)
return B_BAD_VALUE;
// Haiku's struct stat has an additional st_type field (for an attribute
// type), but that doesn't matter here
return fFSOps->wstat(fVolumeCookie, node, (struct beos_stat*)st,
(long)mask);
}
// #pragma mark - files
// Create
status_t
BeOSKernelVolume::Create(fs_vnode dir, const char* name, int openMode, int mode,
fs_cookie* cookie, ino_t* vnid)
{
if (!fFSOps->create)
return B_BAD_VALUE;
return fFSOps->create(fVolumeCookie, dir, name, openMode, mode, vnid,
cookie);
}
// Open
status_t
BeOSKernelVolume::Open(fs_vnode node, int openMode, fs_cookie* cookie)
{
if (!fFSOps->open)
return B_BAD_VALUE;
return fFSOps->open(fVolumeCookie, node, openMode, cookie);
}
// Close
status_t
BeOSKernelVolume::Close(fs_vnode node, fs_cookie cookie)
{
if (!fFSOps->close)
return B_OK;
return fFSOps->close(fVolumeCookie, node, cookie);
}
// FreeCookie
status_t
BeOSKernelVolume::FreeCookie(fs_vnode node, fs_cookie cookie)
{
if (!fFSOps->free_cookie)
return B_OK;
return fFSOps->free_cookie(fVolumeCookie, node, cookie);
}
// Read
status_t
BeOSKernelVolume::Read(fs_vnode node, fs_cookie cookie, off_t pos, void* buffer,
size_t bufferSize, size_t* bytesRead)
{
if (!fFSOps->read)
return B_BAD_VALUE;
*bytesRead = bufferSize;
return fFSOps->read(fVolumeCookie, node, cookie, pos, buffer, bytesRead);
}
// Write
status_t
BeOSKernelVolume::Write(fs_vnode node, fs_cookie cookie, off_t pos,
const void* buffer, size_t bufferSize, size_t* bytesWritten)
{
if (!fFSOps->write)
return B_BAD_VALUE;
*bytesWritten = bufferSize;
return fFSOps->write(fVolumeCookie, node, cookie, pos, buffer,
bytesWritten);
}
// #pragma mark - directories
// CreateDir
status_t
BeOSKernelVolume::CreateDir(fs_vnode dir, const char* name, int mode,
ino_t *newDir)
{
if (!fFSOps->mkdir || !fFSOps->walk) // we need walk() too
return B_BAD_VALUE;
status_t error = fFSOps->mkdir(fVolumeCookie, dir, name, mode);
if (error != B_OK)
return error;
// we need to get the node ID by invoking walk()
ino_t id;
error = fFSOps->walk(fVolumeCookie, dir, name, NULL, &id);
if (error != B_OK)
return error;
// put the node for walk()'s get_vnode()
UserlandFS::KernelEmu::put_vnode(GetID(), id);
*newDir = id;
return B_OK;
}
// RemoveDir
status_t
BeOSKernelVolume::RemoveDir(fs_vnode dir, const char* name)
{
if (!fFSOps->rmdir)
return B_BAD_VALUE;
return fFSOps->rmdir(fVolumeCookie, dir, name);
}
// OpenDir
status_t
BeOSKernelVolume::OpenDir(fs_vnode node, fs_cookie* cookie)
{
if (!fFSOps->opendir)
return B_BAD_VALUE;
return fFSOps->opendir(fVolumeCookie, node, cookie);
}
// CloseDir
status_t
BeOSKernelVolume::CloseDir(fs_vnode node, fs_vnode cookie)
{
if (!fFSOps->closedir)
return B_OK;
return fFSOps->closedir(fVolumeCookie, node, cookie);
}
// FreeDirCookie
status_t
BeOSKernelVolume::FreeDirCookie(fs_vnode node, fs_vnode cookie)
{
if (!fFSOps->free_dircookie)
return B_OK;
return fFSOps->free_dircookie(fVolumeCookie, node, cookie);
}
// ReadDir
status_t
BeOSKernelVolume::ReadDir(fs_vnode node, fs_vnode cookie, void* buffer,
size_t bufferSize, uint32 count, uint32* countRead)
{
if (!fFSOps->readdir)
return B_BAD_VALUE;
*countRead = count;
// Haiku's struct dirent equals BeOS's version
return fFSOps->readdir(fVolumeCookie, node, cookie, (long*)countRead,
(beos_dirent*)buffer, bufferSize);
}
// RewindDir
status_t
BeOSKernelVolume::RewindDir(fs_vnode node, fs_vnode cookie)
{
if (!fFSOps->rewinddir)
return B_BAD_VALUE;
return fFSOps->rewinddir(fVolumeCookie, node, cookie);
}
// #pragma mark - attribute directories
// OpenAttrDir
status_t
BeOSKernelVolume::OpenAttrDir(fs_vnode node, fs_cookie *cookie)
{
if (!fFSOps->open_attrdir)
return B_BAD_VALUE;
return fFSOps->open_attrdir(fVolumeCookie, node, cookie);
}
// CloseAttrDir
status_t
BeOSKernelVolume::CloseAttrDir(fs_vnode node, fs_cookie cookie)
{
if (!fFSOps->close_attrdir)
return B_OK;
return fFSOps->close_attrdir(fVolumeCookie, node, cookie);
}
// FreeAttrDirCookie
status_t
BeOSKernelVolume::FreeAttrDirCookie(fs_vnode node, fs_cookie cookie)
{
if (!fFSOps->free_attrdircookie)
return B_OK;
return fFSOps->free_attrdircookie(fVolumeCookie, node, cookie);
}
// ReadAttrDir
status_t
BeOSKernelVolume::ReadAttrDir(fs_vnode node, fs_cookie cookie, void* buffer,
size_t bufferSize, uint32 count, uint32* countRead)
{
if (!fFSOps->read_attrdir)
return B_BAD_VALUE;
*countRead = count;
// Haiku's struct dirent equals BeOS's version
return fFSOps->read_attrdir(fVolumeCookie, node, cookie, (long*)countRead,
(struct beos_dirent*)buffer, bufferSize);
}
// RewindAttrDir
status_t
BeOSKernelVolume::RewindAttrDir(fs_vnode node, fs_cookie cookie)
{
if (!fFSOps->rewind_attrdir)
return B_BAD_VALUE;
return fFSOps->rewind_attrdir(fVolumeCookie, node, cookie);
}
// #pragma mark - attributes
// CreateAttr
status_t
BeOSKernelVolume::CreateAttr(fs_vnode node, const char* name, uint32 type,
int openMode, fs_cookie* cookie)
{
return _OpenAttr(node, name, type, openMode, true, cookie);
}
// OpenAttr
status_t
BeOSKernelVolume::OpenAttr(fs_vnode node, const char* name, int openMode,
fs_cookie* cookie)
{
return _OpenAttr(node, name, 0, openMode, false, cookie);
}
// CloseAttr
status_t
BeOSKernelVolume::CloseAttr(fs_vnode node, fs_cookie cookie)
{
return B_OK;
}
// FreeAttrCookie
status_t
BeOSKernelVolume::FreeAttrCookie(fs_vnode node, fs_cookie _cookie)
{
AttributeCookie* cookie = (AttributeCookie*)_cookie;
// If the attribute doesn't exist yet and it was opened with
// CreateAttr(), we could create it now. We have a race condition here
// though, since someone else could have created it in the meantime.
delete cookie;
return B_OK;
}
// ReadAttr
status_t
BeOSKernelVolume::ReadAttr(fs_vnode node, fs_cookie _cookie, off_t pos,
void* buffer, size_t bufferSize, size_t* bytesRead)
{
AttributeCookie* cookie = (AttributeCookie*)_cookie;
// check, if open mode allows reading
if ((open_mode_to_access(cookie->fOpenMode) | R_OK) == 0)
return B_FILE_ERROR;
// read
if (!fFSOps->read_attr)
return B_BAD_VALUE;
*bytesRead = bufferSize;
return fFSOps->read_attr(fVolumeCookie, node, cookie->fName, cookie->fType,
buffer, bytesRead, pos);
}
// WriteAttr
status_t
BeOSKernelVolume::WriteAttr(fs_vnode node, fs_cookie _cookie, off_t pos,
const void* buffer, size_t bufferSize, size_t* bytesWritten)
{
AttributeCookie* cookie = (AttributeCookie*)_cookie;
// check, if open mode allows writing
if ((open_mode_to_access(cookie->fOpenMode) | W_OK) == 0)
return B_FILE_ERROR;
// write
if (!fFSOps->write_attr)
return B_BAD_VALUE;
*bytesWritten = bufferSize;
return fFSOps->write_attr(fVolumeCookie, node, cookie->fName, cookie->fType,
buffer, bytesWritten, pos);
}
// ReadAttrStat
status_t
BeOSKernelVolume::ReadAttrStat(fs_vnode node, fs_cookie _cookie,
struct stat *st)
{
AttributeCookie* cookie = (AttributeCookie*)_cookie;
// get the stats
beos_attr_info attrInfo;
if (!fFSOps->stat_attr)
return B_BAD_VALUE;
status_t error = fFSOps->stat_attr(fVolumeCookie, node, cookie->fName,
&attrInfo);
if (error != B_OK)
return error;
// translate to struct stat
st->st_size = attrInfo.size;
st->st_type = attrInfo.type;
return B_OK;
}
// RenameAttr
status_t
BeOSKernelVolume::RenameAttr(fs_vnode oldNode, const char* oldName,
fs_vnode newNode, const char* newName)
{
if (!fFSOps->rename_attr)
return B_BAD_VALUE;
if (oldNode != newNode)
return B_BAD_VALUE;
return fFSOps->rename_attr(fVolumeCookie, oldNode, oldName, newName);
}
// RemoveAttr
status_t
BeOSKernelVolume::RemoveAttr(fs_vnode node, const char* name)
{
if (!fFSOps->remove_attr)
return B_BAD_VALUE;
return fFSOps->remove_attr(fVolumeCookie, node, name);
}
// #pragma mark - indices
// OpenIndexDir
status_t
BeOSKernelVolume::OpenIndexDir(fs_cookie *cookie)
{
if (!fFSOps->open_indexdir)
return B_BAD_VALUE;
return fFSOps->open_indexdir(fVolumeCookie, cookie);
}
// CloseIndexDir
status_t
BeOSKernelVolume::CloseIndexDir(fs_cookie cookie)
{
if (!fFSOps->close_indexdir)
return B_OK;
return fFSOps->close_indexdir(fVolumeCookie, cookie);
}
// FreeIndexDirCookie
status_t
BeOSKernelVolume::FreeIndexDirCookie(fs_cookie cookie)
{
if (!fFSOps->free_indexdircookie)
return B_OK;
return fFSOps->free_indexdircookie(fVolumeCookie, NULL, cookie);
}
// ReadIndexDir
status_t
BeOSKernelVolume::ReadIndexDir(fs_cookie cookie, void* buffer,
size_t bufferSize, uint32 count, uint32* countRead)
{
if (!fFSOps->read_indexdir)
return B_BAD_VALUE;
*countRead = count;
// Haiku's struct dirent equals BeOS's version
return fFSOps->read_indexdir(fVolumeCookie, cookie, (long*)countRead,
(struct beos_dirent*)buffer, bufferSize);
}
// RewindIndexDir
status_t
BeOSKernelVolume::RewindIndexDir(fs_cookie cookie)
{
if (!fFSOps->rewind_indexdir)
return B_BAD_VALUE;
return fFSOps->rewind_indexdir(fVolumeCookie, cookie);
}
// CreateIndex
status_t
BeOSKernelVolume::CreateIndex(const char* name, uint32 type, uint32 flags)
{
if (!fFSOps->create_index)
return B_BAD_VALUE;
return fFSOps->create_index(fVolumeCookie, name, (int)type, (int)flags);
}
// RemoveIndex
status_t
2007-02-26 16:26:02 +00:00
BeOSKernelVolume::RemoveIndex(const char* name)
{
if (!fFSOps->remove_index)
return B_BAD_VALUE;
return fFSOps->remove_index(fVolumeCookie, name);
}
// StatIndex
status_t
BeOSKernelVolume::ReadIndexStat(const char *name, struct stat *st)
{
if (!fFSOps->stat_index)
return B_BAD_VALUE;
beos_index_info indexInfo;
status_t error = fFSOps->stat_index(fVolumeCookie, name, &indexInfo);
if (error != B_OK)
return error;
// translate index_info into struct stat
st->st_type = indexInfo.type;
st->st_size = indexInfo.size;
st->st_mtime = indexInfo.modification_time;
st->st_crtime = indexInfo.creation_time;
st->st_uid = indexInfo.uid;
st->st_gid = indexInfo.gid;
return B_OK;
}
// #pragma mark - queries
// OpenQuery
status_t
BeOSKernelVolume::OpenQuery(const char* queryString, uint32 flags, port_id port,
uint32 token, fs_cookie *cookie)
{
if (!fFSOps->open_query)
return B_BAD_VALUE;
return fFSOps->open_query(fVolumeCookie, queryString, flags, port,
(long)token, cookie);
}
// CloseQuery
status_t
BeOSKernelVolume::CloseQuery(fs_cookie cookie)
{
if (!fFSOps->close_query)
return B_OK;
return fFSOps->close_query(fVolumeCookie, cookie);
}
// FreeQueryCookie
status_t
BeOSKernelVolume::FreeQueryCookie(fs_cookie cookie)
{
if (!fFSOps->free_querycookie)
return B_OK;
return fFSOps->free_querycookie(fVolumeCookie, NULL, cookie);
}
// ReadQuery
status_t
BeOSKernelVolume::ReadQuery(fs_cookie cookie, void* buffer, size_t bufferSize,
uint32 count, uint32* countRead)
{
if (!fFSOps->read_query)
return B_BAD_VALUE;
*countRead = count;
// Haiku's struct dirent equals BeOS's version
return fFSOps->read_query(fVolumeCookie, cookie, (long*)countRead,
(struct beos_dirent*)buffer, bufferSize);
}
// #pragma mark - Private
// _OpenAttr
status_t
BeOSKernelVolume::_OpenAttr(fs_vnode node, const char* name, uint32 type,
int openMode, bool create, fs_cookie* _cookie)
{
// check permissions first
int accessMode = open_mode_to_access(openMode) | (create ? W_OK : 0);
status_t error = Access(node, accessMode);
if (error != B_OK)
return error;
// check whether the attribute already exists
beos_attr_info attrInfo;
if (!fFSOps->stat_attr)
return B_BAD_VALUE;
bool exists
= (fFSOps->stat_attr(fVolumeCookie, node, name, &attrInfo) == B_OK);
if (create) {
// create: fail, if attribute exists and non-existence was required
if (exists && (openMode & O_EXCL))
return B_FILE_EXISTS;
} else {
// open: fail, if attribute doesn't exist
if (!exists)
return B_ENTRY_NOT_FOUND;
// keep the attribute type
type = attrInfo.type;
}
// create an attribute cookie
AttributeCookie* cookie = new(nothrow) AttributeCookie(name, type,
openMode, exists, create);
if (!cookie)
return B_NO_MEMORY;
// TODO: If we want to support O_TRUNC, we should do that here.
*_cookie = cookie;
return B_OK;
}
// open_mode_to_access
static int
open_mode_to_access(int openMode)
{
switch (openMode & O_RWMASK) {
case O_RDONLY:
return R_OK;
case O_WRONLY:
return W_OK;
case O_RDWR:
default:
return W_OK | R_OK;
}
}