// -*- C++ -*- (c) 2015 Jiří Weiser #include <iostream> #include <cerrno> #include <cstdarg> #include <cstdlib> #include <cstring> #include <bits/types.h> #include <sys/stat.h> #include <unistd.h> #include <dirent.h> #include <fcntl.h> #include "fs-manager.h" #ifdef __divine__ # define FS_MASK __divine_interrupt_mask(); # define FS_MALLOC( x ) __divine_malloc( x ) # define FS_PROBLEM( msg ) __divine_problem( 1, msg ) #else # define FS_MASK # define FS_MALLOC( x ) std::malloc( x ) # define FS_PROBLEM( msg ) #endif using divine::fs::Error; using divine::fs::vfs; extern "C" { static void _initStat( struct stat *buf ) { buf->st_dev = 0; buf->st_rdev = 0; buf->st_atime = 0; buf->st_mtime = 0; buf->st_ctime = 0; } static int _fillStat( const divine::fs::Node item, struct stat *buf ) { if ( !item ) return -1; _initStat( buf ); buf->st_ino = item->ino(); buf->st_mode = item->mode(); buf->st_nlink = item.use_count(); buf->st_size = item->size(); buf->st_uid = item->uid(); buf->st_gid = item->gid(); buf->st_blksize = 512; buf->st_blocks = ( buf->st_size + 1 ) / buf->st_blksize; return 0; } int openat( int dirfd, const char *path, int flags, ... ) { FS_MASK using namespace divine::fs::flags; divine::fs::Flags< Open > f = Open::NoFlags; mode_t m = 0; // special behaviour - check for access rights but do not grant them if ( ( flags & 3 ) == 3) f |= Open::NoAccess; if ( flags & O_RDWR ) { f |= Open::Read; f |= Open::Write; } else if ( flags & O_WRONLY ) f |= Open::Write; else f |= Open::Read; if ( flags & O_CREAT ) { f |= divine::fs::flags::Open::Create; va_list args; va_start( args, flags ); if ( !args ) FS_PROBLEM( "flag O_CREAT has been specified but mode was not set" ); m = va_arg( args, mode_t ); va_end( args ); } if ( flags & O_EXCL ) f |= divine::fs::flags::Open::Excl; if ( flags & O_TRUNC ) f |= divine::fs::flags::Open::Truncate; if ( flags & O_APPEND ) f |= divine::fs::flags::Open::Append; if ( flags & O_NOFOLLOW ) f |= divine::fs::flags::Open::SymNofollow; if ( dirfd == AT_FDCWD ) dirfd = divine::fs::CURRENT_DIRECTORY; try { return vfs.instance().openFileAt( dirfd, path, f, m ); } catch ( Error & ) { return -1; } } int open( const char *path, int flags, ... ) { FS_MASK int mode = 0; if ( flags & O_CREAT ) { va_list args; va_start( args, flags ); if ( !args ) FS_PROBLEM( "flag O_CREAT has been specified but mode was not set" ); mode = va_arg( args, int ); va_end( args ); } return openat( AT_FDCWD, path, flags, mode ); } int creat( const char *path, mode_t mode ) { FS_MASK return openat( AT_FDCWD, path, O_CREAT | O_WRONLY | O_TRUNC, mode ); } int close( int fd ) { FS_MASK try { vfs.instance().closeFile( fd ); return 0; } catch ( Error & ) { return -1; } } ssize_t write( int fd, const void *buf, size_t count ) { FS_MASK try { auto f = vfs.instance().getFile( fd ); return f->write( buf, count ); } catch ( Error & ) { return -1; } } ssize_t pwrite( int fd, const void *buf, size_t count, off_t offset ) { FS_MASK try { auto f = vfs.instance().getFile( fd ); size_t savedOffset = f->offset(); f->offset( offset ); auto d = divine::fs::utils::make_defer( [&]{ f->offset( savedOffset ); } ); return f->write( buf, count ); } catch ( Error & ) { return -1; } } ssize_t read( int fd, void *buf, size_t count ) { FS_MASK try { auto f = vfs.instance().getFile( fd ); return f->read( buf, count ); } catch ( Error & ) { return -1; } } ssize_t pread( int fd, void *buf, size_t count, off_t offset ) { FS_MASK try { auto f = vfs.instance().getFile( fd ); size_t savedOffset = f->offset(); f->offset( offset ); auto d = divine::fs::utils::make_defer( [&]{ f->offset( savedOffset ); } ); return f->read( buf, count ); } catch ( Error & ) { return -1; } } int mkdirat( int dirfd, const char *path, mode_t mode ) { FS_MASK if ( dirfd == AT_FDCWD ) dirfd = divine::fs::CURRENT_DIRECTORY; try { vfs.instance().createDirectoryAt( dirfd, path, mode ); return 0; } catch ( Error & ) { return -1; } } int mkdir( const char *path, mode_t mode ) { FS_MASK return mkdirat( AT_FDCWD, path, mode ); } int mkfifoat( int dirfd, const char *path, mode_t mode ) { if ( dirfd == AT_FDCWD ) dirfd = divine::fs::CURRENT_DIRECTORY; try { vfs.instance().createFifoAt( dirfd, path, mode ); return 0; } catch ( Error & ) { return -1; } } int mkfifo( const char *path, mode_t mode ) { FS_MASK return mkfifoat( AT_FDCWD, path, mode ); } int unlink( const char *path ) { FS_MASK try { vfs.instance().removeFile( path ); return 0; } catch ( Error & ) { return -1; } } int rmdir( const char *path ) { FS_MASK try { vfs.instance().removeDirectory( path ); return 0; } catch ( Error & ) { return -1; } } int unlinkat( int dirfd, const char *path, int flags ) { FS_MASK divine::fs::flags::At f; switch( flags ) { case 0: f = divine::fs::flags::At::NoFlags; break; case AT_REMOVEDIR: f = divine::fs::flags::At::RemoveDir; break; default: f = divine::fs::flags::At::Invalid; break; } if ( dirfd == AT_FDCWD ) dirfd = divine::fs::CURRENT_DIRECTORY; try { vfs.instance().removeAt( dirfd, path, f ); return 0; } catch ( Error & ) { return -1; } } off_t lseek( int fd, off_t offset, int whence ) { FS_MASK try { divine::fs::Seek w = divine::fs::Seek::Undefined; switch( whence ) { case SEEK_SET: w = divine::fs::Seek::Set; break; case SEEK_CUR: w = divine::fs::Seek::Current; break; case SEEK_END: w = divine::fs::Seek::End; break; } return vfs.instance().lseek( fd, offset, w ); } catch ( Error & ) { return -1; } } int dup( int fd ) { FS_MASK try { return vfs.instance().duplicate( fd ); } catch ( Error & ) { return -1; } } int dup2( int oldfd, int newfd ) { FS_MASK try { return vfs.instance().duplicate2( oldfd, newfd ); } catch ( Error & ) { return -1; } } int symlinkat( const char *target, int dirfd, const char *linkpath ) { FS_MASK if ( dirfd == AT_FDCWD ) dirfd = divine::fs::CURRENT_DIRECTORY; try { vfs.instance().createSymLinkAt( dirfd, linkpath, target ); return 0; } catch ( Error & ) { return -1; } } int symlink( const char *target, const char *linkpath ) { FS_MASK return symlinkat( target, AT_FDCWD, linkpath ); } int linkat( int olddirfd, const char *target, int newdirfd, const char *linkpath, int flags ) { FS_MASK if ( olddirfd == AT_FDCWD ) olddirfd = divine::fs::CURRENT_DIRECTORY; if ( newdirfd == AT_FDCWD ) newdirfd = divine::fs::CURRENT_DIRECTORY; divine::fs::Flags< divine::fs::flags::At > fl = divine::fs::flags::At::NoFlags; if ( flags & AT_SYMLINK_FOLLOW ) fl |= divine::fs::flags::At::SymFollow; if ( ( flags | AT_SYMLINK_FOLLOW ) != AT_SYMLINK_FOLLOW ) fl |= divine::fs::flags::At::Invalid; try { vfs.instance().createHardLinkAt( newdirfd, linkpath, olddirfd, target, fl ); return 0; } catch ( Error & ) { return -1; } } int link( const char *target, const char *linkpath ) { FS_MASK return linkat( AT_FDCWD, target, AT_FDCWD, linkpath, 0 ); } ssize_t readlinkat( int dirfd, const char *path, char *buf, size_t count ) { FS_MASK if ( dirfd == AT_FDCWD ) dirfd = divine::fs::CURRENT_DIRECTORY; try { return vfs.instance().readLinkAt( dirfd, path, buf, count ); } catch ( Error & ) { return -1; } } ssize_t readlink( const char *path, char *buf, size_t count ) { FS_MASK return readlinkat( AT_FDCWD, path, buf, count ); } int faccessat( int dirfd, const char *path, int mode, int flags ) { FS_MASK divine::fs::Flags< divine::fs::flags::Access > m = divine::fs::flags::Access::OK; if ( mode & R_OK ) m |= divine::fs::flags::Access::Read; if ( mode & W_OK ) m |= divine::fs::flags::Access::Write; if ( mode & X_OK ) m |= divine::fs::flags::Access::Execute; if ( ( mode | R_OK | W_OK | X_OK ) != ( R_OK | W_OK | X_OK ) ) m |= divine::fs::flags::Access::Invalid; if ( dirfd == AT_FDCWD ) dirfd = divine::fs::CURRENT_DIRECTORY; divine::fs::Flags< divine::fs::flags::At > fl = divine::fs::flags::At::NoFlags; if ( flags & AT_EACCESS ) fl |= divine::fs::flags::At::EffectiveID; if ( flags & AT_SYMLINK_NOFOLLOW ) fl |= divine::fs::flags::At::SymNofollow; if ( ( flags | AT_EACCESS | AT_SYMLINK_NOFOLLOW ) != ( AT_EACCESS | AT_SYMLINK_NOFOLLOW ) ) fl |= divine::fs::flags::At::Invalid; try { vfs.instance().accessAt( dirfd, path, m, fl ); return 0; } catch ( Error & ) { return -1; } } int access( const char *path, int mode ) { FS_MASK return faccessat( AT_FDCWD, path, mode, 0 ); } int stat( const char *path, struct stat *buf ) { FS_MASK try { auto item = vfs.instance().findDirectoryItem( path ); if ( !item ) throw Error( ENOENT ); return _fillStat( item, buf ); } catch ( Error & ) { return -1; } } int lstat( const char *path, struct stat *buf ) { FS_MASK try { auto item = vfs.instance().findDirectoryItem( path, false ); if ( !item ) throw Error( ENOENT ); return _fillStat( item, buf ); } catch ( Error & ) { return -1; } } int fstat( int fd, struct stat *buf ) { FS_MASK try { auto item = vfs.instance().getFile( fd ); return _fillStat( item->inode(), buf ); } catch ( Error & ) { return -1; } } mode_t umask( mode_t mask ) { FS_MASK mode_t result = vfs.instance().umask(); vfs.instance().umask( mask & 0777 ); return result; } int chdir( const char *path ) { FS_MASK try { vfs.instance().changeDirectory( path ); return 0; } catch( Error & ) { return -1; } } int fchdir( int dirfd ) { FS_MASK try { vfs.instance().changeDirectory( dirfd ); return 0; } catch( Error & ) { return -1; } } int fdatasync( int fd ) { FS_MASK try { vfs.instance().getFile( fd ); return 0; } catch ( Error & ) { return -1; } } int fsync( int fd ) { FS_MASK try { vfs.instance().getFile( fd ); return 0; } catch ( Error & ) { return -1; } } int ftruncate( int fd, off_t length ) { FS_MASK try { auto item = vfs.instance().getFile( fd ); if ( !item->flags().has( divine::fs::flags::Open::Write ) ) throw Error( EINVAL ); vfs.instance().truncate( item->inode(), length ); return 0; } catch ( Error & ) { return -1; } } int truncate( const char *path, off_t length ) { FS_MASK try { auto item = vfs.instance().findDirectoryItem( path ); vfs.instance().truncate( item, length ); return 0; } catch ( Error & ) { return -1; } } void swab( const void *_from, void *_to, ssize_t n ) { FS_MASK const char *from = reinterpret_cast< const char * >( _from ); char *to = reinterpret_cast< char * >( _to ); for ( ssize_t i = 0; i < n/2; ++i ) { *to = *(from + 1); *(to + 1) = *from; to += 2; from += 2; } } int isatty( int fd ) { FS_MASK try { vfs.instance().getFile( fd ); errno = EINVAL; } catch ( Error & ) { } return 0; } char *ttyname( int fd ) { FS_MASK try { vfs.instance().getFile( fd ); errno = ENOTTY; } catch ( Error & ) { } return nullptr; } int ttyname_r( int fd, char *buf, size_t count ) { FS_MASK try { vfs.instance().getFile( fd ); return ENOTTY; } catch ( Error &e ) { return e.code(); } } void sync( void ) {} int syncfs( int fd ) { FS_MASK try { vfs.instance().getFile( fd ); return 0; } catch ( Error & ) { return -1; } } int _FS_renameitemat( int olddirfd, const char *oldpath, int newdirfd, const char *newpath ) { FS_MASK if ( olddirfd == AT_FDCWD ) olddirfd = divine::fs::CURRENT_DIRECTORY; if ( newdirfd == AT_FDCWD ) newdirfd = divine::fs::CURRENT_DIRECTORY; try { vfs.instance().renameAt( newdirfd, newpath, olddirfd, oldpath ); return 0; } catch ( Error & ) { return -1; } } int _FS_renameitem( const char *oldpath, const char *newpath ) { FS_MASK return _FS_renameitemat( AT_FDCWD, oldpath, AT_FDCWD, newpath ); } int pipe( int pipefd[ 2 ] ) { FS_MASK try { std::tie( pipefd[ 0 ], pipefd[ 1 ] ) = vfs.instance().pipe(); return 0; } catch ( Error & ) { return -1; } } int fchmodeat( int dirfd, const char *path, mode_t mode, int flags ) { FS_MASK divine::fs::Flags< divine::fs::flags::At > fl = divine::fs::flags::At::NoFlags; if ( flags & AT_SYMLINK_NOFOLLOW ) fl |= divine::fs::flags::At::SymNofollow; if ( ( flags | AT_SYMLINK_NOFOLLOW ) != AT_SYMLINK_NOFOLLOW ) fl |= divine::fs::flags::At::Invalid; if ( dirfd == AT_FDCWD ) dirfd = divine::fs::CURRENT_DIRECTORY; try { vfs.instance().chmodAt( dirfd, path, mode, fl ); return 0; } catch ( Error & ) { return -1; } } int chmod( const char *path, mode_t mode ) { FS_MASK return fchmodeat( AT_FDCWD, path, mode, 0 ); } int fchmod( int fd, mode_t mode ) { FS_MASK try { vfs.instance().chmod( fd, mode ); return 0; } catch ( Error & ) { return -1; } } int alphasort( const struct dirent **a, const struct dirent **b ) { return std::strcoll( (*a)->d_name, (*b)->d_name ); } int closedir( DIR *dirp ) { FS_MASK try { vfs.instance().closeDirectory( dirp ); return 0; } catch ( Error & ) { return -1; } } int dirfd( DIR *dirp ) { FS_MASK try { return vfs.instance().getDirectory( dirp )->fd(); } catch ( Error & ) { return -1; } } DIR *fdopendir( int fd ) { FS_MASK try { return vfs.instance().openDirectory( fd ); } catch ( Error & ) { return nullptr; } } DIR *opendir( const char *path ) { FS_MASK using namespace divine::fs::flags; divine::fs::Flags< Open > f = Open::Read; try { int fd = vfs.instance().openFileAt( divine::fs::CURRENT_DIRECTORY, path, f, 0 ); return vfs.instance().openDirectory( fd ); } catch ( Error & ) { return nullptr; } } struct dirent *readdir( DIR *dirp ) { FS_MASK static struct dirent entry; try { auto dir = vfs.instance().getDirectory( dirp ); auto ent = dir->get(); if ( !ent ) return nullptr; entry.d_ino = ent->ino(); char *x = std::copy( ent->name().begin(), ent->name().end(), entry.d_name ); *x = '\0'; dir->next(); return &entry; } catch ( Error & ) { return nullptr; } } int readdir_r( DIR *dirp, struct dirent *entry, struct dirent **result ) { FS_MASK try { auto dir = vfs.instance().getDirectory( dirp ); auto ent = dir->get(); if ( ent ) { entry->d_ino = ent->ino(); char *x = std::copy( ent->name().begin(), ent->name().end(), entry->d_name ); *x = '\0'; *result = entry; dir->next(); } else *result = nullptr; return 0; } catch ( Error &e ) { return e.code(); } } void rewinddir( DIR *dirp ) { FS_MASK int e = errno; try { vfs.instance().getDirectory( dirp )->rewind(); } catch ( Error & ) { errno = e; } } int scandir( const char *path, struct dirent ***namelist, int (*filter)( const struct dirent * ), int (*compare)( const struct dirent **, const struct dirent ** )) { FS_MASK using namespace divine::fs::flags; divine::fs::Flags< Open > f = Open::Read; DIR *dirp = nullptr; try { int length = 0; int fd = vfs.instance().openFileAt( divine::fs::CURRENT_DIRECTORY, path, f, 0 ); dirp = vfs.instance().openDirectory( fd ); struct dirent **entries = nullptr; struct dirent *workingEntry = (struct dirent *)FS_MALLOC( sizeof( struct dirent ) ); while ( true ) { auto dir = vfs.instance().getDirectory( dirp ); auto ent = dir->get(); if ( !ent ) break; workingEntry->d_ino = ent->ino(); char *x = std::copy( ent->name().begin(), ent->name().end(), workingEntry->d_name ); *x = '\0'; dir->next(); if ( filter && !filter( workingEntry ) ) continue; struct dirent **newEntries = (struct dirent **)FS_MALLOC( ( length + 1 ) * sizeof( struct dirent * ) ); if ( length ) std::memcpy( newEntries, entries, length * sizeof( struct dirent * ) ); std::swap( entries, newEntries ); std::free( newEntries ); entries[ length ] = workingEntry; workingEntry = (struct dirent *)FS_MALLOC( sizeof( struct dirent ) ); ++length; } vfs.instance().closeDirectory( dirp ); typedef int( *cmp )( const void *, const void * ); std::qsort( entries, length, sizeof( struct dirent * ), reinterpret_cast< cmp >( compare ) ); return 0; } catch ( Error & ) { if ( dirp ) vfs.instance().closeDirectory( dirp ); return -1; } } long telldir( DIR *dirp ) { FS_MASK int e = errno; try { return vfs.instance().getDirectory( dirp )->tell(); } catch ( Error & ) { return -1; } } void seekdir( DIR *dirp, long offset ) { FS_MASK int e = errno; try { vfs.instance().getDirectory( dirp )->seek( offset ); } catch ( Error & ) { errno = e; } } } // extern "C"