void BackupStoreAccounts::LockAccount(int32_t ID, NamedLock& rNamedLock) { const BackupStoreAccountDatabase::Entry &en(mrDatabase.GetEntry(ID)); std::string rootDir = MakeAccountRootDir(ID, en.GetDiscSet()); int discSet = en.GetDiscSet(); std::string writeLockFilename; StoreStructure::MakeWriteLockFilename(rootDir, discSet, writeLockFilename); bool gotLock = false; int triesLeft = 8; do { gotLock = rNamedLock.TryAndGetLock(writeLockFilename, 0600 /* restrictive file permissions */); if(!gotLock) { --triesLeft; ::sleep(1); } } while (!gotLock && triesLeft > 0); if (!gotLock) { THROW_EXCEPTION_MESSAGE(BackupStoreException, CouldNotLockStoreAccount, "Failed to get exclusive " "lock on account " << BOX_FORMAT_ACCOUNT(ID)); } }
void BackupStoreRefCountDatabase::Discard() { if (!mIsTemporaryFile) { THROW_EXCEPTION_MESSAGE(CommonException, Internal, "Cannot discard a permanent reference count database"); } // Under normal conditions, we should know whether the file is still // open or not, and not Discard it unless it's open. However if the // final rename() fails during Commit(), the file will already be // closed, and we don't want to blow up here in that case. if (mapDatabaseFile.get()) { mapDatabaseFile->Close(); mapDatabaseFile.reset(); } if(unlink(mFilename.c_str()) != 0) { THROW_EMU_FILE_ERROR("Failed to delete temporary refcount " "database file", mFilename, CommonException, OSFileError); } mIsModified = false; mIsTemporaryFile = false; }
// -------------------------------------------------------------------------- // // Function // Name: BackupStoreRefCountDatabase::~BackupStoreRefCountDatabase // Purpose: Destructor // Created: 2003/08/28 // // -------------------------------------------------------------------------- BackupStoreRefCountDatabase::~BackupStoreRefCountDatabase() { if (mIsTemporaryFile) { THROW_EXCEPTION_MESSAGE(CommonException, Internal, "BackupStoreRefCountDatabase destroyed without " "explicit commit or discard"); Discard(); } }
void BackupStoreRefCountDatabase::Commit() { if (!mIsTemporaryFile) { THROW_EXCEPTION_MESSAGE(CommonException, Internal, "Cannot commit a permanent reference count database"); } if (!mapDatabaseFile.get()) { THROW_EXCEPTION_MESSAGE(CommonException, Internal, "Reference count database is already closed"); } mapDatabaseFile->Close(); mapDatabaseFile.reset(); std::string Final_Filename = GetFilename(mAccount, false); #ifdef WIN32 if(FileExists(Final_Filename) && unlink(Final_Filename.c_str()) != 0) { THROW_EMU_FILE_ERROR("Failed to delete old permanent refcount " "database file", mFilename, CommonException, OSFileError); } #endif if(rename(mFilename.c_str(), Final_Filename.c_str()) != 0) { THROW_EMU_ERROR("Failed to rename temporary refcount database " "file from " << mFilename << " to " << Final_Filename, CommonException, OSFileError); } mFilename = Final_Filename; mIsModified = false; mIsTemporaryFile = false; }
// -------------------------------------------------------------------------- // // Function // Name: HTTPRequest::Send(IOStream &, int) // Purpose: Write the request to an IOStream using HTTP. // Created: 03/01/09 // // -------------------------------------------------------------------------- bool HTTPRequest::Send(IOStream &rStream, int Timeout, bool ExpectContinue) { switch (mMethod) { case Method_UNINITIALISED: THROW_EXCEPTION(HTTPException, RequestNotInitialised); break; case Method_UNKNOWN: THROW_EXCEPTION(HTTPException, BadRequest); break; case Method_GET: rStream.Write("GET"); break; case Method_HEAD: rStream.Write("HEAD"); break; case Method_POST: rStream.Write("POST"); break; case Method_PUT: rStream.Write("PUT"); break; } rStream.Write(" "); rStream.Write(mRequestURI.c_str()); rStream.Write(" "); switch (mHTTPVersion) { case HTTPVersion_0_9: rStream.Write("HTTP/0.9"); break; case HTTPVersion_1_0: rStream.Write("HTTP/1.0"); break; case HTTPVersion_1_1: rStream.Write("HTTP/1.1"); break; default: THROW_EXCEPTION_MESSAGE(HTTPException, NotImplemented, "Unsupported HTTP version: " << mHTTPVersion); } rStream.Write("\n"); std::ostringstream oss; if (mContentLength != -1) { oss << "Content-Length: " << mContentLength << "\n"; } if (mContentType != "") { oss << "Content-Type: " << mContentType << "\n"; } if (mHostName != "") { if (mHostPort != 80) { oss << "Host: " << mHostName << ":" << mHostPort << "\n"; } else { oss << "Host: " << mHostName << "\n"; } } if (mpCookies) { THROW_EXCEPTION_MESSAGE(HTTPException, NotImplemented, "Cookie support not implemented yet"); } if (mClientKeepAliveRequested) { oss << "Connection: keep-alive\n"; } else { oss << "Connection: close\n"; } for (std::vector<Header>::iterator i = mExtraHeaders.begin(); i != mExtraHeaders.end(); i++) { oss << i->first << ": " << i->second << "\n"; } if (ExpectContinue) { oss << "Expect: 100-continue\n"; } rStream.Write(oss.str().c_str()); rStream.Write("\n"); return true; }
// -------------------------------------------------------------------------- // // Function // Name: HTTPRequest::Receive(IOStreamGetLine &, int) // Purpose: Read the request from an IOStreamGetLine (and // attached stream). // Returns false if there was no valid request, // probably due to a kept-alive connection closing. // Created: 26/3/04 // // -------------------------------------------------------------------------- bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout) { // Check caller's logic if(mMethod != Method_UNINITIALISED) { THROW_EXCEPTION(HTTPException, RequestAlreadyBeenRead); } // Read the first line, which is of a different format to the rest of the lines std::string requestLine; if(!rGetLine.GetLine(requestLine, false /* no preprocessing */, Timeout)) { // Didn't get the request line, probably end of connection which had been kept alive return false; } BOX_TRACE("Request line: " << requestLine); // Check the method size_t p = 0; // current position in string p = requestLine.find(' '); // end of first word if(p == std::string::npos) { // No terminating space, looks bad p = requestLine.size(); } else { mHttpVerb = requestLine.substr(0, p); if (mHttpVerb == "GET") { mMethod = Method_GET; } else if (mHttpVerb == "HEAD") { mMethod = Method_HEAD; } else if (mHttpVerb == "POST") { mMethod = Method_POST; } else if (mHttpVerb == "PUT") { mMethod = Method_PUT; } else { mMethod = Method_UNKNOWN; } } // Skip spaces to find URI const char *requestLinePtr = requestLine.c_str(); while(requestLinePtr[p] != '\0' && requestLinePtr[p] == ' ') { ++p; } // Check there's a URI following... if(requestLinePtr[p] == '\0') { // Didn't get the request line, probably end of connection which had been kept alive return false; } // Read the URI, unescaping any %XX hex codes while(requestLinePtr[p] != ' ' && requestLinePtr[p] != '\0') { // End of URI, on to query string? if(requestLinePtr[p] == '?') { // Put the rest into the query string, without escaping anything ++p; while(requestLinePtr[p] != ' ' && requestLinePtr[p] != '\0') { mQueryString += requestLinePtr[p]; ++p; } break; } // Needs unescaping? else if(requestLinePtr[p] == '+') { mRequestURI += ' '; } else if(requestLinePtr[p] == '%') { // Be tolerant about this... bad things are silently accepted, // rather than throwing an error. char code[4] = {0,0,0,0}; code[0] = requestLinePtr[++p]; if(code[0] != '\0') { code[1] = requestLinePtr[++p]; } // Convert into a char code long c = ::strtol(code, NULL, 16); // Accept it? if(c > 0 && c <= 255) { mRequestURI += (char)c; } } else { // Simple copy of character mRequestURI += requestLinePtr[p]; } ++p; } // End of URL? if(requestLinePtr[p] == '\0') { // Assume HTTP 0.9 mHTTPVersion = HTTPVersion_0_9; } else { // Skip any more spaces while(requestLinePtr[p] != '\0' && requestLinePtr[p] == ' ') { ++p; } // Check to see if there's the right string next... if(::strncmp(requestLinePtr + p, "HTTP/", 5) == 0) { // Find the version numbers int major, minor; if(::sscanf(requestLinePtr + p + 5, "%d.%d", &major, &minor) != 2) { THROW_EXCEPTION_MESSAGE(HTTPException, BadRequest, "Unable to parse HTTP version number: " << requestLinePtr); } // Store version mHTTPVersion = (major * HTTPVersion__MajorMultiplier) + minor; } else { // Not good -- wrong string found THROW_EXCEPTION_MESSAGE(HTTPException, BadRequest, "Unable to parse HTTP request line: " << requestLinePtr); } } BOX_TRACE("HTTPRequest: method=" << mMethod << ", uri=" << mRequestURI << ", version=" << mHTTPVersion); // If HTTP 1.1 or greater, assume keep-alive if(mHTTPVersion >= HTTPVersion_1_1) { mClientKeepAliveRequested = true; } // Decode query string? if((mMethod == Method_GET || mMethod == Method_HEAD) && !mQueryString.empty()) { HTTPQueryDecoder decoder(mQuery); decoder.DecodeChunk(mQueryString.c_str(), mQueryString.size()); decoder.Finish(); } // Now parse the headers ParseHeaders(rGetLine, Timeout); std::string expected; if(GetHeader("Expect", &expected)) { if(expected == "100-continue") { mExpectContinue = true; } } // Parse form data? if(mMethod == Method_POST && mContentLength >= 0) { // Too long? Don't allow people to be nasty by sending lots of data if(mContentLength > MAX_CONTENT_SIZE) { THROW_EXCEPTION(HTTPException, POSTContentTooLong); } // Some data in the request to follow, parsing it bit by bit HTTPQueryDecoder decoder(mQuery); // Don't forget any data left in the GetLine object int fromBuffer = rGetLine.GetSizeOfBufferedData(); if(fromBuffer > mContentLength) fromBuffer = mContentLength; if(fromBuffer > 0) { BOX_TRACE("Decoding " << fromBuffer << " bytes of " "data from getline buffer"); decoder.DecodeChunk((const char *)rGetLine.GetBufferedData(), fromBuffer); // And tell the getline object to ignore the data we just used rGetLine.IgnoreBufferedData(fromBuffer); } // Then read any more data, as required int bytesToGo = mContentLength - fromBuffer; while(bytesToGo > 0) { char buf[4096]; int toRead = sizeof(buf); if(toRead > bytesToGo) toRead = bytesToGo; IOStream &rstream(rGetLine.GetUnderlyingStream()); int r = rstream.Read(buf, toRead, Timeout); if(r == 0) { // Timeout, just error THROW_EXCEPTION_MESSAGE(HTTPException, RequestReadFailed, "Failed to read complete request with the timeout"); } decoder.DecodeChunk(buf, r); bytesToGo -= r; } // Finish off decoder.Finish(); } else if (mContentLength > 0) { IOStream::pos_type bytesToCopy = rGetLine.GetSizeOfBufferedData(); if (bytesToCopy > mContentLength) { bytesToCopy = mContentLength; } Write(rGetLine.GetBufferedData(), bytesToCopy); SetForReading(); mpStreamToReadFrom = &(rGetLine.GetUnderlyingStream()); } return true; }