Files
haiku-beta6/src/add-ons/kernel/file_systems/userlandfs/server/BeOSKernelVolume.cpp
T
Ingo Weinhold cdebdeb0aa Some more work towards adjusting the UserlandFS to the current FS interface:
* Split the FSCapabilities bit mask into three bit masks for the FS, the
  volume, and the vnodes, since we have three independent FS interface
  structures, now. The latter is not supported in the kernel add-on, yet.
* Server:
  - Temporarily removed some things from the build (the BeOS interface
    and the cache implementations).
  - Some WIP in HaikuKernelVolume and [haiku_]kernel_emu.{h,cpp}.
  - Added HaikuKernelNode, which wraps fs_vnode for the client FS.
  The server is still quite a bit away from being buildable again.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29323 a95241bf-73f2-0310-859d-f6bbb57e9c96
2009-02-26 00:09:43 +00:00

880 lines
19 KiB
C++

// BeOSKernelVolume.cpp
#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)
: Volume(fileSystem, id),
fFSOps(fsOps),
fVolumeCookie(NULL)
{
}
// destructor
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
BeOSKernelVolume::Unmount()
{
if (!fFSOps->unmount)
return B_BAD_VALUE;
return fFSOps->unmount(fVolumeCookie);
}
// Sync
status_t
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)
{
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, int* type,
uint32* flags)
{
#error Return type and flags!
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,
selectsync* sync)
{
if (!fFSOps->select) {
UserlandFS::KernelEmu::notify_select_event(sync, event, false);
return B_OK;
}
return fFSOps->select(fVolumeCookie, node, cookie, event, 0, 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
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;
}
}