// file.h cross platform basic file class. supports 64 bit offsets and such. /* Copyright 2009 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #if !defined(_WIN32) #include "errno.h" #include #include #include #include #include #endif #include "text.h" namespace mongo { #ifndef __sunos__ typedef uint64_t fileofs; #else typedef boost::uint64_t fileofs; #endif /* NOTE: not thread-safe. (at least the windows implementation isn't. */ class FileInterface { public: void open(const char *fn) {} void write(fileofs o, const char *data, unsigned len) {} void read(fileofs o, char *data, unsigned len) {} bool bad() {return false;} bool is_open() {return false;} fileofs len() { return 0; } void fsync() { assert(false); } // shrink file to size bytes. No-op if file already smaller. void truncate(fileofs size); /** @return -1 if error or unavailable */ static boost::intmax_t freeSpace(const string &path) { assert(false); return -1; } }; #if defined(_WIN32) #include class File : public FileInterface { HANDLE fd; bool _bad; string _name; void err(BOOL b=false) { /* false = error happened */ if( !b && !_bad ) { _bad = true; log() << "File " << _name << "I/O error " << GetLastError() << '\n'; } } public: File() { fd = INVALID_HANDLE_VALUE; _bad = true; } ~File() { if( is_open() ) CloseHandle(fd); fd = INVALID_HANDLE_VALUE; } void open(const char *filename, bool readOnly=false , bool direct=false) { _name = filename; fd = CreateFile( toNativeString(filename).c_str(), ( readOnly ? 0 : GENERIC_WRITE ) | GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if( !is_open() ) { DWORD e = GetLastError(); log() << "Create/Open File failed " << filename << ' ' << errnoWithDescription(e) << endl; } else _bad = false; } static boost::intmax_t freeSpace(const string &path) { ULARGE_INTEGER avail; if( GetDiskFreeSpaceEx(toNativeString(path.c_str()).c_str(), &avail, NULL, NULL) ) { return avail.QuadPart; } DWORD e = GetLastError(); log() << "GetDiskFreeSpaceEx fails errno: " << e << endl; return -1; } void write(fileofs o, const char *data, unsigned len) { LARGE_INTEGER li; li.QuadPart = o; SetFilePointerEx(fd, li, NULL, FILE_BEGIN); DWORD written; err( WriteFile(fd, data, len, &written, NULL) ); } void read(fileofs o, char *data, unsigned len) { DWORD read; LARGE_INTEGER li; li.QuadPart = o; SetFilePointerEx(fd, li, NULL, FILE_BEGIN); int ok = ReadFile(fd, data, len, &read, 0); if( !ok ) err(ok); else massert( 10438 , "ReadFile error - truncated file?", read == len); } bool bad() { return _bad; } bool is_open() { return fd != INVALID_HANDLE_VALUE; } fileofs len() { LARGE_INTEGER li; li.LowPart = GetFileSize(fd, (DWORD *) &li.HighPart); if( li.HighPart == 0 && li.LowPart == INVALID_FILE_SIZE ) { err( false ); return 0; } return li.QuadPart; } void fsync() { FlushFileBuffers(fd); } void truncate(fileofs size) { if (len() <= size) return; LARGE_INTEGER li; li.QuadPart = size; if (SetFilePointerEx(fd, li, NULL, FILE_BEGIN) == 0){ err(false); return; //couldn't seek } err(SetEndOfFile(fd)); } }; #else class File : public FileInterface { public: int fd; private: bool _bad; void err(bool ok) { if( !ok && !_bad ) { _bad = true; log() << "File I/O " << errnoWithDescription() << '\n'; } } public: File() { fd = -1; _bad = true; } ~File() { if( is_open() ) ::close(fd); fd = -1; } #ifndef O_NOATIME #define O_NOATIME 0 #endif void open(const char *filename, bool readOnly=false , bool direct=false) { fd = ::open(filename, O_CREAT | ( readOnly ? 0 : ( O_RDWR | O_NOATIME ) ) #if defined(O_DIRECT) | ( direct ? O_DIRECT : 0 ) #endif , S_IRUSR | S_IWUSR); if ( fd <= 0 ) { out() << "couldn't open " << filename << ' ' << errnoWithDescription() << endl; return; } _bad = false; } void write(fileofs o, const char *data, unsigned len) { err( ::pwrite(fd, data, len, o) == (int) len ); } void read(fileofs o, char *data, unsigned len) { ssize_t s = ::pread(fd, data, len, o); if( s == -1 ) { err(false); } else if( s != (int) len ) { _bad = true; log() << "File error read:" << s << " bytes, wanted:" << len << " ofs:" << o << endl; } } bool bad() { return _bad; } bool is_open() { return fd > 0; } fileofs len() { off_t o = lseek(fd, 0, SEEK_END); if( o != (off_t) -1 ) return o; err(false); return 0; } void fsync() { ::fsync(fd); } static boost::intmax_t freeSpace ( const string &path ) { struct statvfs info; assert( !statvfs( path.c_str() , &info ) ); return boost::intmax_t( info.f_bavail ) * info.f_frsize; } void truncate(fileofs size) { if (len() <= size) return; err(ftruncate(fd, size) == 0); } }; #endif }