// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- /* * (c) 2018, 2019 Petr Ročkai * * 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. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inode.hpp" #include "flags.hpp" #include "file.hpp" #include "directory.hpp" #include "fd.hpp" #include "syscall.hpp" #include namespace __dios::fs { /* This is a part of the DiOS config stack which provides filesystem functionality. - Extends process-local information (struct Process) with filesystem data. - Overrideds fork to keep files open. - In setup initializes stdin (empty, or with a specified content if passed to it), creates stdout & stder, and imports filesystem snapshot. */ template < typename Next > struct VFS: Syscall, Next { struct Process : Next::Process, fs::ProcessInfo {}; Node root() override { return _root; } ProcessInfo &proc() override { return *static_cast< Process * >( this->current_process() ); } int8_t nondet_byte( int level ) override { return Next::nondet_i8( level ); } /* During the boot process the configuration can not be fetched * and causes a NULL pointer dereference. Therefore this workaround. */ bool nondet_fail() override { return Next::get_fault_config( _DiOS_SF_Vfs ) == _DiOS_FC_SimFail; } using Syscall::open; using Syscall::openat; using Syscall::creat; using Syscall::close; using Syscall::dup; using Syscall::dup2; using Syscall::fcntl; using Syscall::chmod; using Syscall::fchmod; using Syscall::fchmodat; using Syscall::read; using Syscall::write; using Syscall::mkdir; using Syscall::mkdirat; using Syscall::mknod; using Syscall::mknodat; using Syscall::truncate; using Syscall::ftruncate; using Syscall::lseek; using Syscall::unlink; using Syscall::rmdir; using Syscall::unlinkat; using Syscall::fdatasync; using Syscall::fsync; using Syscall::syncfs; using Syscall::sync; using Syscall::fstatat; using Syscall::stat; using Syscall::lstat; using Syscall::fstat; using Syscall::fstatfs; using Syscall::statfs; using Syscall::linkat; using Syscall::link; using Syscall::symlinkat; using Syscall::symlink; using Syscall::readlinkat; using Syscall::readlink; using Syscall::umask; using Syscall::renameat; using Syscall::rename; using Syscall::chdir; using Syscall::fchdir; using Syscall::getcwd; using Syscall::__getcwd; using Syscall::faccessat; using Syscall::access; using Syscall::pipe; using Syscall::socketpair; using Syscall::socket; using Syscall::getsockname; using Syscall::getpeername; using Syscall::connect; using Syscall::listen; using Syscall::bind; using Syscall::accept; using Syscall::accept4; using Syscall::send; using Syscall::sendto; using Syscall::recv; using Syscall::recvfrom; void sysfork( pid_t *child ) { Next::sysfork( child ); Process *proc = static_cast< Process * >( Next::findProcess( *child ) ); if ( proc ) for ( auto &fd : proc->_fds ) if ( fd.inode() ) fd.inode()->open(); /* update refcount */ return proc; } template< typename Setup > void setup( Setup s ) { traceAlias< VFS >( "{VFS}" ); for ( auto env = s.env ; env->key; env++ ) if ( !strcmp( env->key, "vfs.stdin" ) ) { //look for nondet file prefix std::string_view content( env->value, env->size ); if ( content.rfind( "#!nd", 0 ) == 0 ) { auto begin = content.begin() + 4; auto end = content.end(); // read name of the starting rule Array< char > rule; while ( begin != end && isspace( *begin ) ) begin++; if ( !isalpha( *begin ) ) __dios_fault( _DiOS_F_Config, "invalid starting rule name for a grammar, first char must be a letter" ); while ( begin != end && !isspace( *begin ) ) { if ( !isalnum( *begin ) ) __dios_fault( _DiOS_F_Config, "invalid starting rule name for a grammar, only alnum chars are allowed" ); rule.push_back( *begin ); begin++; } //move to the start of the grammar definition while ( begin != end && isspace( *begin ) ) begin++; _stdio[ 0 ] = new nondet_stdin( this, begin, end, rule ); } else _stdio[ 0 ] = new StandardInput( env->value, env->size ); } if ( !_stdio[ 0 ] ) _stdio[ 0 ] = new StandardInput(); _stdio[ 1 ] = make_tracefile( s.opts, "stdout" ); _stdio[ 2 ] = make_tracefile( s.opts, "stderr" ); for ( int i = 0; i <= 2; ++i ) { _stdio[ i ]->mode( S_IFREG | S_IRUSR ); s.proc1->_fds.emplace_back( _stdio[ i ], i ? O_WRONLY : O_RDONLY ); s.proc1->_fd_refs.emplace_back( i ); } s.proc1->_umask = S_IWGRP | S_IWOTH; _root = new Directory(); _root->mode( S_IFDIR | ACCESSPERMS ); _root->link(); s.proc1->_cwd = _root; auto dev = new Directory( _root ); dev->mode( S_IFDIR | ACCESSPERMS ); static_cast< Directory * >( _root )->create( "dev", dev, true ); auto null = new NullFile(); null->mode( S_IWUSR | S_IWGRP | S_IWOTH | S_IFCHR ); dev->create( "null", null, true ); import( s.env ); Next::setup( s ); } void getHelp( ArrayMap< std::string_view, HelpOption >& options ) { const char *opt1 = "{stdout|stderr}"; if ( options.find( opt1 ) != options.end() ) { __dios_trace_f( "Option %s already present", opt1 ); __dios_fault( _DiOS_F_Config, "Option conflict" ); }; options[ { opt1 } ] = { "specify how to treat program output", { "notrace - ignore the stream", "unbuffered - trace each write", "trace - trace after each newline (default)"} }; Next::getHelp( options ); } static Node make_tracefile( SysOpts& o, std::string_view stream ) { auto r = extract_opt( stream, o ); if ( r.empty() || r == "trace" ) return new VmBuffTraceFile(); if ( r == "unbuffered" ) return new VmTraceFile(); if ( r == "notrace" ) return new NullFile(); __dios_trace_f( "Invalid configuration for file %.*s", int( stream.size() ), stream.begin() ); __vm_ctl_flag( 0, _VM_CF_Error ); return new NullFile(); } private: /* helper methods */ Node _root; std::array< Node, 3 > _stdio; }; }