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

870 lines
26 KiB
C++

#include <common/toolkit/serialization/Serialization.h>
#include <components/buddyresyncer/BuddyResyncer.h>
#include <program/Program.h>
#include "MetadataEx.h"
#include "DirEntry.h"
#include <sys/xattr.h>
/*
* Store the dirEntryID file. This is a normal dirEntry (with inlined inode),
* but the file name is the entryID.
*
* @param logContext
* @param idPath - path to the idFile, including the file name
*/
FhgfsOpsErr DirEntry::storeInitialDirEntryID(const char* logContext, const std::string& idPath)
{
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
char buf[DIRENTRY_SERBUF_SIZE];
Serializer ser(buf, sizeof(buf));
bool useXAttrs = Program::getApp()->getConfig()->getStoreUseExtendedAttribs();
// create file
/* note: if we ever think about switching to a rename-based version here, we must keep very
long user file names in mind, which might lead to problems if we add an extension to the
temporary file name. */
int openFlags = O_CREAT|O_EXCL|O_WRONLY;
int fd = open(idPath.c_str(), openFlags, 0644);
if (unlikely (fd == -1) ) // this is our ID file, failing to create it is very unlikely
{ // error
LogContext(logContext).logErr("Unable to create dentry file: " + idPath + ". " +
"SysErr: " + System::getErrString() );
if (errno == EMFILE)
{ /* Creating the file succeeded, but there are already too many open file descriptors to
* open the file. We don't want to leak an entry-by-id file, so delete it.
* We only want to delete the file for specific errors, as for example EEXIST would mean
* we would delete an existing (probably) working entry. */
int unlinkRes = unlink(idPath.c_str() );
if (unlinkRes && errno != ENOENT)
LogContext(logContext).logErr("Failed to unlink failed dentry: " + idPath + ". " +
"SysErr: " + System::getErrString() );
}
if (errno == EEXIST)
{
/* EEXIST never should happen, as our ID is supposed to be unique, but there rare cases
* as for the upgrade tool */
retVal = FhgfsOpsErr_EXISTS;
#ifdef BEEGFS_DEBUG
LogContext(logContext).logBacktrace();
#endif
}
else
{
retVal = FhgfsOpsErr_INTERNAL;
}
return retVal;
}
// serialize (to new buf)
serializeDentry(ser);
if (!ser.good())
{
LogContext(logContext).logErr("Dentry too large: " + idPath + ".");
retVal = FhgfsOpsErr_INTERNAL;
}
// write buf to file
if(useXAttrs)
{ // extended attribute
int setRes = fsetxattr(fd, META_XATTR_NAME, buf, ser.size(), 0);
if(unlikely(setRes == -1) )
{ // error
LogContext(logContext).logErr("Unable to store dentry xattr metadata: " + idPath + ". " +
"SysErr: " + System::getErrString() );
retVal = FhgfsOpsErr_INTERNAL;
goto error_closefile;
}
}
else
{ // normal file content
ssize_t writeRes = write(fd, buf, ser.size());
if(unlikely(writeRes != (ssize_t)ser.size()))
{ // error
LogContext(logContext).logErr("Unable to store dentry metadata: " + idPath + ". " +
"SysErr: " + System::getErrString() );
retVal = FhgfsOpsErr_INTERNAL;
goto error_closefile;
}
}
close(fd);
return retVal;
// error compensation
error_closefile:
close(fd);
int unlinkRes = unlink(idPath.c_str() );
if (unlikely(unlinkRes && errno != ENOENT) )
{
LogContext(logContext).logErr("Creating the dentry-by-name file failed and"
"now also deleting the dentry-by-id file fails: " + idPath);
}
return retVal;
}
/**
* Store the dirEntry as file name
*/
FhgfsOpsErr DirEntry::storeInitialDirEntryName(const char* logContext, const std::string& idPath,
const std::string& namePath, bool isNonInlinedInode)
{
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
int linkRes = link(idPath.c_str(), namePath.c_str() );
if (linkRes)
{ /* Creating the dirEntry-by-name failed, most likely this is EEXIST.
* In principle it also might be possible there is an invalid dentry-by-name file,
* however, we already want to delete those during lookup calls now. So invalid
* entries are supposed to be very very unlikely and so no self-healing code is
* implemented here. */
if (likely(errno == EEXIST) )
retVal = FhgfsOpsErr_EXISTS;
else
{
LogContext(logContext).logErr("Creating the dentry-by-name file failed: Path: " +
namePath + " SysErr: " + System::getErrString() );
retVal = FhgfsOpsErr_INTERNAL;
}
int unlinkRes = unlink(idPath.c_str() );
if (unlikely(unlinkRes) )
{
LogContext(logContext).logErr("Creating the dentry-by-name file failed and"
"now also deleting the dentry-by-id file fails: " + idPath);
}
return retVal;
}
if (isNonInlinedInode)
{
// unlink the dentry-by-id file - we don't need it for dirs (or non-inlined inodes in general)
int unlinkRes = unlink(idPath.c_str() );
if (unlikely(unlinkRes) )
{
LogContext(logContext).logErr("Failed to unlink the (dir) dentry-by-id file "+ idPath +
" SysErr: " + System::getErrString() );
}
}
LOG_DEBUG(logContext, 4, "Initial dirEntry stored: " + namePath);
return retVal;
}
/**
* Note: Wrapper/chooser for storeUpdatedDirEntryBufAsXAttr/Contents.
*
* @param buf the serialized object state that is to be stored
*/
bool DirEntry::storeUpdatedDirEntryBuf(const std::string& idStorePath, char* buf, unsigned bufLen)
{
bool useXAttrs = Program::getApp()->getConfig()->getStoreUseExtendedAttribs();
bool result = useXAttrs
? storeUpdatedDirEntryBufAsXAttr(idStorePath, buf, bufLen)
: storeUpdatedDirEntryBufAsContents(idStorePath, buf, bufLen);
return result;
}
/**
* Note: Don't call this directly, use the wrapper storeUpdatedDirEntryBuf().
*
* @param buf the serialized object state that is to be stored
*/
bool DirEntry::storeUpdatedDirEntryBufAsXAttr(const std::string& idStorePath,
char* buf, unsigned bufLen)
{
const char* logContext = DIRENTRY_LOG_CONTEXT "(store updated xattr metadata)";
// write data to file
int setRes = setxattr(idStorePath.c_str(), META_XATTR_NAME, buf, bufLen, 0);
if(unlikely(setRes == -1) )
{ // error
LogContext(logContext).logErr("Unable to write dentry update: " +
idStorePath + ". " + "SysErr: " + System::getErrString() );
return false;
}
LOG_DEBUG(logContext, 4, "Dentry update stored: " + idStorePath);
return true;
}
/**
* Stores the update directly to the current metadata file (instead of creating a separate file
* first and renaming it).
*
* Note: Don't call this directly, use the wrapper storeUpdatedDirEntryBuf().
*
* @param buf the serialized object state that is to be stored
*/
bool DirEntry::storeUpdatedDirEntryBufAsContents(const std::string& idStorePath, char* buf,
unsigned bufLen)
{
const char* logContext = DIRENTRY_LOG_CONTEXT "(store updated metadata in-place)";
int fallocRes;
ssize_t writeRes;
int truncRes;
// open file (create it, but not O_EXCL because a former update could have failed)
int openFlags = O_CREAT|O_WRONLY;
int fd = open(idStorePath.c_str(), openFlags, 0644);
if(fd == -1)
{ // error
LogContext(logContext).logErr("Unable to create dentry metadata update file: " +
idStorePath + ". " + "SysErr: " + System::getErrString() );
return false;
}
// make sure we have enough room to write our update
fallocRes = posix_fallocate(fd, 0, bufLen); // (note: posix_fallocate does not set errno)
if(fallocRes == EBADF)
{ // special case for XFS bug
struct stat statBuf;
int statRes = fstat(fd, &statBuf);
if (statRes == -1)
{
LogContext(logContext).log(Log_WARNING, "Unexpected error: fstat() failed with SysErr: "
+ System::getErrString(errno) );
goto error_closefile;
}
if (statBuf.st_size < bufLen)
{
LogContext(logContext).log(Log_WARNING, "File space allocation ("
+ StringTk::intToStr(bufLen) + ") for metadata update failed: " + idStorePath + ". " +
"SysErr: " + System::getErrString(fallocRes) + " "
"statRes: " + StringTk::intToStr(statRes) + " "
"oldSize: " + StringTk::intToStr(statBuf.st_size) );
goto error_closefile;
}
else
{ // // XFS bug! We only return an error if statBuf.st_size < bufLen. Ingore fallocRes then
LOG_DEBUG(logContext, Log_SPAM, "Ignoring kernel file system bug: "
"posix_fallocate() failed for len < filesize");
}
}
else
if (fallocRes != 0)
{ // default error handling if posix_fallocate() failed
LogContext(logContext).log(Log_WARNING, "File space allocation ("
+ StringTk::intToStr(bufLen) + ") for metadata update failed: " + idStorePath + ". " +
"SysErr: " + System::getErrString(fallocRes) );
goto error_closefile;
}
// write data to file
writeRes = write(fd, buf, bufLen);
if(unlikely(writeRes != (ssize_t)bufLen) )
{ // error
LogContext(logContext).logErr("Unable to write dentry metadata update: " +
idStorePath + ". " + "SysErr: " + System::getErrString() );
goto error_closefile;
}
// truncate in case the update lead to a smaller file size
truncRes = ftruncate(fd, bufLen);
if(truncRes == -1)
{ // ignore trunc errors
LogContext(logContext).log(Log_WARNING, "Unable to truncate metadata file (strange, but "
"proceeding anyways): " + idStorePath + ". " + "SysErr: " + System::getErrString() );
}
close(fd);
LOG_DEBUG(logContext, 4, "Dentry metadata update stored: " + idStorePath);
return true;
// error compensation
error_closefile:
close(fd);
return false;
}
/**
* Store an update DirEntry.
*
* Note: Never write to a dentry using the entryNamePath. We might simply write to the wrong
* path. E.g. after a rename() and overwriting an opened file. Closing the overwritten
* file will result in an inode update. If then some data structures are not correct
* yet, writing to dentry-by-name instead of dentry-by-id might update the wrong file,
* instead of simply returning an error message.
* However, an exception is fsck, which needs to modify the dentry-by-name directly to update
* a dentry-owner.
*/
bool DirEntry::storeUpdatedDirEntry(const std::string& dirEntryPath)
{
char buf[DIRENTRY_SERBUF_SIZE];
Serializer ser(buf, sizeof(buf));
serializeDentry(ser);
std::string idStorePath = dirEntryPath + "/" + name;
bool result = storeUpdatedDirEntryBuf(idStorePath, buf, ser.size());
if (getIsBuddyMirrored())
if (auto* resync = BuddyResyncer::getSyncChangeset())
resync->addModification(idStorePath, MetaSyncFileType::Dentry);
return result;
}
/**
* Store the inlined inode from a dir-entry.
* */
FhgfsOpsErr DirEntry::storeUpdatedInode(const std::string& dirEntryPath)
{
if (!this->getIsInodeInlined() )
return FhgfsOpsErr_INODENOTINLINED;
char buf[DIRENTRY_SERBUF_SIZE];
Serializer ser(buf, sizeof(buf));
serializeDentry(ser);
std::string idStorePath = MetaStorageTk::getMetaDirEntryIDPath(dirEntryPath) + getEntryID();
bool storeRes = storeUpdatedDirEntryBuf(idStorePath, buf, ser.size());
if (!storeRes)
return FhgfsOpsErr_SAVEERROR;
if (getIsBuddyMirrored())
if (auto* resync = BuddyResyncer::getSyncChangeset())
resync->addModification(idStorePath, MetaSyncFileType::Inode);
return FhgfsOpsErr_SUCCESS;
}
FhgfsOpsErr DirEntry::removeDirEntryFile(const std::string& filePath)
{
int unlinkRes = unlink(filePath.c_str() );
if (unlinkRes == 0)
return FhgfsOpsErr_SUCCESS;
if (errno == ENOENT)
return FhgfsOpsErr_PATHNOTEXISTS;
LOG(GENERAL, ERR, "Unable to delete dentry file", filePath, sysErr);
return FhgfsOpsErr_INTERNAL;
}
/**
* Remove the given filePath. This method is used for dirEntries-by-entryID and dirEntries-by-name.
*/
FhgfsOpsErr DirEntry::removeDirEntryName(const char* logContext, const std::string& filePath,
bool isBuddyMirrored)
{
FhgfsOpsErr retVal = removeDirEntryFile(filePath);
if (isBuddyMirrored)
if (auto* resync = BuddyResyncer::getSyncChangeset())
resync->addDeletion(filePath, MetaSyncFileType::Dentry);
return retVal;
}
/**
* Remove the dirEntrID file.
*/
FhgfsOpsErr DirEntry::removeDirEntryID(const std::string& dirEntryPath,
const std::string& entryID, bool isBuddyMirrored)
{
std::string idPath = MetaStorageTk::getMetaDirEntryIDPath(dirEntryPath) + entryID;
FhgfsOpsErr idUnlinkRes = removeDirEntryFile(idPath);
if (likely(idUnlinkRes == FhgfsOpsErr_SUCCESS))
LOG_DBG(GENERAL, DEBUG, "Dir-Entry ID metadata deleted", idPath);
if (isBuddyMirrored)
if (auto* resync = BuddyResyncer::getSyncChangeset())
resync->addDeletion(idPath, MetaSyncFileType::Inode);
return idUnlinkRes;
}
/**
* Remove a dir-entry with an inlined inode. We cannot remove the inode, though and so
* will rename the dir-entry into the inode-hash directories to create a non-inlined
* inode (with dentry format, though).
*
* @param unlinkFileName Unlink only the ID file or also the entryName. If false entryName might
* be empty.
*/
FhgfsOpsErr DirEntry::removeBusyFile(const std::string& dirEntryBasePath,
const std::string& entryID, const std::string& entryName, unsigned unlinkTypeFlags)
{
const char* logContext = "Unlinking dirEnty with busy inlined inode";
App* app = Program::getApp();
std::string dentryPath = dirEntryBasePath + '/' + entryName;
std::string idPath = MetaStorageTk::getMetaDirEntryIDPath(dirEntryBasePath ) + entryID;
std::string inodePath = MetaStorageTk::getMetaInodePath(
getIsBuddyMirrored()
? app->getBuddyMirrorInodesPath()->str()
: app->getInodesPath()->str(),
entryID);
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
if (unlinkTypeFlags & DirEntry_UNLINK_FILENAME)
{
// Delete the dentry-by-name
int unlinkNameRes = unlink(dentryPath.c_str() );
if (unlinkNameRes)
{
if (errno != ENOENT)
{
LogContext(logContext).logErr("Failed to unlink the dirEntry file: " + dentryPath +
" SysErr: " + System::getErrString() );
retVal = FhgfsOpsErr_INTERNAL;
}
else
retVal = FhgfsOpsErr_PATHNOTEXISTS;
goto out;
}
if (getIsBuddyMirrored())
if (auto* resync = BuddyResyncer::getSyncChangeset())
resync->addDeletion(dentryPath, MetaSyncFileType::Dentry);
retVal = FhgfsOpsErr_SUCCESS;
}
if (unlinkTypeFlags & DirEntry_UNLINK_ID)
{
// Rename the ID to the inode directory
int renameRes = rename(idPath.c_str(), inodePath.c_str() );
if (!renameRes)
{
/* Posix rename() has a very weird feature - it does nothing if fromPath and toPath
* are a hard-link pointing to each other. And it even does not give an error message.
* Seems they wanted to have rename(samePath, samePath).
* Once we do support hard links, we are going to hard-link dentryPath and inodePath
* to each other. An unlink() then should be done using the seperate dentry/inode path,
* but if there should be race between link() and unlink(), we might leave a dentry in
* place, which was supposed to be unlinked. So without an error message from rename()
* if it going to do nothing, we always need to try to unlink the file now. We can only
* hope the kernel still has a negative dentry in place, which will immediately tell
* that the file already does not exist anymore. */
unlink(idPath.c_str() );
// Now link to the disposal dir
DirInode* disposalDir = getIsBuddyMirrored()
? app->getBuddyMirrorDisposalDir()
: app->getDisposalDir();
disposalDir->linkFileInodeToDir(inodePath, entryID); // entryID will be the new fileName
// Note: we ignore a possible error here, as don't know what to do with it.
if (getIsBuddyMirrored())
if (auto* resync = BuddyResyncer::getSyncChangeset())
{
resync->addDeletion(idPath, MetaSyncFileType::Inode);
resync->addModification(inodePath, MetaSyncFileType::Inode);
}
retVal = FhgfsOpsErr_SUCCESS;
}
else
{
int errCode = errno;
LogContext(logContext).logErr("Failed to move dirEntry:"
" From: " + idPath +
" To: " + inodePath +
" SysErr: " + System::getErrString() );
if (unlinkTypeFlags & DirEntry_UNLINK_FILENAME)
{
// take the error code (SUCCESS) from file name unlink
}
else
{
if (errCode == ENOENT)
retVal = FhgfsOpsErr_PATHNOTEXISTS;
else
retVal = FhgfsOpsErr_INTERNAL;
}
}
}
out:
return retVal;
}
/**
* Note: Wrapper/chooser for loadFromFileXAttr/Contents.
* To be used for all Dentry (dir-entry) operations.
*/
bool DirEntry::loadFromFileName(const std::string& dirEntryPath, const std::string& entryName)
{
std::string entryNamePath = dirEntryPath + "/" + entryName;
return loadFromFile(entryNamePath);
}
/**
* To be used for all operations regarding the inlined inode.
*/
bool DirEntry::loadFromID(const std::string& dirEntryPath, const std::string& entryID)
{
std::string idStorePath = MetaStorageTk::getMetaDirEntryIDPath(dirEntryPath) + entryID;
return loadFromFile(idStorePath);
}
/**
* Note: Wrapper/chooser for loadFromFileXAttr/Contents.
* Note: Do not call directly, but use loadFromFileName() or loadFromID()
* Retrieve the dir-entry either from xattrs or from real file data - configuration option
*/
bool DirEntry::loadFromFile(const std::string& path)
{
bool useXAttrs = Program::getApp()->getConfig()->getStoreUseExtendedAttribs();
if(useXAttrs)
return loadFromFileXAttr(path);
return loadFromFileContents(path);
}
/**
* Note: Don't call this directly, use the wrapper loadFromFileName().
*/
bool DirEntry::loadFromFileXAttr(const std::string& path)
{
const char* logContext = DIRENTRY_LOG_CONTEXT "(load from xattr file)";
Config* cfg = Program::getApp()->getConfig();
bool retVal = false;
char buf[DIRENTRY_SERBUF_SIZE];
ssize_t getRes = getxattr(path.c_str(), META_XATTR_NAME, buf, DIRENTRY_SERBUF_SIZE);
if(getRes > 0)
{ // we got something => deserialize it
Deserializer des(buf, getRes);
deserializeDentry(des);
if(unlikely(!des.good()))
{ // deserialization failed
LogContext(logContext).logErr("Unable to deserialize dir-entry file: " + path);
goto error_exit;
}
retVal = true;
}
else
if( (getRes == -1) && (errno == ENOENT) )
{ // file not exists
LOG_DEBUG_CONTEXT(LogContext(logContext), Log_DEBUG, "dir-entry file not exists: " +
path + ". " + "SysErr: " + System::getErrString() );
}
else
if( ( (getRes == 0) || ( (getRes == -1) && (errno == ENODATA) ) ) &&
(cfg->getStoreSelfHealEmptyFiles() ) )
{ // empty link file probably due to server crash => self-heal through removal
if (likely(this->name != META_DIRENTRYID_SUB_STR) )
{
LogContext(logContext).logErr("Found an empty dir-entry file. "
"(Self-healing through file removal): " + path);
int unlinkRes = unlink(path.c_str() );
if(unlinkRes == -1)
{
LogContext(logContext).logErr("File removal for self-healing failed: " + path + ". "
"SysErr: " + System::getErrString() );
}
}
}
else
{ // unhandled error
LogContext(logContext).logErr("Unable to open/read dir-entry file: " + path + ". " +
"SysErr: " + System::getErrString() );
}
error_exit:
return retVal;
}
/**
* Note: Don't call this directly, use the wrapper loadFromFile().
*/
bool DirEntry::loadFromFileContents(const std::string& path)
{
const char* logContext = DIRENTRY_LOG_CONTEXT "(load from file)";
Config* cfg = Program::getApp()->getConfig();
bool retVal = false;
char buf[DIRENTRY_SERBUF_SIZE];
int readRes;
int openFlags = O_NOATIME | O_RDONLY;
int fd = open(path.c_str(), openFlags);
if(fd == -1)
{ // open failed
if(likely(errno == ENOENT) )
{ // file not exists
LOG_DEBUG_CONTEXT(LogContext(logContext), Log_DEBUG, "Unable to open dentry file: " +
path + ". " + "SysErr: " + System::getErrString() );
}
else
{
LogContext(logContext).logErr("Unable to open link file: " + path + ". " +
"SysErr: " + System::getErrString() );
}
goto error_donothing;
}
readRes = read(fd, buf, DIRENTRY_SERBUF_SIZE);
if(likely(readRes > 0) )
{ // we got something => deserialize it
Deserializer des(buf, readRes);
deserializeDentry(des);
if(unlikely(!des.good()))
{ // deserialization failed
LogContext(logContext).logErr("Unable to deserialize dentry file: " + path);
goto error_close;
}
retVal = true;
}
else
if( (readRes == 0) && cfg->getStoreSelfHealEmptyFiles() )
{ // empty link file probably due to server crash => self-heal through removal
LogContext(logContext).logErr("Found an empty link file. "
"(Self-healing through file removal): " + path);
int unlinkRes = unlink(path.c_str() );
if(unlinkRes == -1)
{
LogContext(logContext).logErr("File removal for self-healing failed: " + path + ". "
"SysErr: " + System::getErrString() );
}
}
else
{ // read error
LogContext(logContext).logErr("Unable to read denty file: " + path + ". " +
"SysErr: " + System::getErrString() );
}
error_close:
close(fd);
error_donothing:
return retVal;
}
DirEntryType DirEntry::loadEntryTypeFromFile(const std::string& path, const std::string& entryName)
{
bool useXAttrs = Program::getApp()->getConfig()->getStoreUseExtendedAttribs();
if(useXAttrs)
return loadEntryTypeFromFileXAttr(path, entryName);
return loadEntryTypeFromFileContents(path, entryName);
}
/**
* Note: We don't do any serious error checking here, we just want to find out the type or will
* return DirEntryType_INVALID otherwise (eg if file not found)
*/
DirEntryType DirEntry::loadEntryTypeFromFileXAttr(const std::string& path,
const std::string& entryName)
{
DirEntryType retVal = DirEntryType_INVALID;
std::string storePath(path + "/" + entryName);
char buf[DIRENTRY_SERBUF_SIZE];
int getRes = getxattr(storePath.c_str(), META_XATTR_NAME, buf, DIRENTRY_SERBUF_SIZE);
if(getRes <= 0)
{ // getting failed
goto out;
}
retVal = (DirEntryType)buf[DIRENTRY_TYPE_BUF_POS];
out:
return retVal;
}
/**
* Note: We don't do any serious error checking here, we just want to find out the type or will
* return DirEntryType_INVALID otherwise (eg if file not found)
*/
DirEntryType DirEntry::loadEntryTypeFromFileContents(const std::string& path,
const std::string& entryName)
{
DirEntryType retVal = DirEntryType_INVALID;
std::string storePath(path + "/" + entryName);
int openFlags = O_NOATIME | O_RDONLY;
int fd = open(storePath.c_str(), openFlags);
if(fd == -1)
{ // open failed
return DirEntryType_INVALID;
}
char buf;
int readRes = pread(fd, &buf, 1, DIRENTRY_TYPE_BUF_POS);
if(likely(readRes > 0) )
{ // we got something
retVal = (DirEntryType)buf;
}
close(fd);
return retVal;
}
DirEntry* DirEntry::createFromFile(const std::string& path, const std::string& entryName)
{
std::string filepath = path + "/" + entryName;
DirEntry* newDir = new DirEntry(entryName);
bool loadRes = newDir->loadFromFileName(path, entryName);
if(!loadRes)
{
delete(newDir);
return NULL;
}
return newDir;
}
/**
* Return the inlined inode from a dir-entry.
*/
FileInode* DirEntry::createInodeByID(const std::string& dirEntryPath, EntryInfo* entryInfo)
{
bool loadRes = loadFromID(dirEntryPath, entryInfo->getEntryID() );
if (!loadRes)
return NULL;
if (!this->getIsInodeInlined() )
return NULL;
unsigned dentryFeatureFlags = getDentryFeatureFlags();
unsigned inodeFeatureFlags = this->inodeData.getInodeFeatureFlags();
if (inodeFeatureFlags & FILEINODE_FEATURE_HAS_ORIG_PARENTID)
{
// i.e. file was renamed between directories, disk data already have origParentEntryID
}
else
this->inodeData.setDynamicOrigParentEntryID(entryInfo->getParentEntryID() );
/* NOTE: origParentUID also might not be on disk, but the deserializer then set it from
* statData->uid */
FileInode* inode = new (std::nothrow) FileInode(this->getID(), &this->inodeData,
this->getEntryType(), dentryFeatureFlags);
if (unlikely(!inode) )
{
LogContext(__func__).logErr("Out of memory, failed to allocate inode.");
return NULL; // out of memory
}
return inode;
}
/**
* Note: Must be called before any of the disk modifying methods
* (otherwise they will fail)
*
* @param path does not include the filename
*/
FhgfsOpsErr DirEntry::storeInitialDirEntry(const std::string& dirEntryPath)
{
const char* logContext = DIRENTRY_LOG_CONTEXT "(store initial dirEntry)";
LOG_DEBUG(logContext, 4, "Storing initial dentry metadata for ID: '" + getEntryID() + "'");
std::string idPath = MetaStorageTk::getMetaDirEntryIDPath(dirEntryPath) + getEntryID();
// first create the dirEntry-by-ID
FhgfsOpsErr entryIdRes = this->storeInitialDirEntryID(logContext, idPath);
if (entryIdRes != FhgfsOpsErr_SUCCESS)
return entryIdRes;
bool nonInlined = DirEntryType_ISDIR(getEntryType()) || !this->getIsInodeInlined();
// eventually the dirEntry-by-name
std::string namePath = dirEntryPath + '/' + this->name;
FhgfsOpsErr result = this->storeInitialDirEntryName(logContext, idPath, namePath, nonInlined);
if (result == FhgfsOpsErr_SUCCESS && getIsBuddyMirrored())
if (auto* resync = BuddyResyncer::getSyncChangeset())
{
if (!nonInlined)
resync->addModification(idPath, MetaSyncFileType::Inode);
resync->addModification(namePath, MetaSyncFileType::Dentry);
}
return result;
}