// -*- C++ -*- (c) 2015 Jiří Weiser #include #include #include "fs-inode.h" #include "fs-utils.h" #include "fs-constants.h" #ifndef _FS_DIRECTORY_H_ #define _FS_DIRECTORY_H_ namespace divine { namespace fs { struct DirectoryEntry { DirectoryEntry( utils::String name, Node inode ) : _name( std::move( name ) ), _inode( std::move( inode ) ), _weak( false ) {} DirectoryEntry( utils::String name, WeakNode inode ) : _name( std::move( name ) ), _inode( std::move( inode ) ), _weak( true ) {} DirectoryEntry( const DirectoryEntry &other ) : _name( other.name() ), _weak( other._weak ) { if ( _weak ) new (&_inode.weak) WeakNode( other._inode.weak ); else new (&_inode.strong) Node( other._inode.strong ); } DirectoryEntry( DirectoryEntry &&other ) : _name( std::move( other._name ) ), _weak( other._weak ) { if ( _weak ) new (&_inode.weak) WeakNode( std::move( other._inode.weak ) ); else new (&_inode.strong) Node( std::move( other._inode.strong ) ); } ~DirectoryEntry() { if ( _weak ) _inode.weak.~WeakNode(); else _inode.strong.~Node(); } DirectoryEntry &operator=( DirectoryEntry other ) { swap( other ); return *this; } utils::String &name() { return _name; } const utils::String &name() const { return _name; } Node inode() { return _weak ? _inode.weak.lock() : _inode.strong; } const Node inode() const { return _weak ? _inode.weak.lock() : _inode.strong; } void swap( DirectoryEntry &other ) { using std::swap; swap( _name, other._name ); if ( _weak && other._weak ) _inode.weak.swap( other._inode.weak ); else if ( !_weak && !other._weak ) _inode.strong.swap( other._inode.strong ); else if ( _weak ) { WeakNode tmp( std::move( _inode.weak ) ); new (&_inode.strong) Node( std::move( other._inode.strong ) ); new (&other._inode.weak) WeakNode( std::move( tmp ) ); } else { WeakNode tmp( std::move( other._inode.weak ) ); new (&other._inode.strong) Node( std::move( _inode.strong ) ); new (&_inode.weak) WeakNode( std::move( tmp ) ); } swap( _weak, other._weak ); } private: utils::String _name; union _U { Node strong; WeakNode weak; _U() {} _U( Node &&node ) : strong( std::move( node ) ) {} _U( WeakNode &&node ) : weak( std::move( node ) ) {} _U( const _U & ) {} _U( _U && ) {} ~_U() {} } _inode; bool _weak; }; struct DirectoryItemLabel { DirectoryItemLabel( const DirectoryEntry &entry ) : _name( entry.name() ), _ino( entry.inode()->ino() ) {} DirectoryItemLabel( const DirectoryItemLabel & ) = default; DirectoryItemLabel( DirectoryItemLabel && ) = default; DirectoryItemLabel &operator=( DirectoryItemLabel other ) { _name.swap( other._name ); _ino = other._ino; return *this; } const utils::String &name() const { return _name; } unsigned ino() const { return _ino; } private: utils::String _name; unsigned _ino; }; struct Directory : DataItem { using Items = utils::Vector< DirectoryEntry >; Directory( WeakNode self, WeakNode parent = WeakNode{} ) : _items{ DirectoryEntry{ ".", self }, DirectoryEntry{ "..", !parent.expired() ? parent : self } } {} size_t size() const override { return _items.size(); } void create( utils::String name, Node inode ) { if ( name.size() > FILE_NAME_LIMIT ) throw Error( ENAMETOOLONG ); _insertItem( DirectoryEntry( std::move( name ), std::move( inode ) ) ); } Node find( const utils::String &name ) { auto position = _findItem( name ); if ( position == _items.end() || name != position->name() ) return Node(); return position->inode(); } void replaceEntry( const utils::String &name, Node node ) { auto position = _findItem( name ); if ( position == _items.end() || name != position->name() ) throw Error( ENOENT ); *position = DirectoryEntry( name, node ); } template< typename T > T *find( const utils::String &name ) { Node node = find( name ); if ( !node ) return nullptr; return node->data()->as< T >(); } void remove( const utils::String &name ) { auto position = _findItem( name ); if ( position == _items.end() || name != position->name() ) throw Error( ENOENT ); if ( position->inode()->mode().isDirectory() ) throw Error( EISDIR ); _items.erase( position ); } void removeDirectory( const utils::String &name ) { auto position = _findItem( name ); if ( position == _items.end() || name != position->name() ) throw Error( ENOENT ); if ( !position->inode()->mode().isDirectory() ) throw Error( ENOTDIR ); if ( position->inode()->size() != 2 ) throw Error( ENOTEMPTY ); _items.erase( position ); } void forceRemove( const utils::String &name ) { auto position = _findItem( name ); if ( position != _items.end() && name == position->name() ) _items.erase( position ); } Items::iterator begin() { return _items.begin(); } Items::iterator end() { return _items.end(); } Items::const_iterator begin() const { return _items.begin(); } Items::const_iterator end() const { return _items.end(); } private: void _insertItem( DirectoryEntry &&entry ) { auto position = _findItem( entry.name() ); if ( position == _items.end() ) { _items.emplace_back( std::move( entry ) ); return; } if ( position->name() != entry.name() ) { _items.insert( position, std::move( entry ) ); return; } throw Error( EEXIST ); } Items::iterator _findItem( const utils::String &name ) { return std::lower_bound( _items.begin(), _items.end(), name, []( const DirectoryEntry &entry, const utils::String &name ) { return entry.name() < name; } ); } Items _items; }; } // namespace fs } // namespace divine #endif