LogFile::LogFile(const std::string& name, bool readwrite) : _name(name) { int options = O_CREAT | (readwrite?O_RDWR:O_WRONLY) #if defined(O_DIRECT) | O_DIRECT #endif #if defined(O_NOATIME) | O_NOATIME #endif ; _fd = open(name.c_str(), options, S_IRUSR | S_IWUSR); #if defined(O_DIRECT) _direct = true; if( _fd < 0 ) { _direct = false; options &= ~O_DIRECT; _fd = open(name.c_str(), options, S_IRUSR | S_IWUSR); } #else _direct = false; #endif if( _fd < 0 ) { uasserted(13516, str::stream() << "couldn't open file " << name << " for writing " << errnoWithDescription()); } flushMyDirectory(name); }
Status StorageEngineLockFile::writePid() { if (!_lockFileHandle->isValid()) { return Status(ErrorCodes::FileNotOpen, str::stream() << "Unable to write process ID to " << _filespec << " because file has not been opened."); } if (::ftruncate(_lockFileHandle->_fd, 0)) { int errorcode = errno; return Status(ErrorCodes::FileStreamFailed, str::stream() << "Unable to write process id to file (ftruncate failed): " << _filespec << ' ' << errnoWithDescription(errorcode)); } ProcessId pid = ProcessId::getCurrent(); std::stringstream ss; ss << pid << std::endl; std::string pidStr = ss.str(); int bytesWritten = ::write(_lockFileHandle->_fd, pidStr.c_str(), pidStr.size()); if (bytesWritten < 0) { int errorcode = errno; return Status(ErrorCodes::FileStreamFailed, str::stream() << "Unable to write process id " << pid.toString() << " to file: " << _filespec << ' ' << errnoWithDescription(errorcode)); } else if (bytesWritten == 0) { return Status(ErrorCodes::FileStreamFailed, str::stream() << "Unable to write process id " << pid.toString() << " to file: " << _filespec << " no data written."); } if (::fsync(_lockFileHandle->_fd)) { int errorcode = errno; return Status(ErrorCodes::FileStreamFailed, str::stream() << "Unable to write process id " << pid.toString() << " to file (fsync failed): " << _filespec << ' ' << errnoWithDescription(errorcode)); } flushMyDirectory(_filespec); return Status::OK(); }
boost::filesystem::path ensureParentDirCreated(const boost::filesystem::path& p){ const boost::filesystem::path parent = p.branch_path(); if (! boost::filesystem::exists(parent)){ ensureParentDirCreated(parent); log() << "creating directory " << parent.string() << endl; boost::filesystem::create_directory(parent); flushMyDirectory(parent); // flushes grandparent to ensure parent exists after crash } assert(boost::filesystem::is_directory(parent)); return parent; }
void FileCreatedOp::replay() { // i believe the code assumes new files are filled with zeros. thus we have to recreate the file, // or rewrite at least, even if it were the right length. perhaps one day we should change that // although easier to avoid defects if we assume it is zeros perhaps. string full = _p.asFullPath(); if( boost::filesystem::exists(full) ) { try { boost::filesystem::remove(full); } catch(std::exception& e) { LOG(1) << "recover info FileCreateOp::replay unlink " << e.what() << endl; } } log() << "recover create file " << full << ' ' << _len/1024.0/1024.0 << "MB" << endl; if( boost::filesystem::exists(full) ) { // first delete if exists. try { boost::filesystem::remove(full); } catch(...) { log() << "warning could not delete file " << full << endl; } } ensureParentDirCreated(full); File f; f.open(full.c_str()); massert(13547, str::stream() << "recover couldn't create file " << full, f.is_open()); unsigned long long left = _len; const unsigned blksz = 64 * 1024; scoped_array<char> v( new char[blksz] ); memset( v.get(), 0, blksz ); fileofs ofs = 0; while( left ) { unsigned long long w = left < blksz ? left : blksz; f.write(ofs, v.get(), (unsigned) w); left -= w; ofs += w; } f.fsync(); flushMyDirectory(full); massert(13628, str::stream() << "recover failure writing file " << full, !f.bad() ); }
void acquirePathLock() { string name = ( boost::filesystem::path( dbpath ) / "mongod.lock" ).string(); #ifdef _WIN32 lockFileHandle = CreateFileA( name.c_str(), GENERIC_READ | GENERIC_WRITE, 0 /* do not allow anyone else access */, NULL, OPEN_ALWAYS /* success if fh can open */, 0, NULL ); if (lockFileHandle == INVALID_HANDLE_VALUE) { DWORD code = GetLastError(); char *msg; FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL); string m = msg; str::stripTrailing(m, "\r\n"); uasserted( 13627 , str::stream() << "Unable to create/open lock file: " << name << ' ' << m << ". Is a mongod instance already running?" ); } lockFile = _open_osfhandle((intptr_t)lockFileHandle, 0); #else lockFile = open( name.c_str(), O_RDWR | O_CREAT , S_IRWXU | S_IRWXG | S_IRWXO ); if( lockFile <= 0 ) { uasserted( 10309 , str::stream() << "Unable to create/open lock file: " << name << ' ' << errnoWithDescription() << " Is a mongod instance already running?" ); } if (flock( lockFile, LOCK_EX | LOCK_NB ) != 0) { close ( lockFile ); lockFile = 0; uassert( 10310 , "Unable to lock file: " + name + ". Is a mongod instance already running?", 0 ); } #endif #ifdef _WIN32 uassert( 13625, "Unable to truncate lock file", _chsize(lockFile, 0) == 0); writePid( lockFile ); _commit( lockFile ); #else uassert( 13342, "Unable to truncate lock file", ftruncate(lockFile, 0) == 0); writePid( lockFile ); fsync( lockFile ); flushMyDirectory(name); #endif }
LogFile::LogFile(const std::string& name, bool readwrite) : _name(name) { int options = O_CREAT | (readwrite ? O_RDWR : O_WRONLY) #if defined(O_DIRECT) | O_DIRECT #endif #if defined(O_NOATIME) | O_NOATIME #endif ; _fd = open(name.c_str(), options, S_IRUSR | S_IWUSR); _blkSize = g_minOSPageSizeBytes; #if defined(O_DIRECT) _direct = true; if (_fd < 0) { _direct = false; options &= ~O_DIRECT; _fd = open(name.c_str(), options, S_IRUSR | S_IWUSR); } #ifdef __linux__ ssize_t tmpBlkSize = ioctl(_fd, BLKBSZGET); // TODO: We need some sanity checking on tmpBlkSize even if ioctl() did not fail. if (tmpBlkSize > 0) { _blkSize = (size_t)tmpBlkSize; } #endif #else _direct = false; #endif if (_fd < 0) { uasserted(13516, str::stream() << "couldn't open file " << name << " for writing " << errnoWithDescription()); } flushMyDirectory(name); }
void acquirePathLock(bool doingRepair) { string name = ( boost::filesystem::path( dbpath ) / "mongod.lock" ).string(); bool oldFile = false; if ( boost::filesystem::exists( name ) && boost::filesystem::file_size( name ) > 0 ) { oldFile = true; } #ifdef _WIN32 lockFileHandle = CreateFileA( name.c_str(), GENERIC_READ | GENERIC_WRITE, 0 /* do not allow anyone else access */, NULL, OPEN_ALWAYS /* success if fh can open */, 0, NULL ); if (lockFileHandle == INVALID_HANDLE_VALUE) { DWORD code = GetLastError(); char *msg; FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL); string m = msg; str::stripTrailing(m, "\r\n"); uasserted( 13627 , str::stream() << "Unable to create/open lock file: " << name << ' ' << m << ". Is a mongod instance already running?" ); } lockFile = _open_osfhandle((intptr_t)lockFileHandle, 0); #else lockFile = open( name.c_str(), O_RDWR | O_CREAT , S_IRWXU | S_IRWXG | S_IRWXO ); if( lockFile <= 0 ) { uasserted( 10309 , str::stream() << "Unable to create/open lock file: " << name << ' ' << errnoWithDescription() << " Is a mongod instance already running?" ); } if (flock( lockFile, LOCK_EX | LOCK_NB ) != 0) { close ( lockFile ); lockFile = 0; uassert( 10310 , "Unable to lock file: " + name + ". Is a mongod instance already running?", 0 ); } #endif if ( oldFile ) { // we check this here because we want to see if we can get the lock // if we can't, then its probably just another mongod running string errmsg; if (doingRepair && dur::haveJournalFiles()) { errmsg = "************** \n" "You specified --repair but there are dirty journal files. Please\n" "restart without --repair to allow the journal files to be replayed.\n" "If you wish to repair all databases, please shutdown cleanly and\n" "run with --repair again.\n" "**************"; } else if (cmdLine.dur) { if (!dur::haveJournalFiles(/*anyFiles=*/true)) { // Passing anyFiles=true as we are trying to protect against starting in an // unclean state with the journal directory unmounted. If there are any files, // even prealloc files, then it means that it is mounted so we can continue. // Previously there was an issue (SERVER-5056) where we would fail to start up // if killed during prealloc. vector<string> dbnames; getDatabaseNames( dbnames ); if ( dbnames.size() == 0 ) { // this means that mongod crashed // between initial startup and when journaling was initialized // it is safe to continue } else { errmsg = str::stream() << "************** \n" << "old lock file: " << name << ". probably means unclean shutdown,\n" << "but there are no journal files to recover.\n" << "this is likely human error or filesystem corruption.\n" << "please make sure that your journal directory is mounted.\n" << "found " << dbnames.size() << " dbs.\n" << "see: http://dochub.mongodb.org/core/repair for more information\n" << "*************"; } } } else { if (!dur::haveJournalFiles() && !doingRepair) { errmsg = str::stream() << "************** \n" << "Unclean shutdown detected.\n" << "Please visit http://dochub.mongodb.org/core/repair for recovery instructions.\n" << "*************"; } } if (!errmsg.empty()) { cout << errmsg << endl; #ifdef _WIN32 CloseHandle( lockFileHandle ); #else close ( lockFile ); #endif lockFile = 0; uassert( 12596 , "old lock file" , 0 ); } } // Not related to lock file, but this is where we handle unclean shutdown if( !cmdLine.dur && dur::haveJournalFiles() ) { cout << "**************" << endl; cout << "Error: journal files are present in journal directory, yet starting without journaling enabled." << endl; cout << "It is recommended that you start with journaling enabled so that recovery may occur." << endl; cout << "**************" << endl; uasserted(13597, "can't start without --journal enabled when journal/ files are present"); } #ifdef _WIN32 uassert( 13625, "Unable to truncate lock file", _chsize(lockFile, 0) == 0); writePid( lockFile ); _commit( lockFile ); #else uassert( 13342, "Unable to truncate lock file", ftruncate(lockFile, 0) == 0); writePid( lockFile ); fsync( lockFile ); flushMyDirectory(name); #endif }
void FileAllocator::run( FileAllocator * fa ) { setThreadName( "FileAllocator" ); while( 1 ) { { scoped_lock lk( fa->_pendingMutex ); if ( fa->_pending.size() == 0 ) fa->_pendingUpdated.wait( lk.boost() ); } while( 1 ) { string name; long size; { scoped_lock lk( fa->_pendingMutex ); if ( fa->_pending.size() == 0 ) break; name = fa->_pending.front(); size = fa->_pendingSize[ name ]; } string tmp; long fd = 0; try { log() << "allocating new datafile " << name << ", filling with zeroes..." << endl; boost::filesystem::path parent = ensureParentDirCreated(name); tmp = makeTempFileName( parent ); ensureParentDirCreated(tmp); fd = open(tmp.c_str(), O_CREAT | O_RDWR | O_NOATIME, S_IRUSR | S_IWUSR); if ( fd <= 0 ) { log() << "FileAllocator: couldn't create " << name << " (" << tmp << ") " << errnoWithDescription() << endl; uasserted(10439, ""); } #if defined(POSIX_FADV_DONTNEED) if( posix_fadvise(fd, 0, size, POSIX_FADV_DONTNEED) ) { log() << "warning: posix_fadvise fails " << name << " (" << tmp << ") " << errnoWithDescription() << endl; } #endif Timer t; /* make sure the file is the full desired length */ ensureLength( fd , size ); close( fd ); fd = 0; if( rename(tmp.c_str(), name.c_str()) ) { log() << "error: couldn't rename " << tmp << " to " << name << ' ' << errnoWithDescription() << endl; uasserted(13653, ""); } flushMyDirectory(name); log() << "done allocating datafile " << name << ", " << "size: " << size/1024/1024 << "MB, " << " took " << ((double)t.millis())/1000.0 << " secs" << endl; // no longer in a failed state. allow new writers. fa->_failed = false; } catch ( ... ) { if ( fd > 0 ) close( fd ); log() << "error failed to allocate new file: " << name << " size: " << size << ' ' << errnoWithDescription() << warnings; log() << " will try again in 10 seconds" << endl; // not going to warning logs try { if ( tmp.size() ) MONGO_ASSERT_ON_EXCEPTION( boost::filesystem::remove( tmp ) ); MONGO_ASSERT_ON_EXCEPTION( boost::filesystem::remove( name ) ); } catch ( ... ) { } scoped_lock lk( fa->_pendingMutex ); fa->_failed = true; // not erasing from pending fa->_pendingUpdated.notify_all(); sleepsecs(10); continue; } { scoped_lock lk( fa->_pendingMutex ); fa->_pendingSize.erase( name ); fa->_pending.pop_front(); fa->_pendingUpdated.notify_all(); } } } }
void FileAllocator::run( FileAllocator * fa ) { setThreadName( "FileAllocator" ); { // initialize unique temporary file name counter // TODO: SERVER-6055 -- Unify temporary file name selection SimpleMutex::scoped_lock lk(_uniqueNumberMutex); _uniqueNumber = curTimeMicros64(); } while( 1 ) { { scoped_lock lk( fa->_pendingMutex ); if ( fa->_pending.size() == 0 ) fa->_pendingUpdated.wait( lk.boost() ); } while( 1 ) { string name; long size = 0; { scoped_lock lk( fa->_pendingMutex ); if ( fa->_pending.size() == 0 ) break; name = fa->_pending.front(); size = fa->_pendingSize[ name ]; } string tmp; long fd = 0; try { log() << "allocating new datafile " << name << ", filling with zeroes..." << endl; boost::filesystem::path parent = ensureParentDirCreated(name); tmp = fa->makeTempFileName( parent ); ensureParentDirCreated(tmp); #if defined(_WIN32) fd = _open( tmp.c_str(), _O_RDWR | _O_CREAT | O_NOATIME, _S_IREAD | _S_IWRITE ); #else fd = open(tmp.c_str(), O_CREAT | O_RDWR | O_NOATIME, S_IRUSR | S_IWUSR); #endif if ( fd < 0 ) { log() << "FileAllocator: couldn't create " << name << " (" << tmp << ") " << errnoWithDescription() << endl; uasserted(10439, ""); } #if defined(POSIX_FADV_DONTNEED) if( posix_fadvise(fd, 0, size, POSIX_FADV_DONTNEED) ) { log() << "warning: posix_fadvise fails " << name << " (" << tmp << ") " << errnoWithDescription() << endl; } #endif Timer t; /* make sure the file is the full desired length */ ensureLength( fd , size ); close( fd ); fd = 0; if( rename(tmp.c_str(), name.c_str()) ) { const string& errStr = errnoWithDescription(); const string& errMessage = str::stream() << "error: couldn't rename " << tmp << " to " << name << ' ' << errStr; msgasserted(13653, errMessage); } flushMyDirectory(name); log() << "done allocating datafile " << name << ", " << "size: " << size/1024/1024 << "MB, " << " took " << ((double)t.millis())/1000.0 << " secs" << endl; // no longer in a failed state. allow new writers. fa->_failed = false; } catch ( const std::exception& e ) { log() << "error: failed to allocate new file: " << name << " size: " << size << ' ' << e.what() << ". will try again in 10 seconds" << endl; if ( fd > 0 ) close( fd ); try { if ( ! tmp.empty() ) boost::filesystem::remove( tmp ); boost::filesystem::remove( name ); } catch ( const std::exception& e ) { log() << "error removing files: " << e.what() << endl; } scoped_lock lk( fa->_pendingMutex ); fa->_failed = true; // not erasing from pending fa->_pendingUpdated.notify_all(); sleepsecs(10); continue; } { scoped_lock lk( fa->_pendingMutex ); fa->_pendingSize.erase( name ); fa->_pending.pop_front(); fa->_pendingUpdated.notify_all(); } } } }