blob: 78cca9add8674a72fccfeec991eb8894d11dcdf9 [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
//
/// \file libwrite.cpp
/// \brief STFS_write implementation
///
/// This file contains the implementation of the STFS_write() function,
/// starting with the functional STFSLIB_write function and drilling
/// down to supporting functions.
//
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
//
// @@@ END COPYRIGHT @@@
///////////////////////////////////////////////////////////////////////////////
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <iostream>
#include "stfs/stfslib.h"
#include "stfs_metadata.h"
#include "stfs_defs.h"
#include "stfs_util.h"
#include "stfs_message.h"
#include "stfsd.h"
#include "send.h"
namespace STFS {
///////////////////////////////////////////////////////////////////////////////
///
// STSFSLIB_write()
///
/// \brief The actual implementation of an STFS_write
///
/// This method is the driver for an STFS_write() function call.
/// it also implements the protocol for determining whether or not
/// to create a new fragment. Only STFSLIB can make this call, not
/// STFS-d, so the logic is put here. We could put a lot of "if
/// !ExecutingInDaemon" escape clauses, but there's no need to put
/// those in the mainline remote write path.
///
///
/// \param pv_Offset byte offset count based on whence
/// \param pv_Whence values are as follow: \n
/// SEEK_SET - The offset is set to offset bytes. \n
/// SEEK_CUR - The offset is set to its current
/// location plus offset bytes. \n
/// SEEK_END - The offset is set to the size of the
/// file plus offset bytes.
///
/// \retval off_t SUCCESS: The resulting offset location from beginning
/// of current fragment file.\n
/// FAILURE: -1
///
///////////////////////////////////////////////////////////////////////////////
ssize_t
STFSLIB_write( stfs_fhndl_t pv_Fhandle,
const void *pp_Buf,
size_t pv_Count)
{
const char *WHERE = "STFSLIB_write";
STFS_ScopeTrace lv_st(WHERE);
if (!pp_Buf) {
errno = EINVAL;
return -1;
}
STFS_ExternalFileHandle *lp_Efh
= STFS_ExternalFileHandle::GetExternalFileHandle(pv_Fhandle);
if (! lp_Efh) {
TRACE_PRINTF2(1, "Error in obtaining External File Handle for %ld\n", pv_Fhandle);
return -1;
}
lp_Efh->ResetErrors();
bool lv_WriteCompleted = false;
ssize_t lv_totalCountWritten = 0;
void *lp_currBuffPtr = (void *) pp_Buf;
ssize_t lv_writeSize = pv_Count;
while (!lv_WriteCompleted)
{
lp_Efh->SetCurrentFragmentBasedOnCurrOffset();
ssize_t lv_Stat = lp_Efh->Write( lp_currBuffPtr,
lv_writeSize);
if (lv_Stat == lv_writeSize) {
// The usual case: we wrote everything as expected. Prepare
// to exit the loop and go home for the day!
lv_totalCountWritten += lv_writeSize;
lv_WriteCompleted = true;
}
else if (lv_Stat < 0) {
// Hmm, do we need to create a new fragment?
if (errno == ENOSPC) {
//gotcha! Create a new fragment and continue writing the
//buffer
STFS_ExternalFileMetadata *lp_EFM = lp_Efh->EfmGet();
// make sure that the lib has the same opinion as the
// daemon on the new fragment's offset
size_t lv_newFragExpectedOffset = lp_Efh->CurrentOffsetGet();
#ifdef SQ_PACK
STFS_OpenIdentifier lp_openIdentifier = lp_Efh->OpenIdentifierGet();
lv_Stat = SendToSTFSd_createFrag(lp_EFM,
STFS_util::GetMyNodeId(),
STFS_util::GetMyPID(),
&lp_openIdentifier);
#else
lv_Stat = STFSd_createFragment(lp_EFM,
STFS_util::GetMyNodeId(),
STFS_util::GetMyPID(),
lp_Efh->CurrentOffsetGet());
#endif
if (lv_Stat < 0) {
// Fragment not created. Treat it like a normal ENOSPC
// error after all. If the error on the createFrag was
// something else, then we'll hope that it was put in
// the open error structure...
errno = ENOSPC;
}
else {
// open the new fragment
lv_Stat = lp_Efh->OpenNewFragments();
if (lv_Stat < 0) {
errno=ENOSPC;
}
// Now check to see if the new fragment has the correct
// starting offset. The new fragment isn't yet the
// current one, so we have to extract the current number
// of fragments, use it as the index to get the last
// fragment's FFM, and call OffsetGet on that fragment.
size_t lv_newFragRealStartOffset = lp_Efh->GetLastFFMStartOffset();
if (lv_newFragRealStartOffset != lv_newFragExpectedOffset) {
// Something's wrong because the daemon has a
// different opinion on the previous fragment's
// size or our calculations are out of sync. This
// is a data integrity problem
// This code should really be fixed to change the
// if condition to be a Permassert or the
// following Assert (false) to kill the library
// process.
STFS_util::SoftwareFailureHandler (WHERE);
}
}
}
if (lv_Stat < 0) {
// we've got a real, live error here!
// before we just return this error, check to see if we've
// written anything! If so, we want to return that
// value. It's up to the library caller to decide whether
// to retry the I/O in that case.
if (lv_totalCountWritten > 0) {
return lv_totalCountWritten;
}
else {
// a real error! Make sure errno wasn't overwritten by
// this point! Any additional information or subsequent
// errors should be stuffed in the error structure by
// lower calls.
return -1;
}
} // Not ENOSPC check
} // lv_Stat < 0 check
else {
//Rev. Following line is comparing signed vs unsigned.
ASSERT ((size_t)(lv_totalCountWritten+lv_Stat) < pv_Count);
// partial write! Hmmmmm, might mean we need a new fragment
// (or it might mean there was partial write). We need to
// save the number of bytes we wrote and then try again to
// write the data. We also have to save our current
// location in the buffer and calculate the new number of
// bytes to write.
// Careful here: it's possible that a single large write
// might create more than one fragment! Make sure that
// whatever code gets added uses lv_CountWritten and lv_Stat
// with the proper context.
lv_totalCountWritten += lv_Stat;
lp_currBuffPtr = (char *)lp_currBuffPtr + lv_Stat;
lv_writeSize -= lv_Stat;
}
} // While !lv_WriteCompleted
return lv_totalCountWritten;
}
} //namespace STFS