/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012, 2013, 2014.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see .
vnode.cpp
Nodes in the virtual filesystem.
*******************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "fs/user.h"
namespace Sortix {
static Ref LookupMountUnlocked(Ref inode, size_t* out_index = NULL)
{
Ref result_inode(NULL);
Ref mtable = CurrentProcess()->GetMTable();
for ( size_t i = 0; i < mtable->nummounts; i++ )
{
mountpoint_t* mp = mtable->mounts + i;
if ( mp->ino != inode->ino || mp->dev != inode->dev )
continue;
if ( out_index )
*out_index = i;
result_inode = mp->inode;
}
return result_inode;
}
static Ref LookupMount(Ref inode)
{
Ref mtable = CurrentProcess()->GetMTable();
ScopedLock mtable_lock(&mtable->mtablelock);
return LookupMountUnlocked(inode);
}
Vnode::Vnode(Ref inode, Ref mountedat, ino_t rootino, dev_t rootdev)
{
for ( Ref tmp = mountedat; tmp; tmp = tmp->mountedat )
assert(tmp != this);
this->inode = inode;
this->mountedat = mountedat;
this->rootino = rootino;
this->rootdev = rootdev;
this->ino = inode->ino;
this->dev = inode->dev;
this->type = inode->type;
}
Vnode::~Vnode()
{
}
Ref Vnode::open(ioctx_t* ctx, const char* filename, int flags, mode_t mode)
{
bool dotdot = strcmp(filename, "..") == 0;
// Prevent escaping the root filesystem.
if ( dotdot )
{
Ref root = CurrentProcess()->GetRoot();
if ( root->ino == ino && root->dev == dev )
return Ref(this);
}
// Handle transition across filesystem mount points.
bool isroot = inode->ino == rootino && inode->dev == rootdev;
if ( isroot && dotdot && mountedat )
return mountedat;
// Move within the current filesystem.
Ref retinode = inode->open(ctx, filename, flags, mode);
if ( !retinode )
return Ref(NULL);
Ref retmountedat = mountedat;
ino_t retrootino = rootino;
dev_t retrootdev = rootdev;
// Check whether we moved into a filesystem mount point.
Ref mounted = LookupMount(retinode);
if ( mounted )
{
retinode = mounted;
retmountedat = Ref(this);
retrootino = mounted->ino;
retrootdev = mounted->dev;
}
return Ref(new Vnode(retinode, retmountedat, retrootino, retrootdev));
}
int Vnode::fsm_fsbind(ioctx_t* ctx, Ref target, int flags)
{
(void) ctx;
if ( flags & ~(0) )
return errno = EINVAL, -1;
Ref mtable = CurrentProcess()->GetMTable();
if ( !mtable->AddMount(ino, dev, target->inode, true) )
return -1;
return 0;
}
Ref Vnode::fsm_mount(ioctx_t* ctx,
const char* filename,
const struct stat* rootst_ptr,
int flags)
{
if ( flags & ~(0) )
return Ref(NULL);
if ( !strcmp(filename, ".") || !strcmp(filename, "..") )
return errno = EINVAL, Ref(NULL);
struct stat rootst;
if ( !ctx->copy_from_src(&rootst, rootst_ptr, sizeof(struct stat)) )
return Ref(NULL);
Ref normal_inode = inode->open(ctx, filename, O_READ, 0);
if ( !normal_inode )
return Ref(NULL);
Ref root_inode;
Ref server_inode;
if ( !UserFS::Bootstrap(&root_inode, &server_inode, &rootst) )
return Ref(NULL);
Ref server_vnode(new Vnode(server_inode, Ref(NULL), 0, 0));
if ( !server_vnode )
return Ref(NULL);
Ref mtable = CurrentProcess()->GetMTable();
ScopedLock lock(&mtable->mtablelock);
if ( Ref mounted = LookupMountUnlocked(normal_inode) )
normal_inode = mounted;
if ( !mtable->AddMountUnlocked(normal_inode->ino, normal_inode->dev, root_inode, false) )
return Ref(NULL);
return server_vnode;
}
int Vnode::unmount(ioctx_t* ctx, const char* filename, int flags)
{
if ( flags & ~(UNMOUNT_FORCE | UNMOUNT_DETACH) )
return errno = EINVAL, -1;
if ( !strcmp(filename, ".") || !strcmp(filename, "..") )
return errno = EINVAL, -1;
Ref normal_inode = inode->open(ctx, filename, O_READ, 0);
if ( !normal_inode )
return -1;
Ref mtable = CurrentProcess()->GetMTable();
ScopedLock mtable_lock(&mtable->mtablelock);
size_t mp_index;
if ( !LookupMountUnlocked(normal_inode, &mp_index) )
return errno = EINVAL, -1;
mountpoint_t* mp = mtable->mounts + mp_index;
Ref mp_inode = mp->inode;
bool mp_fsbind = mp->fsbind;
mp->inode.Reset();
for ( size_t n = mp_index; n < mtable->nummounts - 1; n++ )
{
mtable->mounts[n] = mtable->mounts[n+1];
mtable->mounts[n+1].inode.Reset();
}
mtable->nummounts--;
mtable_lock.Reset();
if ( !mp_fsbind )
{
// TODO: Implement the !UNMOUNT_DETACH case.
// TODO: Implement the UNMOUNT_FORCE case.
mp_inode->unmounted(ctx);
}
mp_inode.Reset();
return 0;
}
int Vnode::sync(ioctx_t* ctx)
{
return inode->sync(ctx);
}
int Vnode::stat(ioctx_t* ctx, struct stat* st)
{
return inode->stat(ctx, st);
}
int Vnode::statvfs(ioctx_t* ctx, struct statvfs* stvfs)
{
return inode->statvfs(ctx, stvfs);
}
int Vnode::chmod(ioctx_t* ctx, mode_t mode)
{
return inode->chmod(ctx, mode);
}
int Vnode::chown(ioctx_t* ctx, uid_t owner, gid_t group)
{
return inode->chown(ctx, owner, group);
}
int Vnode::truncate(ioctx_t* ctx, off_t length)
{
return inode->truncate(ctx, length);
}
off_t Vnode::lseek(ioctx_t* ctx, off_t offset, int whence)
{
return inode->lseek(ctx, offset, whence);
}
ssize_t Vnode::read(ioctx_t* ctx, uint8_t* buf, size_t count)
{
return inode->read(ctx, buf, count);
}
ssize_t Vnode::pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off)
{
return inode->pread(ctx, buf, count, off);
}
ssize_t Vnode::write(ioctx_t* ctx, const uint8_t* buf, size_t count)
{
return inode->write(ctx, buf, count);
}
ssize_t Vnode::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off)
{
return inode->pwrite(ctx, buf, count, off);
}
int Vnode::utimens(ioctx_t* ctx, const struct timespec* atime,
const struct timespec* ctime,
const struct timespec* mtime)
{
return inode->utimens(ctx, atime, ctime, mtime);
}
int Vnode::isatty(ioctx_t* ctx)
{
return inode->isatty(ctx);
}
ssize_t Vnode::readdirents(ioctx_t* ctx, struct kernel_dirent* dirent,
size_t size, off_t start, size_t count)
{
return inode->readdirents(ctx, dirent, size, start, count);
}
int Vnode::mkdir(ioctx_t* ctx, const char* filename, mode_t mode)
{
return inode->mkdir(ctx, filename, mode);
}
int Vnode::unlink(ioctx_t* ctx, const char* filename)
{
return inode->unlink(ctx, filename);
}
int Vnode::rmdir(ioctx_t* ctx, const char* filename)
{
return inode->rmdir(ctx, filename);
}
int Vnode::link(ioctx_t* ctx, const char* filename, Ref node)
{
if ( node->inode->dev != inode->dev ) { errno = EXDEV; return -1; }
return inode->link(ctx, filename, node->inode);
}
int Vnode::symlink(ioctx_t* ctx, const char* oldname, const char* filename)
{
return inode->symlink(ctx, oldname, filename);
}
int Vnode::rename_here(ioctx_t* ctx, Ref from, const char* oldname,
const char* newname)
{
if ( from->dev != dev )
return errno = EXDEV, -1;
// TODO: Force the same mount point here, like Linux does.
return inode->rename_here(ctx, from->inode, oldname, newname);
}
ssize_t Vnode::readlink(ioctx_t* ctx, char* buf, size_t bufsiz)
{
return inode->readlink(ctx, buf, bufsiz);
}
int Vnode::fsbind(ioctx_t* /*ctx*/, Vnode* /*node*/, int /*flags*/)
{
// TODO: Support binding in namespaces.
errno = ENOSYS;
return -1;
}
int Vnode::tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp)
{
return inode->tcgetwincurpos(ctx, wcp);
}
int Vnode::tcgetwinsize(ioctx_t* ctx, struct winsize* ws)
{
return inode->tcgetwinsize(ctx, ws);
}
int Vnode::tcsetpgrp(ioctx_t* ctx, pid_t pgid)
{
return inode->tcsetpgrp(ctx, pgid);
}
pid_t Vnode::tcgetpgrp(ioctx_t* ctx)
{
return inode->tcgetpgrp(ctx);
}
int Vnode::settermmode(ioctx_t* ctx, unsigned mode)
{
return inode->settermmode(ctx, mode);
}
int Vnode::gettermmode(ioctx_t* ctx, unsigned* mode)
{
return inode->gettermmode(ctx, mode);
}
int Vnode::poll(ioctx_t* ctx, PollNode* node)
{
return inode->poll(ctx, node);
}
Ref Vnode::accept(ioctx_t* ctx, uint8_t* addr, size_t* addrlen, int flags)
{
Ref retinode = inode->accept(ctx, addr, addrlen, flags);
if ( !retinode )
return Ref();
return Ref(new Vnode(retinode, Ref(), retinode->ino, retinode->dev));
}
int Vnode::bind(ioctx_t* ctx, const uint8_t* addr, size_t addrlen)
{
return inode->bind(ctx, addr, addrlen);
}
int Vnode::connect(ioctx_t* ctx, const uint8_t* addr, size_t addrlen)
{
return inode->connect(ctx, addr, addrlen);
}
int Vnode::listen(ioctx_t* ctx, int backlog)
{
return inode->listen(ctx, backlog);
}
ssize_t Vnode::recv(ioctx_t* ctx, uint8_t* buf, size_t count, int flags)
{
return inode->recv(ctx, buf, count, flags);
}
ssize_t Vnode::send(ioctx_t* ctx, const uint8_t* buf, size_t count, int flags)
{
return inode->send(ctx, buf, count, flags);
}
int Vnode::getsockopt(ioctx_t* ctx, int level, int option_name,
void* option_value, size_t* option_size_ptr)
{
return inode->getsockopt(ctx, level, option_name, option_value, option_size_ptr);
}
int Vnode::setsockopt(ioctx_t* ctx, int level, int option_name,
const void* option_value, size_t option_size)
{
return inode->setsockopt(ctx, level, option_name, option_value, option_size);
}
ssize_t Vnode::tcgetblob(ioctx_t* ctx, const char* name, void* buffer, size_t count)
{
return inode->tcgetblob(ctx, name, buffer, count);
}
ssize_t Vnode::tcsetblob(ioctx_t* ctx, const char* name, const void* buffer, size_t count)
{
return inode->tcsetblob(ctx, name, buffer, count);
}
} // namespace Sortix