/* * Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * 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); if ( retinode->type & S_IFFACTORY && !(retinode->type & S_IFFACTORY_NOSTAT && flags & O_IS_STAT) ) { retinode = retinode->factory(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 = ENOMOUNT, -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; } bool Vnode::pass() { return inode->pass(); } void Vnode::unpass() { inode->unpass(); } 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::readv(ioctx_t* ctx, const struct iovec* iov, int iovcnt) { return inode->readv(ctx, iov, iovcnt); } 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::preadv(ioctx_t* ctx, const struct iovec* iov, int iovcnt, off_t off) { return inode->preadv(ctx, iov, iovcnt, off); } ssize_t Vnode::write(ioctx_t* ctx, const uint8_t* buf, size_t count) { return inode->write(ctx, buf, count); } ssize_t Vnode::writev(ioctx_t* ctx, const struct iovec* iov, int iovcnt) { return inode->writev(ctx, iov, iovcnt); } ssize_t Vnode::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off) { return inode->pwrite(ctx, buf, count, off); } ssize_t Vnode::pwritev(ioctx_t* ctx, const struct iovec* iov, int iovcnt, off_t off) { return inode->pwritev(ctx, iov, iovcnt, off); } int Vnode::utimens(ioctx_t* ctx, const struct timespec* times) { return inode->utimens(ctx, times); } int Vnode::isatty(ioctx_t* ctx) { return inode->isatty(ctx); } ssize_t Vnode::readdirents(ioctx_t* ctx, struct dirent* dirent, size_t size, off_t start) { return inode->readdirents(ctx, dirent, size, start); } 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::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg) { return inode->ioctl(ctx, cmd, arg); } 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::accept4(ioctx_t* ctx, uint8_t* addr, size_t* addrlen, int flags) { Ref retinode = inode->accept4(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::recvmsg(ioctx_t* ctx, struct msghdr* msg, int flags) { return inode->recvmsg(ctx, msg, flags); } ssize_t Vnode::send(ioctx_t* ctx, const uint8_t* buf, size_t count, int flags) { return inode->send(ctx, buf, count, flags); } ssize_t Vnode::sendmsg(ioctx_t* ctx, const struct msghdr* msg, int flags) { return inode->sendmsg(ctx, msg, 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); } int Vnode::tcdrain(ioctx_t* ctx) { return inode->tcdrain(ctx); } int Vnode::tcflow(ioctx_t* ctx, int action) { return inode->tcflow(ctx, action); } int Vnode::tcflush(ioctx_t* ctx, int queue_selector) { return inode->tcflush(ctx, queue_selector); } int Vnode::tcgetattr(ioctx_t* ctx, struct termios* tio) { return inode->tcgetattr(ctx, tio); } pid_t Vnode::tcgetsid(ioctx_t* ctx) { return inode->tcgetsid(ctx); } int Vnode::tcsendbreak(ioctx_t* ctx, int duration) { return inode->tcsendbreak(ctx, duration); } int Vnode::tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio) { return inode->tcsetattr(ctx, actions, tio); } int Vnode::shutdown(ioctx_t* ctx, int how) { return inode->shutdown(ctx, how); } int Vnode::getpeername(ioctx_t* ctx, uint8_t* addr, size_t* addrsize) { return inode->getpeername(ctx, addr, addrsize); } int Vnode::getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize) { return inode->getsockname(ctx, addr, addrsize); } } // namespace Sortix