Internally Safe Filesystem win_xept.hpp - a basic windows api exception
#include &quot;../utl_macro.hpp&quot;

namespace xept
{
    using win_t = struct utl_exception // utl_exception randomly generates a name
                                       // this hides exception names from rtti
    {
        DWORD e = GetLastError( );
    };
}

ramfile.hpp - an interface for any type of file where the data is needed in ram
#pragma once

class ramfile_t
{
public:
    virtual std::uintmax_t size( ) = 0;
    virtual std::vector&lt; std::byte &gt; raw( ) = 0;
    virtual void overwrite( std::vector&lt; std::byte &gt; const &amp; raw,
                            std::size_t const &amp; off = 0 ) = 0;
    virtual void set( std::vector&lt; std::byte &gt; const &amp; raw ) = 0;
};

ramfile_xept.hpp - the basic ramfile exception
#include &quot;../win_xept.hpp&quot;

namespace xept
{
    using ramfile_t = struct utl_exception:
        win_t
    { };
}

ramfile_disk.hpp - basic file for disk
class ramfile_disk_t:
    public ramfile_t
{
public:
    ramfile_disk_t( std::filesystem::path path ):
        path( std::move( path ) ),
        file_guard( file_locks[ this-&gt;path.c_str( ) ] )
    {
        assert( path.is_absolute( ) );
    }

    std::uintmax_t size( ) final
    {
        return std::filesystem::file_size( path );
    }

    std::vector&lt; std::byte &gt; raw( ) final
    {
        std::basic_ifstream&lt; std::byte &gt; file( path,
                                               std::ofstream::binary );

        assert( file.is_open( )
                &amp;&amp; file.good( ) );

        file.seekg( 0 );

        return std::vector&lt; std::byte &gt;( std::istreambuf_iterator&lt; std::byte &gt;( file ),
                                         std::istreambuf_iterator&lt; std::byte &gt;( ) );
    }

    void set( std::vector&lt; std::byte &gt; const &amp; data ) final
    {
        std::basic_ofstream&lt; std::byte &gt; file( path,
                                               std::ofstream::binary | std::ofstream::trunc );

        assert( file.is_open( )
                &amp;&amp; file.good( ) );

        file.write( data.data( ),
                    data.size( ) );
    }

    void overwrite( std::vector&lt; std::byte &gt; const &amp; data,
                    std::size_t const &amp; off = 0 ) final
    {
        std::basic_ofstream&lt; std::byte &gt; file( path,
                                               std::ofstream::binary );

        assert( file.is_open( )
                &amp;&amp; file.good( ) );

        file.seekp( off ).write( data.data( ),
                                 data.size( ) );
    }

    void set_attr( bool readonly,
                   bool hidden )
    {
        DWORD attr = 0;

        if ( readonly )
            attr |= FILE_ATTRIBUTE_READONLY;
        if ( hidden )
            attr |= FILE_ATTRIBUTE_HIDDEN;

        if ( 0 == SetFileAttributes( path.c_str( ),
                                     attr
                                         ? attr
                                         : FILE_ATTRIBUTE_NORMAL ) )
            throw xept::ramfile_disk_attr_t( );
    }

protected:
    std::filesystem::path path;

private:
    using file_locks_t = std::unordered_map&lt; std::wstring_view,
                                             std::mutex &gt;;
    static file_locks_t file_locks;

    std::lock_guard&lt; file_locks_t::mapped_type &gt; file_guard;
};

ramfile_disk_xept.hpp - exceptions throwable from ramfile_disk
#include &quot;ramfile_xept.h&quot;

namespace xept
{
    using ramfile_disk_attr_t = struct utl_exception:
        ramfile_t
    { };
}
Design: The design is pretty bad. Think more on how you plan to use it.
Currently, it is prone to deadlocks.
Say, you create two ramfile_disk_t instances in two different places in your code of the same file. The second instance will be blocked until first is finished. Is this really what you want? Is this the only way you want the code to be used?
At least add options like:

&quot;shared usage&quot; - if you only want to read, there is no reason to disable access from several places. Also user may want to manage read/write safety themselves instead of relying on crude methods.
&quot;try open&quot; - return false if locked.
&quot;open&quot; - throw if locked.
&quot;wait for open&quot; - waits until unlocked if locked.
&quot;lock on usage&quot; - lock/unlocks the mutex upon read/write instead of creation/destruction of the class.

Technical Issues:

Avoid #include &quot;../win_xept.hpp&quot;. Imagine there is another win_xept.hpp file somewhere and user added an include path that results in loading the wrong win_xept.hpp. This leads to build corruption. If anything you'd better make an extra folder for all your code files just to be extra safe. So you always include #include &quot;my_codebase/current_project/win_xept.hpp&quot;. As a bonus your code will be organized better.

Frequently one makes a static create function for abstract classes that will instantiate the implementation depending on user's request. This way, user at no point needs to include implementation's headers.

ramfile_t is a class with virtual functions. Therefore, it is ought to have a virtual destructor (virtual ~ramfile_t() = default). This is especially true for purely abstract classes. Also, do you really think you'll have more that one implementation of the class, why even bother with the interface?

ramfile_disk_t has data-racing issues with the static member file_locks. You ought to guard it with a mutex each time you access it.

I am not 100% sure, but most likely
std::vector&lt; std::byte &gt;( std::istreambuf_iterator&lt; std::byte &gt;( file ),
                          std::istreambuf_iterator&lt; std::byte &gt;( ) );

is a very slow method to read the file. Consider using more low-level functions like fread or read of the iostream instead of the iterator-based approach. At least make some tests.

It is a bit nitpicky, but is there any reason to pass std::size_t const &amp; off = 0 as const reference in the overwrite method? Just pass it by value std::size_t off; not only passing by reference is slower, it becomes nuisance once you attempt to schedule the function in an asynchronous thread.