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;
}
Exemple #5
0
// --------------------------------------------------------------------------
//
// 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;
}
Exemple #6
0
// --------------------------------------------------------------------------
//
// 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;
}