/** * ========================================================================= * File : vfs_redirector.cpp * Project : 0 A.D. * Description : * ========================================================================= */ // license: GPL; see lib/license.txt #include "precompiled.h" #include "vfs_redirector.h" #include "lib/byte_order.h" // FOURCC #include "file_internal.h" static const u32 vtbl_magic = FOURCC('F','P','V','T'); // HACK: these thunks and the vtbls are implemented here, // although they belong in their respective provider's source file. // this is currently necessary because vfs_mount doesn't yet // abstract away the file provider (it's hardcoded for files+archives). LibError afile_open_vfs(const char* fn, uint flags, TFile* tf, File* f) // out { const uintptr_t memento = tfile_get_memento(tf); const Mount* m = tfile_get_mount(tf); const Handle ha = mount_get_archive(m); return afile_open(ha, fn, memento, flags, f); } LibError file_open_vfs(const char* V_path, uint flags, TFile* tf, File* f) // out { char N_path[PATH_MAX]; const Mount* m = tfile_get_mount(tf); RETURN_ERR(mount_realpath(V_path, m, N_path)); RETURN_ERR(file_open(N_path, flags|FILE_DONT_SET_FN, f)); // file_open didn't set fc.atom_fn due to FILE_DONT_SET_FN. f->atom_fn = file_make_unique_fn_copy(V_path); return INFO::OK; } static const FileProvider_VTbl archive_vtbl = { vtbl_magic, 0,0,0, // not supported for archives ATM afile_open_vfs, afile_close, afile_validate, afile_io_issue, afile_io_has_completed, afile_io_wait, afile_io_discard, afile_io_validate, afile_read, afile_map, afile_unmap }; static const FileProvider_VTbl file_vtbl = { vtbl_magic, dir_open, dir_next_ent, dir_close, file_open_vfs, file_close, file_validate, file_io_issue, file_io_has_completed, file_io_wait, file_io_discard, file_io_validate, file_io, file_map, file_unmap }; // see FileProvider_VTbl decl for details on why this is so empty. static const FileProvider_VTbl tree_vtbl = { vtbl_magic, tree_dir_open, tree_dir_next_ent, tree_dir_close, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // rationale for not using virtual functions for file_open vs afile_open: // it would spread out the implementation of each function and makes // keeping them in sync harder. we will very rarely add new sources and // all these functions are in one spot anyway. static LibError vtbl_validate(const FileProvider_VTbl* vtbl) { if(!vtbl) WARN_RETURN(ERR::INVALID_PARAM); if(vtbl->magic != vtbl_magic) WARN_RETURN(ERR::CORRUPTED); return INFO::OK; } #define CHECK_VTBL(type) RETURN_ERR(vtbl_validate(type)) // // directory entry enumeration // LibError xdir_open(const char* dir, DirIterator* di) { // HACK: it is unclear ATM how to set this properly. assume tree_dir_* is // the only user ATM. di->type = &tree_vtbl; CHECK_VTBL(di->type); return di->type->dir_open(dir, di); } LibError xdir_next_ent(DirIterator* di, DirEnt* ent) { CHECK_VTBL(di->type); return di->type->dir_next_ent(di, ent); } LibError xdir_close(DirIterator* di) { CHECK_VTBL(di->type); return di->type->dir_close(di); } // // file object // bool xfile_is_open(const File* f) { // not currently in use if(f->type == 0) return false; WARN_ERR(vtbl_validate(f->type)); return true; } LibError xfile_open(const char* V_path, uint flags, TFile* tf, File* f) { // find out who is providing this file const Mount* m = tfile_get_mount(tf); debug_assert(m != 0); // HACK: see decl of vtbls. ideally vtbl would already be stored in // Mount, but that's not implemented yet. char c = mount_get_type(m); const FileProvider_VTbl* vtbl = (c == 'F')? &file_vtbl : &archive_vtbl; CHECK_VTBL(vtbl); RETURN_ERR(vtbl->file_open(V_path, flags, tf, f)); // success // note: don't assign these unless we succeed to avoid the // false impression that all is well. f->type = vtbl; return INFO::OK; } LibError xfile_close(File* f) { // we must not complain if the file is not open. this happens if // attempting to open a nonexistent file: h_mgr automatically calls // the dtor after reload fails. // note: this takes care of checking the vtbl. if(!xfile_is_open(f)) return INFO::OK; LibError ret = f->type->file_close(f); f->type = 0; return ret; } LibError xfile_validate(const File* f) { CHECK_VTBL(f->type); return f->type->file_validate(f); } // // IO // LibError xfile_io_issue(File* f, off_t ofs, size_t size, void* buf, FileIo* io) { io->type = f->type; CHECK_VTBL(io->type); return io->type->io_issue(f, ofs, size, buf, io); } int xfile_io_has_completed(FileIo* io) { CHECK_VTBL(io->type); return io->type->io_has_completed(io); } LibError xfile_io_wait(FileIo* io, void*& p, size_t& size) { CHECK_VTBL(io->type); return io->type->io_wait(io, p, size); } LibError xfile_io_discard(FileIo* io) { CHECK_VTBL(io->type); return io->type->io_discard(io); } LibError xfile_io_validate(const FileIo* io) { CHECK_VTBL(io->type); return io->type->io_validate(io); } ssize_t xfile_io(File* f, off_t ofs, size_t size, FileIOBuf* pbuf, FileIOCB cb, uintptr_t ctx) { CHECK_VTBL(f->type); // notes: // - for archive file: vfs_open makes sure it wasn't opened for writing // - normal file: let file_io alloc the buffer if the caller didn't // (i.e. p = 0), because it knows about alignment / padding requirements return f->type->io(f, ofs, size, pbuf, cb, ctx); } // // file mapping // LibError xfile_map(File* f, void*& p, size_t& size) { CHECK_VTBL(f->type); return f->type->map(f, p, size); } LibError xfile_unmap(File* f) { CHECK_VTBL(f->type); return f->type->unmap(f); }