Abstraction.
You need an abstraction layer between the code and the actual data (file or memory). You'll need to write two implementations of this abstraction layer - one for a file and the other for memory. Your comments state the need for random access so this layer will need to support seek.
This is much more easily done in C++. If you are limited to C, use a struct with function pointer members - one each for open, close, read, write, and seek.
typedef void *handle_t;
struct indirect
{
bool (*open)(const char *name, handle_t &handle);
void (*close)(handle_t handle);
bool (*read)(handle_t handle, unsigned char *data, size_t size, size_t *copied);
bool (*write)(handle_t handle, const unsigned char *data, size_t size, size_t *copied);
bool (*seek)(handle_t handle, long seek);
};
Next, you'd write five functions that call the standard C FILE * API. Get that working first as it will be the easiest way to verify you have not broken anything.
Finally, write five functions that use memory - probably the heap - to do the same.