beegfs/meta/source/storage/DirEntryStore.cpp
2025-08-10 01:34:16 +02:00

778 lines
24 KiB
C++

#include <program/Program.h>
#include <toolkit/StorageTkEx.h>
#include "DirEntryStore.h"
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/**
* Get a dir-entry-path
*
* Note: Not supposed to be used outside of this file and shall be inlined
*/
static inline std::string getDirEntryStoreDynamicEntryPath(const std::string& parentID,
bool isBuddyMirrored)
{
App* app = Program::getApp();
if ( isBuddyMirrored )
return MetaStorageTk::getMetaDirEntryPath(
app->getBuddyMirrorDentriesPath()->str(), parentID);
else
return MetaStorageTk::getMetaDirEntryPath(app->getDentriesPath()->str(),
parentID);
}
/**
* Note: Sets the parentID to an invalid value, so do not forget to set the parentID before
* adding any elements.
*/
DirEntryStore::DirEntryStore() :
parentID("<undef>"), isBuddyMirrored(false)
{
}
/**
* @param parentID ID of the directory to which this store belongs
* @param isBuddyMirrored true if the directory to which this store belongs is buddy mirrored
*/
DirEntryStore::DirEntryStore(const std::string& parentID, bool isBuddyMirrored) :
parentID(parentID), dirEntryPath(getDirEntryStoreDynamicEntryPath(parentID, isBuddyMirrored) ),
isBuddyMirrored(isBuddyMirrored)
{
}
/*
* Create the new directory for dentries (dir-entries). This directory will contain directory
* entries of files and sub-directories of the directory given by dirID.
*/
FhgfsOpsErr DirEntryStore::mkDentryStoreDir(const std::string& dirID, bool isBuddyMirrored)
{
const char* logContext = "Directory (store initial metadata dir)";
App* app = Program::getApp();
const Path* dentriesPath =
isBuddyMirrored ? app->getBuddyMirrorDentriesPath() : app->getDentriesPath();
std::string contentsDirStr = MetaStorageTk::getMetaDirEntryPath(
dentriesPath->str(), dirID);
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
// create (contents) directory, which will hold directory entries of subdirs and subfiles
int mkDirRes = mkdir(contentsDirStr.c_str(), 0755);
if(mkDirRes)
{ // error
if(errno == EEXIST)
retVal = FhgfsOpsErr_EXISTS;
else
{
LogContext(logContext).logErr("Unable to create contents directory: " + contentsDirStr +
". " + "SysErr: " + System::getErrString() );
retVal = FhgfsOpsErr_INTERNAL;
}
return retVal;
}
LOG_DEBUG(logContext, 4, "Metadata dir created: " + contentsDirStr);
// create the dirEntryID directory, which allows access to inlined inodes via dirID access
std::string contentsDirIDStr = MetaStorageTk::getMetaDirEntryIDPath(contentsDirStr);
int mkDirIDRes = mkdir(contentsDirIDStr.c_str(), 0755);
if(mkDirIDRes)
{ // So contentsDirStr worked, but creating the sub directory contentsDirIDStr failed
LogContext(logContext).logErr("Unable to create dirEntryID directory: " +
contentsDirIDStr + ". " + "SysErr: " + System::getErrString() );
retVal = FhgfsOpsErr_INTERNAL;
int unlinkRes = unlink(contentsDirStr.c_str() );
if (unlinkRes)
{
// we can only write a log message here, but can't do anything about it
LogContext(logContext).logErr("Failed to remove: " + contentsDirStr +
". " + "SysErr: " + System::getErrString() );
}
return retVal;
}
if (isBuddyMirrored)
if (auto* resync = BuddyResyncer::getSyncChangeset())
{
resync->addModification(contentsDirStr, MetaSyncFileType::Directory);
resync->addModification(contentsDirIDStr, MetaSyncFileType::Directory);
}
return retVal;
}
/**
* Remove directory for dir-entries (dentries)
*
* Note: Assumes that the caller already verified that the directory is empty
*/
bool DirEntryStore::rmDirEntryStoreDir(const std::string& id, bool isBuddyMirrored)
{
const char* logContext = "Directory (remove contents dir)";
App* app = Program::getApp();
const Path* dentriesPath =
isBuddyMirrored ? app->getBuddyMirrorDentriesPath() : app->getDentriesPath();
std::string contentsDirStr = MetaStorageTk::getMetaDirEntryPath(
dentriesPath->str(), id);
std::string contentsDirIDStr = MetaStorageTk::getMetaDirEntryIDPath(contentsDirStr);
LOG_DEBUG(logContext, Log_DEBUG,
"Removing content directory: " + contentsDirStr + "; " "id: " + id + "; isBuddyMirrored: "
+ StringTk::intToStr(isBuddyMirrored));
// remove the dirEntryID directory
int rmdirIdRes = rmdir(contentsDirIDStr.c_str() );
if(rmdirIdRes)
{ // error
LogContext(logContext).logErr("Unable to delete dirEntryID directory: " + contentsDirIDStr +
". " + "SysErr: " + System::getErrString() );
if (errno != ENOENT)
return false;
}
if (isBuddyMirrored)
if (auto* resync = BuddyResyncer::getSyncChangeset())
resync->addDeletion(contentsDirIDStr, MetaSyncFileType::Directory);
// remove contents directory
int rmdirRes = rmdir(contentsDirStr.c_str() );
if(rmdirRes)
{ // error
LogContext(logContext).logErr("Unable to delete contents directory: " + contentsDirStr +
". " + "SysErr: " + System::getErrString() );
if (errno != ENOENT)
return false;
}
LOG_DEBUG(logContext, 4, "Contents directory deleted: " + contentsDirStr);
if (isBuddyMirrored)
if (auto* resync = BuddyResyncer::getSyncChangeset())
resync->addDeletion(contentsDirStr, MetaSyncFileType::Directory);
return true;
}
/**
* @param file belongs to the store after calling this method - so do not free it and don't
* use it any more afterwards (re-get it from this store if you need it)
*/
FhgfsOpsErr DirEntryStore::makeEntry(DirEntry* entry)
{
FhgfsOpsErr mkRes;
SafeRWLock safeLock(&rwlock, SafeRWLock_WRITE);
mkRes = makeEntryUnlocked(entry);
safeLock.unlock();
return mkRes;
}
/**
* @param file belongs to the store after calling this method - so do not free it and don't
* use it any more afterwards (re-get it from this store if you need it)
*/
FhgfsOpsErr DirEntryStore::makeEntryUnlocked(DirEntry* entry)
{
const std::string& dirEntryPath = getDirEntryPathUnlocked();
const char* logContext = "make meta dir-entry";
FhgfsOpsErr mkRes = entry->storeInitialDirEntry(dirEntryPath);
if (unlikely(mkRes != FhgfsOpsErr_SUCCESS) && mkRes != FhgfsOpsErr_EXISTS)
LogContext(logContext).logErr(std::string("Failed to create: name: ") + entry->getName() +
std::string(" entryID: ") + entry->getID() + " in path: " + dirEntryPath);
return mkRes;
}
/**
* Create a new dir-entry based on an inode, which is stored in dir-entry format
* (with inlined inode)
*/
FhgfsOpsErr DirEntryStore::linkInodeToDir(const std::string& inodePath, const std::string &fileName)
{
SafeRWLock safeLock(&rwlock, SafeRWLock_WRITE);
FhgfsOpsErr retVal = linkInodeToDirUnlocked(inodePath, fileName);
safeLock.unlock();
return retVal;
}
FhgfsOpsErr DirEntryStore::linkInodeToDirUnlocked(const std::string& inodePath,
const std::string& fileName)
{
const char* logContext = "make meta dir-entry";
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
std::string dirEntryPath = getDirEntryPathUnlocked() + '/' + fileName;
int linkRes = link(inodePath.c_str(), dirEntryPath.c_str() );
if (linkRes)
{
LogContext(logContext).logErr(std::string("Failed to create link from: ") + inodePath +
" To: " + dirEntryPath + " SysErr: " + System::getErrString() );
retVal = FhgfsOpsErr_INTERNAL;
}
if (getIsBuddyMirrored())
if (auto* resync = BuddyResyncer::getSyncChangeset())
resync->addModification(dirEntryPath, MetaSyncFileType::Inode);
return retVal;
}
/**
* note: only for dir dentries
*
* @param outDirEntry is object of the removed dirEntry, maybe NULL of the caller does not need it
*/
FhgfsOpsErr DirEntryStore::removeDir(const std::string& entryName, DirEntry** outDirEntry)
{
SafeRWLock safeLock(&rwlock, SafeRWLock_WRITE);
FhgfsOpsErr delErr = removeDirUnlocked(entryName, outDirEntry);
safeLock.unlock();
return delErr;
}
/**
* note: only for file dentries
*
* @param unlinkEntryName If false do not try to unlink the dentry-name entry, entryName even
* might not be set.
* @param outEntry will be set to the unlinked file and the object must then be deleted by the
* caller (can be NULL if the caller is not interested in the file)
*/
FhgfsOpsErr DirEntryStore::unlinkDirEntry(const std::string& entryName, DirEntry* entry,
unsigned unlinkTypeFlags)
{
SafeRWLock safeLock(&rwlock, SafeRWLock_WRITE);
FhgfsOpsErr delErr = unlinkDirEntryUnlocked(entryName, entry, unlinkTypeFlags);
safeLock.unlock();
return delErr;
}
/**
* Note: only for dir dentries
* Note: This does not lock the mutex, so it must already be locked when calling this.
*
* @param outDirEntry is object of the removed dirEntry, maybe NULL of the caller does not need it
*/
FhgfsOpsErr DirEntryStore::removeDirUnlocked(const std::string& entryName, DirEntry** outDirEntry)
{
SAFE_ASSIGN(outDirEntry, NULL);
DirEntry* entry = DirEntry::createFromFile(getDirEntryPathUnlocked(), entryName);
if(!entry)
return FhgfsOpsErr_PATHNOTEXISTS;
if(!DirEntryType_ISDIR(entry->getEntryType() ) )
{
delete(entry);
return FhgfsOpsErr_PATHNOTEXISTS;
}
FhgfsOpsErr retVal = DirEntry::removeDirDentry(getDirEntryPathUnlocked(), entryName,
entry->getIsBuddyMirrored());
if (outDirEntry)
*outDirEntry = entry;
else
delete(entry);
return retVal;
}
/**
* Note: only for file dentries
* Note: This does not lock the mutex, so it must already be locked when calling this.
*
* @param inEntry might be NULL and the entry then needs to be loaded from disk.
* @param unlinkEntryName If false do not try to unlink the dentry-name entry, entryName even
* might not be set. So !unlinkEntryName and !inEntry together are a logical conflict.
* @param outEntry will be set to the unlinked file and the object must then be deleted by the
* caller (can be NULL if the caller is not interested in the file). Also will not be set if
* inEntry is not NULL, as the caller already knows the DirEntry then.
* @param outEntryID for callers that need the entryID and want to avoid the overhead of using
* outEntry (can be NULL if the caller is not interested); only set on success
*/
FhgfsOpsErr DirEntryStore::unlinkDirEntryUnlocked(const std::string& entryName, DirEntry* entry,
unsigned unlinkTypeFlags)
{
FhgfsOpsErr delErr = DirEntry::removeFileDentry(getDirEntryPathUnlocked(), entry->getID(),
entryName, unlinkTypeFlags, entry->getIsBuddyMirrored());
return delErr;
}
/**
* Create a hardlink from 'fromEntryName' to 'toEntryName'.
*
* NOTE: Only do this for the entries in the same directory with inlined inodes. In order to avoid
* a wrong link count on a possible crash/reboot, the link count already must have been
* increased by 1 (If the link count is too high after a crash we might end up with leaked
* chunk files on unlink, but not with missing chunk files if done the other way around).
*/
FhgfsOpsErr DirEntryStore::linkEntryInDir(const std::string& fromEntryName,
const std::string& toEntryName)
{
const char *logContext = "DirEntryStore renameEntry";
SafeRWLock safeLock(&rwlock, SafeRWLock_WRITE);
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
std::string fromPath = getDirEntryPathUnlocked() + '/' + fromEntryName;
std::string toPath = getDirEntryPathUnlocked() + '/' + toEntryName;
int linkRes = link(fromPath.c_str(), toPath.c_str() );
if (linkRes)
{
if (errno == EEXIST)
retVal = FhgfsOpsErr_EXISTS;
else
{
LogContext(logContext).logErr(std::string("Failed to link file in dir: ") +
getDirEntryPathUnlocked() + " from: " + fromEntryName + " to: " + toEntryName +
". SysErr: " + System::getErrString() );
retVal = FhgfsOpsErr_INTERNAL;
}
}
safeLock.unlock();
if (getIsBuddyMirrored())
if (auto* resync = BuddyResyncer::getSyncChangeset())
resync->addModification(toPath, MetaSyncFileType::Dentry);
return retVal;
}
/**
* In constrast to the moving...()-methods, this method performs a simple rename of an entry,
* where no moving is involved.
*
* @param outRemovedEntry accoring to the rules, this can only be an overwritten file, not a dir
*
* NOTE: The caller already must have done rename-sanity checks.
*/
FhgfsOpsErr DirEntryStore::renameEntry(const std::string& fromEntryName,
const std::string& toEntryName)
{
const char *logContext = "DirEntryStore renameEntry";
SafeRWLock safeLock(&rwlock, SafeRWLock_WRITE);
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
std::string fromPath = getDirEntryPathUnlocked() + '/' + fromEntryName;
std::string toPath = getDirEntryPathUnlocked() + '/' + toEntryName;
int renameRes = rename(fromPath.c_str(), toPath.c_str() );
if (renameRes)
{
LogContext(logContext).logErr(std::string("Failed to rename file in dir: ") +
getDirEntryPathUnlocked() + " from: " + fromEntryName + " to: " + toEntryName +
". SysErr: " + System::getErrString() );
retVal = FhgfsOpsErr_INTERNAL;
}
safeLock.unlock();
if (isBuddyMirrored)
if (auto* resync = BuddyResyncer::getSyncChangeset())
{
resync->addDeletion(fromPath, MetaSyncFileType::Dentry);
resync->addModification(toPath, MetaSyncFileType::Dentry);
}
return retVal;
}
/**
* Note: serverOffset is an internal value and should not be assumed to be just 0, 1, 2, 3, ...;
* so make sure you use either 0 (at the beginning) or something that has been returned by this
* method as offset (similar to posix telldir/seekdir).
*
* Note: You have reached the end of the directory when success is returned and
* "outNames.size() != maxOutNames".
*
* @param serverOffset zero-based offset; represents the native local fs offset (as in telldir() ).
* @param filterDots true if "." and ".." should not be returned.
* @param outArgs outNewOffset is only valid if return value indicates success,
* outEntryTypes and outEntryIDs may be NULL, the rest is required.
*/
FhgfsOpsErr DirEntryStore::listIncrementalEx(int64_t serverOffset,
unsigned maxOutNames, bool filterDots, ListIncExOutArgs& outArgs)
{
// note: we need offsets here that are stable after unlink, because apps like bonnie++ use
// readdir(), then unlink() the returned files and expect readdir() to continue after that.
// this won't work if we use our own offset number and skip the given number of initial
// entries each time. that's why we use the native local file system offset and seek here.
const char* logContext = "DirEntryStore (list inc)";
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
uint64_t numEntries = 0;
struct dirent* dirEntry = NULL;
SafeRWLock safeLock(&rwlock, SafeRWLock_READ); // L O C K
DIR* dirHandle = opendir(getDirEntryPathUnlocked().c_str() );
if(!dirHandle)
{
LogContext(logContext).logErr(std::string("Unable to open dentry directory: ") +
getDirEntryPathUnlocked() + ". SysErr: " + System::getErrString() );
goto err_unlock;
}
// seek to offset (if provided)
if(serverOffset)
{
seekdir(dirHandle, serverOffset); // (seekdir has no return value)
}
// loop over the actual directory entries
for( ;
(numEntries < maxOutNames) &&
(dirEntry = StorageTk::readdirFilteredEx(dirHandle, filterDots, true) );
numEntries++)
{
outArgs.outNames->push_back(dirEntry->d_name);
if(outArgs.outServerOffsets)
outArgs.outServerOffsets->push_back(dirEntry->d_off);
SAFE_ASSIGN(outArgs.outNewServerOffset, dirEntry->d_off);
if(outArgs.outEntryTypes || outArgs.outEntryIDs)
{
DirEntryType entryType;
std::string entryID;
if(!filterDots && !strcmp(dirEntry->d_name, ".") )
{
entryType = DirEntryType_DIRECTORY;
entryID = "<.>";
}
else
if(!filterDots && !strcmp(dirEntry->d_name, "..") )
{
entryType = DirEntryType_DIRECTORY;
entryID = "<..>";
}
else
{ // load dentry metadata
DirEntry entry(dirEntry->d_name);
bool loadSuccess = entry.loadFromFileName(getDirEntryPathUnlocked(), dirEntry->d_name);
if (likely(loadSuccess) )
{
entryType = entry.getEntryType();
entryID = entry.getEntryID();
}
else
{ // loading failed
entryType = DirEntryType_INVALID;
entryID = "<invalid>";
errno = 0;
}
}
if(outArgs.outEntryTypes)
outArgs.outEntryTypes->push_back( (int)entryType);
if (outArgs.outEntryIDs)
outArgs.outEntryIDs->push_back(entryID);
}
}
if(!dirEntry && errno)
{
LogContext(logContext).logErr(std::string("Unable to fetch links directory entry from: ") +
getDirEntryPathUnlocked() + ". SysErr: " + System::getErrString() );
}
else
{ // all entries read
retVal = FhgfsOpsErr_SUCCESS;
}
closedir(dirHandle);
err_unlock:
safeLock.unlock(); // U N L O C K
return retVal;
}
/**
* Note: serverOffset is an internal value and should not be assumed to be just 0, 1, 2, 3, ...;
* so make sure you use either 0 (at the beginning) or something that has been returned by this
* method as outNewOffset.
* Note: You have reached the end of the directory when "outNames.size() != maxOutNames".
* Note: This function was written for fsck
*
* @param serverOffset zero-based offset; represents the native local fs offset; preferred over
* incrementalOffset; use -1 here if you want to seek to e.g. the n-th element and use the slow
* incrementalOffset for that case.
* @param incrementalOffset zero-based offset; only used if serverOffset is -1; skips the given
* number of entries.
* @param outArgs outNewOffset is only valid if return value indicates success, outEntryTypes is
* not used (NULL), outNames is required.
*/
FhgfsOpsErr DirEntryStore::listIDFilesIncremental(int64_t serverOffset,
uint64_t incrementalOffset, unsigned maxOutNames, ListIncExOutArgs& outArgs)
{
// note: we need offsets here that are stable after unlink, because apps like bonnie++ use
// readdir(), then unlink() the returned files and expect readdir() to continue after that.
// this won't work if we use our own offset number and skip the given number of initial
// entries each time. that's why we use the native local file system offset and seek here.
// (incrementalOffset is only provided as fallback for client directory seeks and should
// be avoided when possible.)
const char* logContext = "DirEntryStore (list ID files inc)";
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
uint64_t numEntries = 0;
struct dirent* dirEntry = NULL;
SafeRWLock safeLock(&rwlock, SafeRWLock_READ); // L O C K
std::string path = MetaStorageTk::getMetaDirEntryIDPath(getDirEntryPathUnlocked());
DIR* dirHandle = opendir(path.c_str() );
if(!dirHandle)
{
LogContext(logContext).logErr(std::string("Unable to open dentry-by-ID directory: ") +
path + ". SysErr: " + System::getErrString() );
goto err_unlock;
}
errno = 0; // recommended by posix (readdir(3p) )
// seek to offset
if(serverOffset != -1)
{ // caller provided direct offset
seekdir(dirHandle, serverOffset); // (seekdir has no return value)
}
else
{ // slow path: incremental seek to current offset
for(uint64_t currentOffset = 0;
(currentOffset < incrementalOffset) && (dirEntry=StorageTk::readdirFiltered(dirHandle) );
currentOffset++)
{
// (actual seek work done in loop header)
*outArgs.outNewServerOffset = dirEntry->d_off;
}
}
// the actual entry reading
for( ; (numEntries < maxOutNames) && (dirEntry = StorageTk::readdirFiltered(dirHandle) );
numEntries++)
{
outArgs.outNames->push_back(dirEntry->d_name);
*outArgs.outNewServerOffset = dirEntry->d_off;
}
if(!dirEntry && errno)
{
LogContext(logContext).logErr(std::string("Unable to fetch dentry-by-ID entry from: ") +
path + ". SysErr: " + System::getErrString() );
}
else
{ // all entries read
retVal = FhgfsOpsErr_SUCCESS;
}
closedir(dirHandle);
err_unlock:
safeLock.unlock(); // U N L O C K
return retVal;
}
bool DirEntryStore::exists(const std::string& entryName)
{
bool existsRes = false;
SafeRWLock safeLock(&rwlock, SafeRWLock_READ); // L O C K
existsRes = existsUnlocked(entryName);
safeLock.unlock(); // U N L O C K
return existsRes;
}
bool DirEntryStore::existsUnlocked(const std::string& entryName)
{
bool existsRes = false;
std::string filepath = getDirEntryPathUnlocked() + "/" + entryName;
struct stat statBuf;
int statRes = stat(filepath.c_str(), &statBuf);
if(!statRes)
existsRes = true;
return existsRes;
}
/**
* @param outInodeMetaData might be NULL
* @return DirEntryType_INVALID if no such dir exists
*
*/
FhgfsOpsErr DirEntryStore::getEntryData(const std::string& entryName, EntryInfo* outInfo,
FileInodeStoreData* outInodeMetaData)
{
FhgfsOpsErr retVal = FhgfsOpsErr_PATHNOTEXISTS;
SafeRWLock safeLock(&rwlock, SafeRWLock_READ); // L O C K
DirEntry entry(entryName);
bool loadRes = entry.loadFromFileName(getDirEntryPathUnlocked(), entryName);
if(loadRes)
{
/* copy FileInodeStoreData from entry to outInodeMetaData. We also do not want to allocate
* a new stripe pattern, so we simply copy the pointer and set
* entry.dentryInodeMeta.stripePattern = NULL. So on destruction of the object, it will not
* be deleted. */
FileInodeStoreData* inodeDiskData = entry.getInodeStoreData();
if (outInodeMetaData)
{
*outInodeMetaData = *inodeDiskData;
inodeDiskData->setPattern(NULL);
}
int flags = entry.getIsInodeInlined() ? ENTRYINFO_FEATURE_INLINED : 0;
if (entry.getIsBuddyMirrored())
flags |= ENTRYINFO_FEATURE_BUDDYMIRRORED;
NumNodeID ownerNodeID = entry.getOwnerNodeID();
std::string entryID = entry.getID();
outInfo->set(ownerNodeID, this->parentID, entryID, entryName,
entry.getEntryType(), flags);
retVal = FhgfsOpsErr_SUCCESS;
}
safeLock.unlock(); // U N L O C K
return retVal;
}
/**
* Load and return the dir-entry of the given entry-/fileName
*/
DirEntry* DirEntryStore::dirEntryCreateFromFile(const std::string& entryName)
{
SafeRWLock safeLock(&rwlock, SafeRWLock_READ); // L O C K
DirEntry* dirEntry= DirEntry::createFromFile(this->getDirEntryPathUnlocked(), entryName);
safeLock.unlock(); // U N L O C K
return dirEntry;
}
/**
* Note: Unlocked, so make sure you hold the lock when calling this.
*/
const std::string& DirEntryStore::getDirEntryPathUnlocked() const
{
return dirEntryPath;
}
/**
* @param entryName
* @param ownerNode
*
* @return FhgfsOpsErr based on the result of the operation
*/
FhgfsOpsErr DirEntryStore::setOwnerNodeID(const std::string& entryName, NumNodeID ownerNode)
{
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
DirEntry entry(entryName);
bool loadRes;
SafeRWLock safeLock(&rwlock, SafeRWLock_WRITE);
loadRes = entry.loadFromFileName(getDirEntryPathUnlocked(), entryName);
if(!loadRes)
{ // no such entry
retVal = FhgfsOpsErr_PATHNOTEXISTS;
}
else
{
if ( entry.setOwnerNodeID(getDirEntryPathUnlocked(), ownerNode) )
{
retVal = FhgfsOpsErr_SUCCESS;
}
}
safeLock.unlock();
return retVal;
}
/**
* Note: Unlocked. Use this only during init.
*/
void DirEntryStore::setParentID(const std::string& parentID, bool parentIsBuddyMirrored)
{
this->parentID = parentID;
this->dirEntryPath = getDirEntryStoreDynamicEntryPath(parentID, parentIsBuddyMirrored);
this->isBuddyMirrored = parentIsBuddyMirrored;
}