* Copyright 2012-2020 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include <AutoDeleter.h>
#include <fs_cache.h>
#include <fs_interface.h>
#include <fs_volume.h>
#include "Connection.h"
#include "FileSystem.h"
#include "IdMap.h"
#include "Inode.h"
#include "NFS4Defs.h"
#include "RequestBuilder.h"
#include "ReplyInterpreter.h"
#include "RootInode.h"
#include "RPCCallbackServer.h"
#include "RPCServer.h"
#include "VnodeToInode.h"
#include "WorkQueue.h"
extern fs_volume_ops gNFSv4VolumeOps;
extern fs_vnode_ops gNFSv4VnodeOps;
RPC::ServerManager* gRPCServerManager;
RPC::ProgramData*
CreateNFS4Server(RPC::Server* serv)
{
return new NFS4Server(serv);
}
static status_t
ParseArguments(const char* _args, AddressResolver** address, char** _server,
char** _path, MountConfiguration* conf)
{
if (_args == NULL)
return B_BAD_VALUE;
char* args = strdup(_args);
if (args == NULL)
return B_NO_MEMORY;
MemoryDeleter argsDeleter(args);
char* options = strchr(args, ' ');
if (options != NULL)
*options++ = '\0';
char* path = strrchr(args, ':');
if (path == NULL)
return B_MISMATCHED_VALUES;
*path++ = '\0';
*_server = strdup(args);
if (*_server == NULL)
return B_NO_MEMORY;
*address = new AddressResolver(args);
if (*address == NULL) {
free(*_server);
return B_NO_MEMORY;
}
*_path = strdup(path);
if (*_path == NULL) {
free(*_server);
delete *address;
return B_NO_MEMORY;
}
conf->fHard = false;
conf->fRetryLimit = 5;
conf->fRequestTimeout = sSecToBigTime(60);
conf->fEmulateNamedAttrs = false;
conf->fCacheMetadata = true;
conf->fDirectoryCacheTime = sSecToBigTime(5);
char* optionsEnd = NULL;
if (options != NULL)
optionsEnd = strchr(options, ' ');
while (options != NULL && *options != '\0') {
if (optionsEnd != NULL)
*optionsEnd++ = '\0';
if (strcmp(options, "hard") == 0)
conf->fHard = true;
else if (strncmp(options, "retrans=", 8) == 0) {
options += strlen("retrans=");
conf->fRetryLimit = atoi(options);
} else if (strncmp(options, "timeo=", 6) == 0) {
options += strlen("timeo=");
conf->fRequestTimeout = atoi(options);
} else if (strcmp(options, "noac") == 0)
conf->fCacheMetadata = false;
else if (strcmp(options, "xattr-emu") == 0)
conf->fEmulateNamedAttrs = true;
else if (strncmp(options, "port=", 5) == 0) {
options += strlen("port=");
(*address)->ForcePort(atoi(options));
} else if (strncmp(options, "proto=", 6) == 0) {
options += strlen("proto=");
(*address)->ForceProtocol(options);
} else if (strncmp(options, "dirtime=", 8) == 0) {
options += strlen("dirtime=");
conf->fDirectoryCacheTime = sSecToBigTime(atoi(options));
}
options = optionsEnd;
if (options != NULL)
optionsEnd = strchr(options, ' ');
}
return B_OK;
}
static status_t
nfs4_mount(fs_volume* volume, const char* device, uint32 flags,
const char* args, ino_t* _rootVnodeID)
{
TRACE("volume = %p, device = %s, flags = %" B_PRIu32 ", args = %s\n",
volume, device, flags, args);
status_t result;
MutexLocker locker(gIdMapperLock);
if (gIdMapper == NULL) {
gIdMapper = new(std::nothrow) IdMap;
if (gIdMapper == NULL)
return B_NO_MEMORY;
result = gIdMapper->InitStatus();
if (result != B_OK) {
delete gIdMapper;
gIdMapper = NULL;
return result;
}
}
locker.Unlock();
MountConfiguration config;
config.fReadOnly = (flags & B_MOUNT_READ_ONLY) != 0;
AddressResolver* resolver;
char* path;
char* serverName;
result = ParseArguments(args, &resolver, &serverName, &path, &config);
if (result != B_OK) {
ERROR("Unable to parse mount arguments!\n");
return result;
}
MemoryDeleter pathDeleter(path);
MemoryDeleter serverNameDeleter(serverName);
RPC::Server* server;
result = gRPCServerManager->Acquire(&server, resolver, CreateNFS4Server);
delete resolver;
if (result != B_OK) {
ERROR("Unable to Acquire RPCServerManager!\n");
return result;
}
FileSystem* fs;
result = FileSystem::Mount(&fs, server, serverName, path, volume, config);
if (result != B_OK) {
ERROR("Error mounting filesystem: %s\n", strerror(result));
gRPCServerManager->Release(server);
return result;
}
Inode* inode = fs->Root();
if (inode == NULL) {
delete fs;
gRPCServerManager->Release(server);
ERROR("Unable to locate root inode!\n");
return B_IO_ERROR;
}
volume->private_volume = fs;
volume->ops = &gNFSv4VolumeOps;
VnodeToInode* vti = new VnodeToInode(inode->ID(), fs);
if (vti == NULL) {
delete fs;
gRPCServerManager->Release(server);
ERROR("Unable to translate vnode to inode!\n");
return B_NO_MEMORY;
}
vti->Replace(inode);
result = publish_vnode(volume, inode->ID(), vti, &gNFSv4VnodeOps,
inode->Type(), 0);
if (result != B_OK)
return result;
*_rootVnodeID = inode->ID();
TRACE("*_rootVnodeID = %" B_PRIi64 "\n", inode->ID());
return B_OK;
}
static status_t
nfs4_get_vnode(fs_volume* volume, ino_t id, fs_vnode* vnode, int* _type,
uint32* _flags, bool reenter)
{
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
TRACE("volume = %p, id = %" B_PRIi64 "\n", volume, id);
VnodeToInode* vnodeToInode = new VnodeToInode(id, fs);
if (vnodeToInode == NULL)
return B_NO_MEMORY;
Inode* inode;
status_t result = fs->GetInode(id, &inode);
if (result != B_OK) {
delete vnodeToInode;
return result;
}
vnodeToInode->Replace(inode);
vnode->ops = &gNFSv4VnodeOps;
vnode->private_node = vnodeToInode;
*_type = inode->Type();
*_flags = 0;
return B_OK;
}
static status_t
nfs4_unmount(fs_volume* volume)
{
TRACE("volume = %p\n", volume);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
RPC::Server* server = fs->Server();
put_vnode(volume, fs->Root()->ID());
delete fs;
gRPCServerManager->Release(server);
return B_OK;
}
static status_t
nfs4_read_fs_info(fs_volume* volume, struct fs_info* info)
{
TRACE("volume = %p\n", volume);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
RootInode* inode = reinterpret_cast<RootInode*>(fs->Root());
return inode->ReadInfo(info);
}
static status_t
nfs4_lookup(fs_volume* volume, fs_vnode* dir, const char* name, ino_t* _id)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
if (!strcmp(name, ".")) {
*_id = vti->ID();
void* ptr;
return get_vnode(volume, *_id, &ptr);
}
VnodeToInodeLocker locker(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s\n", volume, vti->ID(),
name);
status_t result = inode->LookUp(name, _id);
if (result != B_OK)
return result;
locker.Unlock();
TRACE("*_id = %" B_PRIi64 "\n", *_id);
void* ptr;
result = get_vnode(volume, *_id, &ptr);
if (result == B_OK)
unremove_vnode(volume, *_id);
return result;
}
static status_t
nfs4_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 "\n", volume, vti->ID());
delete vti;
return B_OK;
}
static status_t
nfs4_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
{
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 "\n", volume, vti->ID());
Inode* node = vti->GetPointer();
if (node == fs->Root())
return B_OK;
if (node != NULL && node->IsStale() == false) {
FileInfo fileInfo;
ASSERT(fs->InoIdMap()->GetFileInfo(&fileInfo, vti->ID()) == B_ENTRY_NOT_FOUND);
}
delete vti;
return B_OK;
}
static status_t
nfs4_read_pages(fs_volume* _volume, fs_vnode* vnode, void* _cookie, off_t pos,
const iovec* vecs, size_t count, size_t* _numBytes)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
", count = %lu, numBytes = %lu\n", _volume, vti->ID(), _cookie, pos,
count, *_numBytes);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
status_t result;
size_t totalRead = 0;
bool eof = false;
for (size_t i = 0; i < count && !eof; i++) {
size_t bytesLeft = vecs[i].iov_len;
char* buffer = reinterpret_cast<char*>(vecs[i].iov_base);
do {
size_t bytesRead = bytesLeft;
result = inode->ReadDirect(cookie, pos, buffer, &bytesRead, &eof);
if (result != B_OK)
return result;
totalRead += bytesRead;
pos += bytesRead;
buffer += bytesRead;
bytesLeft -= bytesRead;
} while (bytesLeft > 0 && !eof);
}
*_numBytes = totalRead;
TRACE("*numBytes = %lu\n", totalRead);
return B_OK;
}
static status_t
nfs4_write_pages(fs_volume* _volume, fs_vnode* vnode, void* _cookie, off_t pos,
const iovec* vecs, size_t count, size_t* _numBytes)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
", count = %lu, numBytes = %lu\n", _volume, vti->ID(), _cookie, pos,
count, *_numBytes);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
status_t result;
for (size_t i = 0; i < count; i++) {
uint64 bytesLeft = vecs[i].iov_len;
if (pos + bytesLeft > inode->MaxFileSize())
bytesLeft = inode->MaxFileSize() - pos;
char* buffer = reinterpret_cast<char*>(vecs[i].iov_base);
do {
size_t bytesWritten = bytesLeft;
result = inode->WriteDirect(cookie, pos, buffer, &bytesWritten);
if (result != B_OK)
return result;
bytesLeft -= bytesWritten;
pos += bytesWritten;
buffer += bytesWritten;
} while (bytesLeft > 0);
}
return B_OK;
}
static status_t
nfs4_io(fs_volume* volume, fs_vnode* vnode, void* cookie, io_request* request)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p\n", volume,
vti->ID(), cookie);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
IORequestArgs* args = new(std::nothrow) IORequestArgs;
if (args == NULL) {
notify_io_request(request, B_NO_MEMORY);
return B_NO_MEMORY;
}
args->fRequest = request;
args->fInode = inode;
status_t result = gWorkQueue->EnqueueJob(IORequest, args);
if (result != B_OK)
notify_io_request(request, result);
return result;
}
static status_t
nfs4_get_file_map(fs_volume* volume, fs_vnode* vnode, off_t _offset,
size_t size, struct file_io_vec* vecs, size_t* _count)
{
return B_ERROR;
}
static status_t
nfs4_set_flags(fs_volume* volume, fs_vnode* vnode, void* _cookie, int flags)
{
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, flags = %d\n",
volume, reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(),
_cookie, flags);
OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
cookie->fMode = (cookie->fMode & ~(O_APPEND | O_NONBLOCK)) | flags;
return B_OK;
}
static status_t
nfs4_fsync(fs_volume* volume, fs_vnode* vnode, bool dataOnly)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 "\n", volume, vti->ID());
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->SyncAndCommit();
}
static status_t
nfs4_read_symlink(fs_volume* volume, fs_vnode* link, char* buffer,
size_t* _bufferSize)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(link->private_node);
TRACE("volume = %p, link = %" B_PRIi64 "\n", volume, vti->ID());
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->ReadLink(buffer, _bufferSize);
}
static status_t
nfs4_create_symlink(fs_volume* volume, fs_vnode* dir, const char* name,
const char* path, int mode)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, path = %s, mode = %d\n",
volume, vti->ID(), name, path, mode);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
ino_t id;
status_t result = inode->CreateLink(name, path, mode, &id);
if (result != B_OK)
return result;
result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
if (result == B_OK) {
unremove_vnode(volume, id);
vti->Clear();
put_vnode(volume, id);
}
return B_OK;
}
static status_t
nfs4_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* vnode)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
VnodeToInode* dirVti = reinterpret_cast<VnodeToInode*>(dir->private_node);
TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, vnode = %" B_PRIi64
"\n", volume, dirVti->ID(), name, vti->ID());
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _dir(dirVti);
Inode* dirInode = dirVti->Get();
if (dirInode == NULL)
return B_ENTRY_NOT_FOUND;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->Link(dirInode, name);
}
static status_t
nfs4_unlink(fs_volume* volume, fs_vnode* dir, const char* name)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s\n", volume, vti->ID(),
name);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker locker(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
ino_t id;
status_t result = inode->LookUp(name, &id);
if (result != B_OK)
return B_ENTRY_NOT_FOUND;
VnodeToInode* childVti = NULL;
result = get_vnode(volume, id, reinterpret_cast<void**>(&childVti));
if (result == B_OK) {
childVti->Get();
ino_t removedId;
status_t result = inode->Remove(name, NF4REG, &removedId);
if (result != B_OK)
return result;
ASSERT(removedId == id);
locker.Unlock();
if (childVti->Unlink(inode->fInfo.fNames, name))
remove_vnode(volume, id);
put_vnode(volume, id);
}
return result;
}
static status_t
nfs4_rename(fs_volume* volume, fs_vnode* fromDir, const char* fromName,
fs_vnode* toDir, const char* toName)
{
VnodeToInode* fromVti
= reinterpret_cast<VnodeToInode*>(fromDir->private_node);
VnodeToInode* toVti = reinterpret_cast<VnodeToInode*>(toDir->private_node);
TRACE("volume = %p, fromDir = %" B_PRIi64 ", toDir = %" B_PRIi64 ","
" fromName = %s, toName = %s\n", volume, fromVti->ID(), toVti->ID(),
fromName, toName);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _from(fromVti);
Inode* fromInode = fromVti->Get();
if (fromInode == NULL)
return B_ENTRY_NOT_FOUND;
VnodeToInodeLocker _to(toVti);
Inode* toInode = toVti->Get();
if (toInode == NULL)
return B_ENTRY_NOT_FOUND;
ino_t id;
ino_t oldID;
status_t result = Inode::Rename(fromInode, toInode, fromName, toName, false,
&id, &oldID);
if (result != B_OK)
return result;
VnodeToInode* vti;
if (oldID != 0) {
result = get_vnode(volume, oldID, reinterpret_cast<void**>(&vti));
if (result == B_OK) {
if (vti->Unlink(toInode->fInfo.fNames, toName))
remove_vnode(volume, oldID);
put_vnode(volume, oldID);
}
}
result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
if (result == B_OK) {
Inode* child = vti->Get();
if (child == NULL) {
put_vnode(volume, id);
return B_ENTRY_NOT_FOUND;
}
unremove_vnode(volume, id);
child->fInfo.fNames->RemoveName(fromInode->fInfo.fNames, fromName);
child->fInfo.fNames->AddName(toInode->fInfo.fNames, toName);
put_vnode(volume, id);
}
return B_OK;
}
static status_t
nfs4_access(fs_volume* volume, fs_vnode* vnode, int mode)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", mode = %d\n", volume, vti->ID(),
mode);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->Access(mode);
}
static status_t
nfs4_read_stat(fs_volume* volume, fs_vnode* vnode, struct stat* stat)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 "\n", volume, vti->ID());
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
status_t result = inode->Stat(stat);
if (inode->GetOpenState() != NULL)
stat->st_size = inode->MaxFileSize();
return result;
}
static status_t
nfs4_write_stat(fs_volume* volume, fs_vnode* vnode, const struct stat* stat,
uint32 statMask)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", statMask = %" B_PRIu32 "\n",
volume, vti->ID(), statMask);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->WriteStat(stat, statMask);
}
static status_t
nfs4_create(fs_volume* volume, fs_vnode* dir, const char* name, int openMode,
int perms, void** _cookie, ino_t* _newVnodeID)
{
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
OpenFileCookie* cookie = new OpenFileCookie(fs);
if (cookie == NULL)
return B_NO_MEMORY;
*_cookie = cookie;
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, openMode = %d," \
" perms = %d\n", volume, vti->ID(), name, openMode, perms);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
MutexLocker createLocker(fs->CreateFileLock());
OpenDelegationData data;
status_t result = inode->Create(name, openMode, perms, cookie, &data,
_newVnodeID);
if (result != B_OK) {
delete cookie;
return result;
}
result = get_vnode(volume, *_newVnodeID, reinterpret_cast<void**>(&vti));
if (result != B_OK) {
delete cookie;
return result;
}
VnodeToInodeLocker _child(vti);
Inode* child = vti->Get();
if (child == NULL) {
delete cookie;
put_vnode(volume, *_newVnodeID);
return B_ENTRY_NOT_FOUND;
}
child->SetOpenState(cookie->fOpenState);
if (data.fType != OPEN_DELEGATE_NONE) {
Delegation* delegation
= new(std::nothrow) Delegation(data, child,
cookie->fOpenState->fClientID);
if (delegation != NULL) {
delegation->fInfo = cookie->fOpenState->fInfo;
delegation->fFileSystem = child->GetFileSystem();
child->SetDelegation(delegation);
}
}
TRACE("*cookie = %p, *newVnodeID = %" B_PRIi64 "\n", *_cookie,
*_newVnodeID);
return result;
}
static status_t
nfs4_open(fs_volume* volume, fs_vnode* vnode, int openMode, void** _cookie)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", openMode = %d\n", volume,
vti->ID(), openMode);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly && (openMode & O_RWMASK) != O_RDONLY)
return B_READ_ONLY_DEVICE;
if (inode->Type() == S_IFDIR && (openMode & O_RWMASK) != O_RDONLY)
return B_IS_A_DIRECTORY;
if ((openMode & O_DIRECTORY) != 0 && inode->Type() != S_IFDIR)
return B_NOT_A_DIRECTORY;
if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) {
*_cookie = NULL;
return B_OK;
}
OpenFileCookie* cookie = new OpenFileCookie(fs);
if (cookie == NULL)
return B_NO_MEMORY;
*_cookie = cookie;
status_t result = inode->Open(openMode, cookie);
if (result != B_OK)
delete cookie;
TRACE("*cookie = %p\n", *_cookie);
return result;
}
static status_t
nfs4_close(fs_volume* volume, fs_vnode* vnode, void* _cookie)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p\n", volume,
vti->ID(), _cookie);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK)
return B_OK;
Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
return cookie->CancelAll();
}
static status_t
nfs4_free_cookie(fs_volume* volume, fs_vnode* vnode, void* _cookie)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p\n", volume,
vti->ID(), _cookie);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK)
return B_OK;
OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
inode->Close(cookie);
delete cookie;
return B_OK;
}
static status_t
nfs4_read(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
void* buffer, size_t* length)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
", length = %lu\n", volume, vti->ID(), _cookie, pos, *length);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
if (inode->Type() == S_IFDIR)
return B_IS_A_DIRECTORY;
if (inode->Type() == S_IFLNK)
return B_BAD_VALUE;
OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
return inode->Read(cookie, pos, buffer, length);;
}
static status_t
nfs4_write(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
const void* _buffer, size_t* length)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
", length = %lu\n", volume, vti->ID(), _cookie, pos, *length);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
if (inode->Type() == S_IFDIR)
return B_IS_A_DIRECTORY;
if (inode->Type() == S_IFLNK)
return B_BAD_VALUE;
OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
return inode->Write(cookie, pos, _buffer, length);
}
static status_t
nfs4_create_dir(fs_volume* volume, fs_vnode* parent, const char* name,
int mode)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(parent->private_node);
TRACE("volume = %p, parent = %" B_PRIi64 ", mode = %d\n", volume, vti->ID(),
mode);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
ino_t id;
status_t result = inode->CreateDir(name, mode, &id);
if (result != B_OK)
return result;
result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
if (result == B_OK) {
unremove_vnode(volume, id);
vti->Clear();
put_vnode(volume, id);
}
return B_OK;
}
static status_t
nfs4_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(parent->private_node);
TRACE("volume = %p, parent = %" B_PRIi64 ", name = %s\n", volume, vti->ID(),
name);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
ino_t id;
status_t result = inode->Remove(name, NF4DIR, &id);
if (result != B_OK)
return result;
result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
if (result == B_OK) {
if (vti->Unlink(inode->fInfo.fNames, name))
remove_vnode(volume, id);
put_vnode(volume, id);
}
return B_OK;
}
static status_t
nfs4_open_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
{
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
OpenDirCookie* cookie = new(std::nothrow) OpenDirCookie(fs);
if (cookie == NULL)
return B_NO_MEMORY;
*_cookie = cookie;
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 "\n", volume, vti->ID());
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
status_t result = inode->OpenDir(cookie);
if (result != B_OK)
delete cookie;
TRACE("*cookie = %p\n", *_cookie);
return result;
}
static status_t
nfs4_close_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie)
{
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p\n", volume,
reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), _cookie);
Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
return cookie->CancelAll();
}
static status_t
nfs4_free_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
{
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p\n", volume,
reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), cookie);
delete reinterpret_cast<OpenDirCookie*>(cookie);
return B_OK;
}
static status_t
nfs4_read_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie,
struct dirent* buffer, size_t bufferSize, uint32* _num)
{
OpenDirCookie* cookie = reinterpret_cast<OpenDirCookie*>(_cookie);
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p\n", volume, vti->ID(),
_cookie);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->ReadDir(buffer, bufferSize, _num, cookie);
}
static status_t
nfs4_rewind_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie)
{
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p\n", volume,
reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), _cookie);
OpenDirCookie* cookie = reinterpret_cast<OpenDirCookie*>(_cookie);
cookie->fSpecial = 0;
if (cookie->fSnapshot != NULL)
cookie->fSnapshot->ReleaseReference();
cookie->fSnapshot = NULL;
cookie->fCurrent = NULL;
cookie->fEOF = false;
return B_OK;
}
static status_t
nfs4_open_attr_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
{
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
OpenDirCookie* cookie = new(std::nothrow) OpenDirCookie(fs);
if (cookie == NULL)
return B_NO_MEMORY;
*_cookie = cookie;
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 "\n", volume, vti->ID());
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
status_t result = inode->OpenAttrDir(cookie);
if (result != B_OK)
delete cookie;
return result;
}
static status_t
nfs4_close_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
{
return nfs4_close_dir(volume, vnode, cookie);
}
static status_t
nfs4_free_attr_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
{
return nfs4_free_dir_cookie(volume, vnode, cookie);
}
static status_t
nfs4_read_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie,
struct dirent* buffer, size_t bufferSize, uint32* _num)
{
return nfs4_read_dir(volume, vnode, cookie, buffer, bufferSize, _num);
}
static status_t
nfs4_rewind_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
{
return nfs4_rewind_dir(volume, vnode, cookie);
}
static status_t
nfs4_create_attr(fs_volume* volume, fs_vnode* vnode, const char* name,
uint32 type, int openMode, void** _cookie)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
OpenAttrCookie* cookie = new OpenAttrCookie(fs);
if (cookie == NULL)
return B_NO_MEMORY;
*_cookie = cookie;
status_t result = inode->OpenAttr(name, openMode, cookie, true, type);
if (result != B_OK)
delete cookie;
return result;
}
static status_t
nfs4_open_attr(fs_volume* volume, fs_vnode* vnode, const char* name,
int openMode, void** _cookie)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly && (openMode & O_RWMASK) != O_RDONLY)
return B_READ_ONLY_DEVICE;
OpenAttrCookie* cookie = new OpenAttrCookie(fs);
if (cookie == NULL)
return B_NO_MEMORY;
*_cookie = cookie;
status_t result = inode->OpenAttr(name, openMode, cookie, false);
if (result != B_OK)
delete cookie;
return result;
}
static status_t
nfs4_close_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie)
{
Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
return cookie->CancelAll();
}
static status_t
nfs4_free_attr_cookie(fs_volume* volume, fs_vnode* vnode, void* _cookie)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
inode->CloseAttr(cookie);
delete cookie;
return B_OK;
}
static status_t
nfs4_read_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
void* buffer, size_t* length)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
bool eof;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->ReadDirect(cookie, pos, buffer, length, &eof);
}
static status_t
nfs4_write_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
const void* buffer, size_t* length)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->WriteDirect(cookie, pos, buffer, length);
}
static status_t
nfs4_read_attr_stat(fs_volume* volume, fs_vnode* vnode, void* _cookie,
struct stat* stat)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->Stat(stat, cookie);
}
static status_t
nfs4_write_attr_stat(fs_volume* volume, fs_vnode* vnode, void* _cookie,
const struct stat* stat, int statMask)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->WriteStat(stat, statMask, cookie);
}
static status_t
nfs4_rename_attr(fs_volume* volume, fs_vnode* fromVnode, const char* fromName,
fs_vnode* toVnode, const char* toName)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(toVnode->private_node);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker to(vti);
Inode* toInode = vti->Get();
if (toInode == NULL)
return B_ENTRY_NOT_FOUND;
vti = reinterpret_cast<VnodeToInode*>(fromVnode->private_node);
VnodeToInodeLocker from(vti);
Inode* fromInode = vti->Get();
if (fromInode == NULL)
return B_ENTRY_NOT_FOUND;
return Inode::Rename(fromInode, toInode, fromName, toName, true);
}
static status_t
nfs4_remove_attr(fs_volume* volume, fs_vnode* vnode, const char* name)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->Remove(name, NF4NAMEDATTR, NULL);
}
static status_t
nfs4_test_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie,
struct flock* lock)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, lock = %p\n",
volume, vti->ID(), _cookie, lock);
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
return inode->TestLock(cookie, lock);
}
static status_t
nfs4_acquire_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie,
const struct flock* lock, bool wait)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, lock = %p\n",
volume, vti->ID(), _cookie, lock);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
inode->RevalidateFileCache();
return inode->AcquireLock(cookie, lock, wait);
}
static status_t
nfs4_release_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie,
const struct flock* lock)
{
VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, lock = %p\n",
volume, vti->ID(), _cookie, lock);
FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
if (fs->GetConfiguration().fReadOnly)
return B_READ_ONLY_DEVICE;
VnodeToInodeLocker _(vti);
Inode* inode = vti->Get();
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK)
return B_OK;
OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
if (lock != NULL)
return inode->ReleaseLock(cookie, lock);
else
return inode->ReleaseAllLocks(cookie);
}
status_t
nfs4_init()
{
init_debugging();
gRPCServerManager = new(std::nothrow) RPC::ServerManager;
if (gRPCServerManager == NULL)
return B_NO_MEMORY;
mutex_init(&gIdMapperLock, "idmapper Init Lock");
gIdMapper = NULL;
gWorkQueue = new(std::nothrow) WorkQueue;
if (gWorkQueue == NULL || gWorkQueue->InitStatus() != B_OK) {
delete gWorkQueue;
mutex_destroy(&gIdMapperLock);
delete gRPCServerManager;
return B_NO_MEMORY;
}
#ifdef _KERNEL_MODE
add_debugger_command("nfs4", kprintf_volume, "dump an nfs4 volume");
add_debugger_command("nfs4_inode", kprintf_inode, "dump an nfs4 inode");
#endif
return B_OK;
}
status_t
nfs4_uninit()
{
exit_debugging();
RPC::CallbackServer::ShutdownAll();
delete gIdMapper;
delete gWorkQueue;
delete gRPCServerManager;
mutex_destroy(&gIdMapperLock);
#ifdef _KERNEL_MODE
remove_debugger_command("nfs4", kprintf_volume);
remove_debugger_command("nfs4_inode", kprintf_inode);
#endif
return B_OK;
}
static status_t
nfs4_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
return nfs4_init();
case B_MODULE_UNINIT:
return nfs4_uninit();
default:
return B_ERROR;
}
}
fs_volume_ops gNFSv4VolumeOps = {
nfs4_unmount,
nfs4_read_fs_info,
NULL,
NULL,
nfs4_get_vnode,
};
fs_vnode_ops gNFSv4VnodeOps = {
nfs4_lookup,
NULL,
nfs4_put_vnode,
nfs4_remove_vnode,
NULL,
nfs4_read_pages,
nfs4_write_pages,
nfs4_io,
NULL,
nfs4_get_file_map,
NULL,
nfs4_set_flags,
NULL,
NULL,
nfs4_fsync,
nfs4_read_symlink,
nfs4_create_symlink,
nfs4_link,
nfs4_unlink,
nfs4_rename,
nfs4_access,
nfs4_read_stat,
nfs4_write_stat,
NULL,
nfs4_create,
nfs4_open,
nfs4_close,
nfs4_free_cookie,
nfs4_read,
nfs4_write,
nfs4_create_dir,
nfs4_remove_dir,
nfs4_open_dir,
nfs4_close_dir,
nfs4_free_dir_cookie,
nfs4_read_dir,
nfs4_rewind_dir,
nfs4_open_attr_dir,
nfs4_close_attr_dir,
nfs4_free_attr_dir_cookie,
nfs4_read_attr_dir,
nfs4_rewind_attr_dir,
nfs4_create_attr,
nfs4_open_attr,
nfs4_close_attr,
nfs4_free_attr_cookie,
nfs4_read_attr,
nfs4_write_attr,
nfs4_read_attr_stat,
nfs4_write_attr_stat,
nfs4_rename_attr,
nfs4_remove_attr,
NULL,
NULL,
nfs4_test_lock,
nfs4_acquire_lock,
nfs4_release_lock,
};
static file_system_module_info sNFSv4ModuleInfo = {
{
"file_systems/nfs4" B_CURRENT_FS_API_VERSION,
0,
nfs4_std_ops,
},
"nfs4",
"Network File System version 4",
0,
NULL,
NULL,
NULL,
NULL,
nfs4_mount,
};
module_info* modules[] = {
(module_info*)&sNFSv4ModuleInfo,
NULL,
};