1// fileio.cpp -- 2// $Id: fileio.cpp 1230 2007-03-09 15:58:53Z jcw $ 3// This is part of Metakit, see http://www.equi4.com/metakit.html 4 5/** @file 6 * Implementation of c4_FileStrategy and c4_FileStream 7 */ 8 9#include "header.h" 10#include "mk4io.h" 11 12#if q4_WIN32 13#if q4_MSVC && !q4_STRICT 14#pragma warning(disable: 4201) // nonstandard extension used : ... 15#endif 16#define WIN32_LEAN_AND_MEAN 17#include <windows.h> 18#include <io.h> 19#include <fcntl.h> 20#include <sys/stat.h> 21#endif 22 23#if q4_UNIX && HAVE_MMAP 24#include <sys/types.h> 25#include <sys/mman.h> 26#endif 27 28#if q4_UNIX 29#include <unistd.h> 30#include <fcntl.h> 31#endif 32 33#if q4_WINCE 34#define _get_osfhandle(x) x 35#endif 36 37#ifndef _O_NOINHERIT 38#define _O_NOINHERIT 0 39#endif 40 41///////////////////////////////////////////////////////////////////////////// 42// 43// The "Carbon" version of a build on Macintosh supports running under 44// either MacOS 7..9 (which has no mmap), or MacOS X (which has mmap). 45// The logic below was adapted from a contribution by Paul Snively, it 46// decides at run time which case it is, and switches I/O calls to match. 47 48#if defined (q4_CARBON) && q4_CARBON 49//#if q4_MAC && !defined (__MACH__) && (!q4_MWCW || __MWERKS__ >= 0x3000) 50#undef HAVE_MMAP 51#define HAVE_MMAP 1 52 53#include <CFBundle.h> 54#include <Folders.h> 55 56#define PROT_NONE 0x00 57#define PROT_READ 0x01 58#define PROT_WRITE 0x02 59#define PROT_EXEC 0x04 60 61#define MAP_SHARED 0x0001 62#define MAP_PRIVATE 0x0002 63 64#define MAP_FIXED 0x0010 65#define MAP_RENAME 0x0020 66#define MAP_NORESERVE 0x0040 67#define MAP_INHERIT 0x0080 68#define MAP_NOEXTEND 0x0100 69#define MAP_HASSEMAPHORE 0x0200 70 71typedef unsigned long t4_u32; 72 73static t4_u32 sfwRefCount = 0; 74static CFBundleRef systemFramework = NULL; 75 76static char *fake_mmap(char *, t4_u32, int, int, int, long long) { 77 return (char*) - 1L; 78} 79 80static int fake_munmap(char *, t4_u32) { 81 return 0; 82} 83 84static FILE *(*my_fopen)(const char *, const char*) = fopen; 85static int(*my_fclose)(FILE*) = fclose; 86static long(*my_ftell)(FILE*) = ftell; 87static int(*my_fseek)(FILE *, long, int) = fseek; 88static t4_u32(*my_fread)(void *ptr, t4_u32, t4_u32, FILE*) = fread; 89static t4_u32(*my_fwrite)(const void *ptr, t4_u32, t4_u32, FILE*) = fwrite; 90static int(*my_ferror)(FILE*) = ferror; 91static int(*my_fflush)(FILE*) = fflush; 92static int(*my_fileno)(FILE*) = fileno; 93static char *(*my_mmap)(char *, t4_u32, int, int, int, long long) = fake_mmap; 94static int(*my_munmap)(char *, t4_u32) = fake_munmap; 95 96static void InitializeIO() { 97 if (sfwRefCount++) 98 return ; 99 // race condition, infinitesimal risk 100 101 FSRef theRef; 102 if (FSFindFolder(kOnAppropriateDisk, kFrameworksFolderType, false, &theRef) 103 == noErr) { 104 CFURLRef fw = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &theRef); 105 if (fw) { 106 CFURLRef bd = CFURLCreateCopyAppendingPathComponent 107 (kCFAllocatorSystemDefault, fw, CFSTR("System.framework"), false); 108 CFRelease(fw); 109 if (bd) { 110 systemFramework = CFBundleCreate(kCFAllocatorSystemDefault, bd); 111 CFRelease(bd); 112 } 113 } 114 if (!systemFramework || !CFBundleLoadExecutable(systemFramework)) 115 return ; 116#define F(x) CFBundleGetFunctionPointerForName(systemFramework, CFSTR(#x)) 117 my_fopen = (FILE *(*)(const char *, const char*))F(fopen); 118 my_fclose = (int(*)(FILE*))F(fclose); 119 my_ftell = (long(*)(FILE*))F(ftell); 120 my_fseek = (int(*)(FILE *, long, int))F(fseek); 121 my_fread = (t4_u32(*)(void *ptr, t4_u32, t4_u32, FILE*))F(fread); 122 my_fwrite = (t4_u32(*)(const void *ptr, t4_u32, t4_u32, FILE*))F(fwrite); 123 my_ferror = (int(*)(FILE*))F(ferror); 124 my_fflush = (int(*)(FILE*))F(fflush); 125 my_fileno = (int(*)(FILE*))F(fileno); 126 my_mmap = (char *(*)(char *, t4_u32, int, int, int, long long))F(mmap); 127 my_munmap = (int(*)(char *, t4_u32))F(munmap); 128#undef F 129 d4_assert(my_fopen && my_fclose && my_ftell && my_fseek && my_fread && 130 my_fwrite && my_ferror && my_fflush && my_fileno && my_mmap && my_munmap); 131 } 132} 133 134static void FinalizeIO() { 135 if (--sfwRefCount) 136 return ; 137 // race condition, infinitesimal risk 138 139 if (systemFramework) { 140 CFBundleUnloadExecutable(systemFramework); 141 CFRelease(systemFramework); 142 systemFramework = 0; 143 } 144} 145 146#define fopen my_fopen 147#define fclose my_fclose 148#define ftell my_ftell 149#define fseek my_fseek 150#define fread my_fread 151#define fwrite my_fwrite 152#define ferror my_ferror 153#define fflush my_fflush 154#define fileno my_fileno 155#define mmap my_mmap 156#define munmap my_munmap 157 158#else 159 160#define InitializeIO() 161#define FinalizeIO() 162 163#endif 164 165///////////////////////////////////////////////////////////////////////////// 166 167#if q4_CHECK 168#include <stdlib.h> 169 170void f4_AssertionFailed(const char *cond_, const char *file_, int line_) { 171 fprintf(stderr, "Assertion failed: %s (file %s, line %d)\n", cond_, file_, 172 line_); 173 abort(); 174} 175 176#endif //q4_CHECK 177 178///////////////////////////////////////////////////////////////////////////// 179// c4_FileStream 180 181c4_FileStream::c4_FileStream(FILE *stream_, bool owned_): _stream(stream_), 182 _owned(owned_){} 183 184c4_FileStream::~c4_FileStream() { 185 if (_owned) 186 fclose(_stream); 187} 188 189int c4_FileStream::Read(void *buffer_, int length_) { 190 d4_assert(_stream != 0); 191 192 return (int)fread(buffer_, 1, length_, _stream); 193} 194 195bool c4_FileStream::Write(const void *buffer_, int length_) { 196 d4_assert(_stream != 0); 197 198 return (int)fwrite(buffer_, 1, length_, _stream) == length_; 199} 200 201///////////////////////////////////////////////////////////////////////////// 202// c4_FileStrategy 203 204c4_FileStrategy::c4_FileStrategy(FILE *file_): _file(file_), _cleanup(0) { 205 InitializeIO(); 206 ResetFileMapping(); 207} 208 209c4_FileStrategy::~c4_FileStrategy() { 210 _file = 0; 211 ResetFileMapping(); 212 213 if (_cleanup) 214 fclose(_cleanup); 215 216 d4_assert(_mapStart == 0); 217 FinalizeIO(); 218} 219 220bool c4_FileStrategy::IsValid()const { 221 return _file != 0; 222} 223 224t4_i32 c4_FileStrategy::FileSize() { 225 d4_assert(_file != 0); 226 227 long size = - 1; 228 229 long old = ftell(_file); 230 if (old >= 0 && fseek(_file, 0, 2) == 0) { 231 long pos = ftell(_file); 232 if (fseek(_file, old, 0) == 0) 233 size = pos; 234 } 235 236 if (size < 0) 237 _failure = ferror(_file); 238 239 return size; 240} 241 242t4_i32 c4_FileStrategy::FreshGeneration() { 243 d4_assert(false); 244 return 0; 245} 246 247void c4_FileStrategy::ResetFileMapping() { 248#if q4_WIN32 249 if (_mapStart != 0) { 250 _mapStart -= _baseOffset; 251 d4_dbgdef(BOOL g = )::UnmapViewOfFile((char*)_mapStart); 252 d4_assert(g); 253 _mapStart = 0; 254 _dataSize = 0; 255 } 256 257 if (_file != 0) { 258 t4_i32 len = FileSize(); 259 260 if (len > 0) { 261 FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(_file))); 262 HANDLE h = ::CreateFileMapping((HANDLE)_get_osfhandle(_fileno(_file)), 0, 263 PAGE_READONLY, 0, len, 0); 264 265 if (h) { 266 _mapStart = (t4_byte*)::MapViewOfFile(h, FILE_MAP_READ, 0, 0, len); 267 268 if (_mapStart != 0) { 269 _mapStart += _baseOffset; 270 _dataSize = len - _baseOffset; 271 } 272 273 d4_dbgdef(BOOL f = )::CloseHandle(h); 274 d4_assert(f); 275 } 276 } 277 } 278#elif HAVE_MMAP && !NO_MMAP 279 if (_mapStart != 0) { 280 _mapStart -= _baseOffset; 281 munmap((char*)_mapStart, _baseOffset + _dataSize); // also loses const 282 _mapStart = 0; 283 _dataSize = 0; 284 } 285 286 if (_file != 0) { 287 t4_i32 len = FileSize(); 288 289 if (len > 0) { 290 _mapStart = (const t4_byte*)mmap(0, len, PROT_READ, MAP_SHARED, fileno 291 (_file), 0); 292 if (_mapStart != (void*) - 1L) { 293 _mapStart += _baseOffset; 294 _dataSize = len - _baseOffset; 295 } else 296 _mapStart = 0; 297 } 298 } 299#endif 300} 301 302#if q4_WIN32 && !q4_BORC && !q4_WINCE 303static DWORD GetPlatformId() { 304 static OSVERSIONINFO os; 305 if (os.dwPlatformId == 0) { 306 os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 307 GetVersionEx(&os); 308 } 309 return os.dwPlatformId; 310} 311 312#endif 313 314bool c4_FileStrategy::DataOpen(const char *fname_, int mode_) { 315 d4_assert(!_file); 316 317#if q4_WIN32 && !q4_BORC && !q4_WINCE 318 int flags = _O_BINARY | _O_NOINHERIT | (mode_ > 0 ? _O_RDWR : _O_RDONLY); 319 int fd = - 1; 320 321 if (GetPlatformId() != VER_PLATFORM_WIN32_NT) 322 fd = _open(fname_, flags); 323 if (fd == - 1) { 324 WCHAR wName[MAX_PATH]; 325 MultiByteToWideChar(CP_UTF8, 0, fname_, - 1, wName, MAX_PATH); 326 fd = _wopen(wName, flags); 327 } 328 if (fd != - 1) 329 _cleanup = _file = _fdopen(fd, mode_ > 0 ? "r+b" : "rb"); 330#else 331 _cleanup = _file = fopen(fname_, mode_ > 0 ? "r+b" : "rb"); 332#if q4_UNIX 333 if (_file != 0) 334 fcntl(fileno(_file), F_SETFD, FD_CLOEXEC); 335#endif //q4_UNIX 336#endif //q4_WIN32 && !q4_BORC && !q4_WINCE 337 338 if (_file != 0) { 339 ResetFileMapping(); 340 return true; 341 } 342 343 if (mode_ > 0) { 344#if q4_WIN32 && !q4_BORC && !q4_WINCE 345 WCHAR wName[MAX_PATH]; 346 MultiByteToWideChar(CP_UTF8, 0, fname_, - 1, wName, MAX_PATH); 347 fd = _wopen(wName, flags | _O_CREAT, _S_IREAD | _S_IWRITE); 348 if (fd != - 1) 349 _cleanup = _file = _fdopen(fd, "w+b"); 350#else 351 _cleanup = _file = fopen(fname_, "w+b"); 352#if q4_UNIX 353 if (_file != 0) 354 fcntl(fileno(_file), F_SETFD, FD_CLOEXEC); 355#endif //q4_UNIX 356#endif //q4_WIN32 && !q4_BORC && !q4_WINCE 357 } 358 359 //d4_assert(_file != 0); 360 return false; 361} 362 363int c4_FileStrategy::DataRead(t4_i32 pos_, void *buf_, int len_) { 364 d4_assert(_baseOffset + pos_ >= 0); 365 d4_assert(_file != 0); 366 367 //printf("DataRead at %d len %d\n", pos_, len_); 368 return fseek(_file, _baseOffset + pos_, 0) != 0 ? - 1: (int)fread(buf_, 1, 369 len_, _file); 370} 371 372void c4_FileStrategy::DataWrite(t4_i32 pos_, const void *buf_, int len_) { 373 d4_assert(_baseOffset + pos_ >= 0); 374 d4_assert(_file != 0); 375#if 0 376 if (_mapStart <= buf_ && buf_ < _mapStart + _dataSize) { 377 printf("DataWrite %08x at %d len %d (map %d)\n", buf_, pos_, len_, (const 378 t4_byte*)buf_ - _mapStart + _baseOffset); 379 } else { 380 printf("DataWrite %08x at %d len %d\n", buf_, pos_, len_); 381 } 382 fprintf(stderr, 383 " _mapStart %08x _dataSize %d buf_ %08x len_ %d _baseOffset %d\n", 384 _mapStart, _dataSize, buf_, len_, _baseOffset); 385 printf(" _mapStart %08x _dataSize %d buf_ %08x len_ %d _baseOffset %d\n", 386 _mapStart, _dataSize, buf_, len_, _baseOffset); 387 fflush(stdout); 388#endif 389 390#if q4_WIN32 || __hpux || __MACH__ 391 // if (buf_ >= _mapStart && buf_ <= _mapLimit - len_) 392 393 // a horrendous hack to allow file mapping for Win95 on network drive 394 // must use a temp buf to avoid write from mapped file to same file 395 // 396 // 6-Feb-1999 -- this workaround is not thread safe 397 // 30-Nov-2001 -- changed to use the stack so now it is 398 // 28-Oct-2002 -- added HP/UX to the mix, to avoid hard lockup 399 char tempBuf[4096]; 400 d4_assert(len_ <= sizeof tempBuf); 401 buf_ = memcpy(tempBuf, buf_, len_); 402#endif 403 404 if (fseek(_file, _baseOffset + pos_, 0) != 0 || (int)fwrite(buf_, 1, len_, 405 _file) != len_) { 406 _failure = ferror(_file); 407 d4_assert(_failure != 0); 408 d4_assert(true); // always force an assertion failure in debug mode 409 } 410} 411 412void c4_FileStrategy::DataCommit(t4_i32 limit_) { 413 d4_assert(_file != 0); 414 415 if (fflush(_file) < 0) { 416 _failure = ferror(_file); 417 d4_assert(_failure != 0); 418 d4_assert(true); // always force an assertion failure in debug mode 419 return ; 420 } 421 422 if (limit_ > 0) { 423#if 0 // can't truncate file in a portable way! 424 // unmap the file first, WinNT is more picky about this than Win95 425 FILE *save = _file; 426 427 _file = 0; 428 ResetFileMapping(); 429 _file = save; 430 431 _file->SetLength(limit_); // now we can resize the file 432#endif 433 ResetFileMapping(); // remap, since file length may have changed 434 } 435} 436 437///////////////////////////////////////////////////////////////////////////// 438