2006-04-12 01:59:08 +02:00
|
|
|
/**
|
|
|
|
* =========================================================================
|
|
|
|
* File : file.cpp
|
|
|
|
* Project : 0 A.D.
|
|
|
|
* Description : file layer on top of POSIX. avoids the need for
|
|
|
|
* : absolute paths.
|
|
|
|
*
|
|
|
|
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
|
|
|
|
* =========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2004-2006 Jan Wassenberg
|
|
|
|
*
|
|
|
|
* Redistribution and/or modification are also permitted under the
|
|
|
|
* terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation (version 2 or later, at your option).
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
*/
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-08 03:11:51 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
#include "lib.h"
|
|
|
|
#include "adts.h"
|
2004-08-05 21:20:45 +02:00
|
|
|
#include "sysdep/sysdep.h"
|
2005-05-18 07:32:09 +02:00
|
|
|
#include "byte_order.h"
|
2005-10-12 19:19:07 +02:00
|
|
|
#include "lib/allocators.h"
|
2006-01-23 21:05:09 +01:00
|
|
|
#include "file_internal.h"
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-06-04 14:41:53 +02:00
|
|
|
#include <vector>
|
|
|
|
#include <algorithm>
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2004-06-09 15:49:32 +02:00
|
|
|
#include <string>
|
2004-06-08 17:16:50 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
// rationale for aio, instead of only using mmap:
|
2004-06-08 17:16:50 +02:00
|
|
|
// - parallelism: instead of just waiting for the transfer to complete,
|
2004-03-03 00:56:51 +01:00
|
|
|
// other work can be done in the meantime.
|
|
|
|
// example: decompressing from a Zip archive is practically free,
|
|
|
|
// because we inflate one block while reading the next.
|
|
|
|
// - throughput: with aio, the drive always has something to do, as opposed
|
|
|
|
// to read requests triggered by the OS for mapped files, which come
|
|
|
|
// in smaller chunks. this leads to much higher transfer rates.
|
|
|
|
// - memory: when used with VFS, aio makes better use of a file cache.
|
|
|
|
// data is generally compressed in an archive. a cache should store the
|
2005-08-10 03:12:03 +02:00
|
|
|
// decompressed and decoded (e.g. TGA colour swapping) data; mmap would
|
2004-03-03 00:56:51 +01:00
|
|
|
// keep the original, compressed data in memory, which doesn't help.
|
|
|
|
// we bypass the OS file cache via aio, and store partial blocks here (*);
|
|
|
|
// higher level routines will cache the actual useful data.
|
|
|
|
// * requests for part of a block are usually followed by another.
|
|
|
|
|
|
|
|
|
2004-12-01 19:44:38 +01:00
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
// layer on top of POSIX opendir/readdir/closedir that handles
|
|
|
|
// portable -> native path conversion, ignores non-file/directory entries,
|
|
|
|
// and additionally returns the file status (size and mtime).
|
|
|
|
|
|
|
|
// rationale: see DirIterator definition in header.
|
|
|
|
struct DirIterator_
|
2004-05-06 19:14:30 +02:00
|
|
|
{
|
2005-08-09 18:32:23 +02:00
|
|
|
DIR* os_dir;
|
|
|
|
|
|
|
|
// to support stat(), we need to either chdir or store the complete path.
|
|
|
|
// the former is unacceptable because it isn't thread-safe. therefore,
|
|
|
|
// we latch dir_open's path and append entry name every dir_next_ent call.
|
|
|
|
// this is also the storage to which DirEnt.name points!
|
|
|
|
// PathPackage avoids repeated memory allocs and strlen() overhead.
|
|
|
|
PathPackage pp;
|
|
|
|
};
|
|
|
|
|
|
|
|
cassert(sizeof(DirIterator_) <= sizeof(DirIterator));
|
|
|
|
|
|
|
|
|
|
|
|
// prepare to iterate (once) over entries in the given directory.
|
|
|
|
// returns a negative error code or 0 on success, in which case <d> is
|
|
|
|
// ready for subsequent dir_next_ent calls and must be freed via dir_close.
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError dir_open(const char* P_path, DirIterator* d_)
|
2005-08-09 18:32:23 +02:00
|
|
|
{
|
|
|
|
DirIterator_* d = (DirIterator_*)d_;
|
|
|
|
|
|
|
|
char n_path[PATH_MAX];
|
2005-10-14 22:28:55 +02:00
|
|
|
// HACK: allow calling with a full (absolute) native path.
|
2006-04-11 03:45:07 +02:00
|
|
|
// (required by wdll_ver).
|
2005-10-14 22:28:55 +02:00
|
|
|
#if OS_WIN
|
|
|
|
if(P_path[1] == ':' && P_path[2] == '\\')
|
|
|
|
strcpy_s(n_path, ARRAY_SIZE(n_path), P_path);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
// note: copying to n_path and then pp.path is inefficient but
|
|
|
|
// more clear/robust. this is only called a few hundred times anyway.
|
|
|
|
RETURN_ERR(file_make_full_native_path(P_path, n_path));
|
|
|
|
}
|
2005-08-09 18:32:23 +02:00
|
|
|
|
|
|
|
d->os_dir = opendir(n_path);
|
|
|
|
if(!d->os_dir)
|
2006-04-03 23:28:10 +02:00
|
|
|
return LibError_from_errno();
|
2005-08-09 18:32:23 +02:00
|
|
|
|
2006-04-22 18:26:16 +02:00
|
|
|
RETURN_ERR(path_package_set_dir(&d->pp, n_path));
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-08-09 18:32:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// return ERR_DIR_END if all entries have already been returned once,
|
|
|
|
// another negative error code, or 0 on success, in which case <ent>
|
|
|
|
// describes the next (order is unspecified) directory entry.
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError dir_next_ent(DirIterator* d_, DirEnt* ent)
|
2005-08-09 18:32:23 +02:00
|
|
|
{
|
|
|
|
DirIterator_* d = (DirIterator_*)d_;
|
|
|
|
|
|
|
|
get_another_entry:
|
2005-10-18 01:35:16 +02:00
|
|
|
errno = 0;
|
2005-08-09 18:32:23 +02:00
|
|
|
struct dirent* os_ent = readdir(d->os_dir);
|
|
|
|
if(!os_ent)
|
2005-10-18 01:35:16 +02:00
|
|
|
{
|
2006-04-03 23:28:10 +02:00
|
|
|
// no error, just no more entries to return
|
|
|
|
if(!errno)
|
|
|
|
return ERR_DIR_END; // NOWARN
|
|
|
|
return LibError_from_errno();
|
2005-10-18 01:35:16 +02:00
|
|
|
}
|
2005-08-09 18:32:23 +02:00
|
|
|
|
|
|
|
// copy os_ent.name[]; we need it for stat() #if !OS_WIN and
|
|
|
|
// return it as ent.name (since os_ent.name[] is volatile).
|
2006-04-22 18:26:16 +02:00
|
|
|
path_package_append_file(&d->pp, os_ent->d_name);
|
2005-08-09 18:32:23 +02:00
|
|
|
const char* name = d->pp.end;
|
|
|
|
|
|
|
|
// get file information (mode, size, mtime)
|
|
|
|
struct stat s;
|
|
|
|
#if OS_WIN
|
|
|
|
// .. wposix readdir has enough information to return dirent
|
|
|
|
// status directly (much faster than calling stat).
|
|
|
|
CHECK_ERR(readdir_stat_np(d->os_dir, &s));
|
2004-12-19 00:30:28 +01:00
|
|
|
#else
|
2005-08-09 18:32:23 +02:00
|
|
|
// .. call regular stat().
|
2006-04-22 18:26:16 +02:00
|
|
|
// we need the full pathname for this. don't use path_append because
|
2005-08-09 18:32:23 +02:00
|
|
|
// it would unnecessarily call strlen.
|
|
|
|
|
|
|
|
CHECK_ERR(stat(d->pp.path, &s));
|
2004-12-19 00:30:28 +01:00
|
|
|
#endif
|
2004-06-21 18:29:47 +02:00
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
// skip "undesirable" entries that POSIX readdir returns:
|
|
|
|
if(S_ISDIR(s.st_mode))
|
2004-12-07 02:19:10 +01:00
|
|
|
{
|
2005-08-09 18:32:23 +02:00
|
|
|
// .. dummy directory entries ("." and "..")
|
|
|
|
if(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
|
|
|
|
goto get_another_entry;
|
|
|
|
|
|
|
|
s.st_size = -1; // our way of indicating it's a directory
|
2004-12-07 02:19:10 +01:00
|
|
|
}
|
2005-08-09 18:32:23 +02:00
|
|
|
// .. neither dir nor file
|
|
|
|
else if(!S_ISREG(s.st_mode))
|
|
|
|
goto get_another_entry;
|
2004-12-07 02:19:10 +01:00
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
ent->size = s.st_size;
|
|
|
|
ent->mtime = s.st_mtime;
|
|
|
|
ent->name = name;
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-08-09 18:32:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// indicate the directory iterator is no longer needed; all resources it
|
|
|
|
// held are freed.
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError dir_close(DirIterator* d_)
|
2005-08-09 18:32:23 +02:00
|
|
|
{
|
|
|
|
DirIterator_* d = (DirIterator_*)d_;
|
|
|
|
WARN_ERR(closedir(d->os_dir));
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-08-09 18:32:23 +02:00
|
|
|
}
|
2004-05-06 19:14:30 +02:00
|
|
|
|
|
|
|
|
2006-02-23 08:55:32 +01:00
|
|
|
|
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
// get file information. output param is zeroed on error.
|
2006-04-03 23:28:10 +02:00
|
|
|
static LibError file_stat_impl(const char* fn, struct stat* s, bool warn_if_failed = true)
|
2004-05-06 19:14:30 +02:00
|
|
|
{
|
2004-08-27 02:33:20 +02:00
|
|
|
memset(s, 0, sizeof(struct stat));
|
|
|
|
|
2006-04-10 21:09:11 +02:00
|
|
|
char N_fn[PATH_MAX];
|
2006-03-01 23:31:11 +01:00
|
|
|
RETURN_ERR(file_make_full_native_path(fn, N_fn));
|
2004-05-06 19:14:30 +02:00
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
errno = 0;
|
2006-04-03 23:28:10 +02:00
|
|
|
int ret = stat(N_fn, s);
|
|
|
|
return LibError_from_posix(ret, warn_if_failed);
|
2006-03-01 23:31:11 +01:00
|
|
|
}
|
|
|
|
|
2006-04-03 23:28:10 +02:00
|
|
|
LibError file_stat(const char* fn, struct stat* s)
|
|
|
|
{
|
|
|
|
return file_stat_impl(fn, s);
|
|
|
|
}
|
2006-03-01 23:31:11 +01:00
|
|
|
|
|
|
|
// does the given file exist? (implemented via file_stat)
|
|
|
|
bool file_exists(const char* fn)
|
|
|
|
{
|
|
|
|
struct stat s;
|
2006-04-03 23:28:10 +02:00
|
|
|
const bool warn_if_failed = false;
|
|
|
|
return file_stat_impl(fn, &s, warn_if_failed) == ERR_OK;
|
2004-05-06 19:14:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-04-10 21:09:11 +02:00
|
|
|
// permanently delete the file. be very careful with this!
|
|
|
|
LibError file_delete(const char* fn)
|
|
|
|
{
|
|
|
|
char N_fn[PATH_MAX+1];
|
|
|
|
RETURN_ERR(file_make_full_native_path(fn, N_fn));
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
int ret = unlink(N_fn);
|
|
|
|
return LibError_from_posix(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// file open/close
|
2004-05-06 19:14:30 +02:00
|
|
|
// stores information about file (e.g. size) in File struct
|
2004-03-03 00:56:51 +01:00
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// interface rationale:
|
|
|
|
// - this module depends on the handle manager for IO management,
|
|
|
|
// but should be useable without the VFS (even if they are designed
|
|
|
|
// to work together).
|
|
|
|
// - allocating a Handle for the file info would solve several problems
|
|
|
|
// (see below), but we don't want to allocate 2..3 (VFS, file, Zip file)
|
|
|
|
// for every file opened - that'd add up quickly.
|
|
|
|
// the Files are always freed at exit though, since they're part of
|
|
|
|
// VFile handles in the VFS.
|
|
|
|
// - we want the VFS open logic to be triggered on file invalidate
|
|
|
|
// (if the dev. file is deleted, we should use what's in the archives).
|
2004-08-09 18:46:57 +02:00
|
|
|
// we don't want to make this module depend on VFS, so we don't
|
|
|
|
// have access to the file location DB; VFS needs to allocate the handle.
|
2004-03-03 00:56:51 +01:00
|
|
|
// - no problem exposing our internals via File struct -
|
|
|
|
// we're only used by the VFS and Zip modules. don't bother making
|
|
|
|
// an opaque struct - that'd have to be kept in sync with the real thing.
|
|
|
|
// - when Zip opens its archives via file_open, a handle isn't needed -
|
|
|
|
// the Zip module hides its File struct (required to close the file),
|
|
|
|
// and the Handle approach doesn't guard against some idiot calling
|
2004-08-09 18:46:57 +02:00
|
|
|
// close(our_fd_value) directly, either.
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2006-04-22 18:26:16 +02:00
|
|
|
cassert(sizeof(PosixFile) < FILE_OPAQUE_SIZE);
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError file_validate(const File* f)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
|
|
|
if(!f)
|
2005-10-12 06:27:55 +02:00
|
|
|
return ERR_INVALID_PARAM;
|
2006-04-19 17:19:04 +02:00
|
|
|
const PosixFile* pf = (PosixFile*)f->opaque;
|
|
|
|
if(pf->fd < 0)
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_1;
|
2005-10-12 06:27:55 +02:00
|
|
|
// mapped but refcount is invalid
|
2006-04-19 17:19:04 +02:00
|
|
|
else if((pf->mapping != 0) ^ (pf->map_refs != 0))
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_2;
|
2006-04-03 23:28:10 +02:00
|
|
|
// note: don't check atom_fn - that complains at the end of
|
|
|
|
// file_open if flags & FILE_DONT_SET_FN and has no benefit, really.
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-04-19 17:19:04 +02:00
|
|
|
LibError file_open(const char* P_fn, uint flags, File* f)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-09-19 17:51:12 +02:00
|
|
|
// zero output param in case we fail below.
|
2005-10-24 01:57:59 +02:00
|
|
|
memset(f, 0, sizeof(*f));
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2006-04-03 23:28:10 +02:00
|
|
|
if(flags > FILE_FLAG_ALL)
|
|
|
|
WARN_RETURN(ERR_INVALID_PARAM);
|
2005-08-09 18:32:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
char N_fn[PATH_MAX];
|
|
|
|
RETURN_ERR(file_make_full_native_path(P_fn, N_fn));
|
2004-05-06 19:14:30 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
// don't stat if opening for writing - the file may not exist yet
|
2004-06-02 22:41:05 +02:00
|
|
|
off_t size = 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-07-05 04:30:53 +02:00
|
|
|
int oflag = O_RDONLY;
|
2004-03-03 00:56:51 +01:00
|
|
|
if(flags & FILE_WRITE)
|
2005-03-29 08:27:35 +02:00
|
|
|
oflag = O_WRONLY|O_CREAT|O_TRUNC;
|
2005-03-18 23:15:49 +01:00
|
|
|
// read access requested
|
2004-03-03 00:56:51 +01:00
|
|
|
else
|
|
|
|
{
|
2005-03-18 23:15:49 +01:00
|
|
|
// get file size
|
2004-03-03 00:56:51 +01:00
|
|
|
struct stat s;
|
2006-01-23 21:05:09 +01:00
|
|
|
if(stat(N_fn, &s) < 0)
|
2006-04-03 23:28:10 +02:00
|
|
|
WARN_RETURN(ERR_FILE_NOT_FOUND);
|
2005-03-18 23:15:49 +01:00
|
|
|
size = s.st_size;
|
|
|
|
|
|
|
|
// note: despite increased overhead, the AIO read method is still
|
|
|
|
// significantly faster, even with small files.
|
|
|
|
// we therefore don't automatically disable AIO.
|
|
|
|
// notes:
|
|
|
|
// - up to 32KB can be read by one SCSI request.
|
|
|
|
// - flags are stored below and will influence file_io.
|
2005-10-24 01:57:59 +02:00
|
|
|
//if(size <= 32*KiB)
|
|
|
|
// flags |= FILE_NO_AIO;
|
2005-03-18 23:15:49 +01:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// make sure <N_fn> is a regular file
|
2004-07-05 04:30:53 +02:00
|
|
|
if(!S_ISREG(s.st_mode))
|
2006-04-03 23:28:10 +02:00
|
|
|
WARN_RETURN(ERR_NOT_FILE);
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
#if OS_WIN
|
2005-04-29 15:17:17 +02:00
|
|
|
if(flags & FILE_TEXT)
|
|
|
|
oflag |= O_TEXT_NP;
|
|
|
|
else
|
|
|
|
oflag |= O_BINARY_NP;
|
2005-03-18 23:15:49 +01:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// if AIO is disabled at user's behest, so inform wposix.
|
2005-03-18 23:15:49 +01:00
|
|
|
if(flags & FILE_NO_AIO)
|
|
|
|
oflag |= O_NO_AIO_NP;
|
2004-07-08 17:10:26 +02:00
|
|
|
#endif
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
int fd = open(N_fn, oflag, S_IRWXO|S_IRWXU|S_IRWXG);
|
2004-03-03 00:56:51 +01:00
|
|
|
if(fd < 0)
|
2006-04-03 23:28:10 +02:00
|
|
|
WARN_RETURN(ERR_FILE_ACCESS);
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2006-04-19 17:19:04 +02:00
|
|
|
f->flags = flags;
|
|
|
|
f->size = size;
|
2006-01-31 04:47:52 +01:00
|
|
|
// see FILE_DONT_SET_FN decl.
|
|
|
|
if(!(flags & FILE_DONT_SET_FN))
|
2006-04-19 17:19:04 +02:00
|
|
|
f->atom_fn = file_make_unique_fn_copy(P_fn);
|
|
|
|
PosixFile* pf = (PosixFile*)f->opaque;
|
|
|
|
pf->mapping = 0;
|
|
|
|
pf->map_refs = 0;
|
|
|
|
pf->fd = fd;
|
2004-06-03 02:17:24 +02:00
|
|
|
CHECK_FILE(f);
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError file_close(File* f)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
|
|
|
CHECK_FILE(f);
|
2006-04-19 17:19:04 +02:00
|
|
|
PosixFile* pf = (PosixFile*)f->opaque;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-06-03 02:27:50 +02:00
|
|
|
// make sure the mapping is actually freed,
|
|
|
|
// regardless of how many references remain.
|
2006-04-19 17:19:04 +02:00
|
|
|
if(pf->map_refs > 1)
|
|
|
|
pf->map_refs = 1;
|
|
|
|
if(pf->mapping) // only free if necessary (unmap complains if not mapped)
|
2004-08-09 18:46:57 +02:00
|
|
|
file_unmap(f);
|
2004-06-03 02:27:50 +02:00
|
|
|
|
2005-03-29 08:27:35 +02:00
|
|
|
// return final file size (required by VFS after writing files).
|
|
|
|
// this is much easier than updating when writing, because we'd have
|
|
|
|
// to add accounting code to both (sync and async) paths.
|
2006-04-19 17:19:04 +02:00
|
|
|
f->size = lseek(pf->fd, 0, SEEK_END);
|
2005-03-29 08:27:35 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
// (check fd to avoid BoundsChecker warning about invalid close() param)
|
2006-04-19 17:19:04 +02:00
|
|
|
if(pf->fd != -1)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2006-04-19 17:19:04 +02:00
|
|
|
close(pf->fd);
|
|
|
|
pf->fd = -1;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// wipe out any cached blocks. this is necessary to cover the (rare) case
|
|
|
|
// of file cache contents predating the file write.
|
2006-04-19 17:19:04 +02:00
|
|
|
if(f->flags & FILE_WRITE)
|
|
|
|
file_cache_invalidate(f->atom_fn);
|
2004-08-15 23:50:29 +02:00
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-10-12 06:27:55 +02:00
|
|
|
}
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// memory mapping
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
2004-06-03 02:17:24 +02:00
|
|
|
// no significance aside from preventing uint overflow.
|
|
|
|
static const uint MAX_MAP_REFS = 255;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
2004-06-03 02:17:24 +02:00
|
|
|
// map the entire file <f> into memory. if already currently mapped,
|
|
|
|
// return the previous mapping (reference-counted).
|
|
|
|
// output parameters are zeroed on failure.
|
|
|
|
//
|
|
|
|
// the mapping will be removed (if still open) when its file is closed.
|
|
|
|
// however, map/unmap calls should still be paired so that the mapping
|
|
|
|
// may be removed when no longer needed.
|
|
|
|
//
|
|
|
|
// rationale: reference counting is required for zip_map: several
|
|
|
|
// Zip "mappings" each reference one ZArchive's actual file mapping.
|
|
|
|
// implement it here so that we also get refcounting for normal files.
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError file_map(File* f, void*& p, size_t& size)
|
2004-06-03 02:17:24 +02:00
|
|
|
{
|
|
|
|
p = 0;
|
|
|
|
size = 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-06-03 02:17:24 +02:00
|
|
|
CHECK_FILE(f);
|
2006-04-19 17:19:04 +02:00
|
|
|
PosixFile* pf = (PosixFile*)f->opaque;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2006-04-19 17:19:04 +02:00
|
|
|
const int prot = (f->flags & FILE_WRITE)? PROT_WRITE : PROT_READ;
|
2004-06-09 15:49:32 +02:00
|
|
|
|
2004-06-03 02:17:24 +02:00
|
|
|
// already mapped - increase refcount and return previous mapping.
|
2006-04-19 17:19:04 +02:00
|
|
|
if(pf->mapping)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-06-03 02:17:24 +02:00
|
|
|
// prevent overflow; if we have this many refs, should find out why.
|
2006-04-19 17:19:04 +02:00
|
|
|
if(pf->map_refs >= MAX_MAP_REFS)
|
2006-01-23 21:05:09 +01:00
|
|
|
WARN_RETURN(ERR_LIMIT);
|
2006-04-19 17:19:04 +02:00
|
|
|
pf->map_refs++;
|
2004-06-03 02:17:24 +02:00
|
|
|
goto have_mapping;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
2004-06-09 18:10:23 +02:00
|
|
|
// don't allow mapping zero-length files (doesn't make sense,
|
2004-08-09 18:46:57 +02:00
|
|
|
// and BoundsChecker warns about wposix mmap failing).
|
|
|
|
// then again, don't complain, because this might happen when mounting
|
|
|
|
// a dir containing empty files; each is opened as a Zip file.
|
2006-04-19 17:19:04 +02:00
|
|
|
if(f->size == 0)
|
2006-04-03 23:28:10 +02:00
|
|
|
return ERR_FAIL; // NOWARN
|
2004-06-09 18:10:23 +02:00
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
errno = 0;
|
2006-04-19 17:19:04 +02:00
|
|
|
pf->mapping = mmap(0, f->size, prot, MAP_PRIVATE, pf->fd, (off_t)0);
|
|
|
|
if(pf->mapping == MAP_FAILED)
|
2005-12-11 23:23:55 +01:00
|
|
|
return LibError_from_errno();
|
2004-06-03 02:17:24 +02:00
|
|
|
|
2006-04-19 17:19:04 +02:00
|
|
|
pf->map_refs = 1;
|
2004-06-03 02:17:24 +02:00
|
|
|
|
|
|
|
have_mapping:
|
2006-04-19 17:19:04 +02:00
|
|
|
p = pf->mapping;
|
|
|
|
size = f->size;
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-03 02:17:24 +02:00
|
|
|
// decrement the reference count for the mapping belonging to file <f>.
|
|
|
|
// fail if there are no references; remove the mapping if the count reaches 0.
|
|
|
|
//
|
|
|
|
// the mapping will be removed (if still open) when its file is closed.
|
|
|
|
// however, map/unmap calls should still be paired so that the mapping
|
|
|
|
// may be removed when no longer needed.
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError file_unmap(File* f)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-06-03 02:17:24 +02:00
|
|
|
CHECK_FILE(f);
|
2006-04-19 17:19:04 +02:00
|
|
|
PosixFile* pf = (PosixFile*)f->opaque;
|
2004-06-03 02:17:24 +02:00
|
|
|
|
2004-06-03 02:27:50 +02:00
|
|
|
// file is not currently mapped
|
2006-04-19 17:19:04 +02:00
|
|
|
if(pf->map_refs == 0)
|
2006-04-03 23:28:10 +02:00
|
|
|
WARN_RETURN(ERR_NOT_MAPPED);
|
2004-06-03 02:27:50 +02:00
|
|
|
|
|
|
|
// still more than one reference remaining - done.
|
2006-04-19 17:19:04 +02:00
|
|
|
if(--pf->map_refs > 0)
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2004-06-03 02:27:50 +02:00
|
|
|
|
|
|
|
// no more references: remove the mapping
|
2006-04-19 17:19:04 +02:00
|
|
|
void* p = pf->mapping;
|
|
|
|
pf->mapping = 0;
|
|
|
|
// don't clear f->size - the file is still open.
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
errno = 0;
|
2006-04-19 17:19:04 +02:00
|
|
|
int ret = munmap(p, f->size);
|
2006-04-03 23:28:10 +02:00
|
|
|
return LibError_from_posix(ret);
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
2005-10-12 06:27:55 +02:00
|
|
|
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
LibError file_init()
|
|
|
|
{
|
2006-04-22 18:26:16 +02:00
|
|
|
path_init();
|
2006-01-23 21:05:09 +01:00
|
|
|
file_cache_init();
|
2006-01-28 23:19:42 +01:00
|
|
|
file_io_init();
|
2006-04-03 23:28:10 +02:00
|
|
|
|
|
|
|
// convenience
|
|
|
|
file_sector_size = sys_max_sector_size();
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
return ERR_OK;
|
|
|
|
}
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError file_shutdown()
|
2005-10-12 06:27:55 +02:00
|
|
|
{
|
2006-01-25 08:21:45 +01:00
|
|
|
stats_dump();
|
2006-04-22 18:26:16 +02:00
|
|
|
path_shutdown();
|
2006-01-23 21:05:09 +01:00
|
|
|
file_io_shutdown();
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-10-12 06:27:55 +02:00
|
|
|
}
|