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

333 lines
10 KiB
C++

#pragma once
#include <common/storage/Metadata.h>
#include <common/storage/StorageDefinitions.h>
#include <common/storage/StorageErrors.h>
#include <common/Common.h>
#include <toolkit/StorageTkEx.h>
#include "DentryStoreData.h"
#include "DiskMetaData.h"
#include "MetadataEx.h"
#include "FileInodeStoreData.h"
#define DIRENTRY_LOG_CONTEXT "DirEntry "
#define DirEntry_UNLINK_ID 1
#define DirEntry_UNLINK_FILENAME 2
#define DirEntry_UNLINK_ID_AND_FILENAME (DirEntry_UNLINK_ID | DirEntry_UNLINK_FILENAME)
/*
* Class for directory entries (aka "dentries", formerly also referred to as "links"), which
* contains the filename and information about where to find the inode (e.g. for remote dir
* inodes).
*
* Note on locking: In contrast to files/dirs, dentries are not referenced. Every caller/thread
* gets its own copy to work with, so dentry instances are not even shared. That's why we don't
* have a mutex here.
*/
class DirEntry
{
friend class MetaStore;
friend class DirEntryStore;
friend class DirInode;
friend class FileInode;
friend class GenericDebugMsgEx;
friend class RecreateDentriesMsgEx;
public:
DirEntry(DirEntryType entryType, const std::string& name, const std::string& entryID,
NumNodeID ownerNodeID) : dentryDiskData(entryID, entryType, ownerNodeID, 0), name(name)
{
}
/**
* Note: This constructor does not perform initialization, so use it for
* metadata loading only.
*/
DirEntry(const std::string& entryName) : name(entryName)
{
// this->name = entryName; // set in initializer list
}
static DirEntry* createFromFile(const std::string& path, const std::string& entryName);
static DirEntryType loadEntryTypeFromFile(const std::string& path,
const std::string& entryName);
protected:
bool loadFromID(const std::string& dirEntryPath, const std::string& entryID);
private:
DentryStoreData dentryDiskData; // data stored on disk
FileInodeStoreData inodeData;
std::string name; // the user-friendly name, note: not set on reading entries anymore
FhgfsOpsErr storeInitialDirEntryID(const char* logContext, const std::string& idPath);
static FhgfsOpsErr storeInitialDirEntryName(const char* logContext, const std::string& idPath,
const std::string& namePath, bool isNonInlinedInode);
bool storeUpdatedDirEntryBuf(const std::string& idStorePath, char* buf, unsigned bufLen);
bool storeUpdatedDirEntryBufAsXAttr(const std::string& idStorePath, char* buf,
unsigned bufLen);
bool storeUpdatedDirEntryBufAsContents(const std::string& idStorePath, char* buf,
unsigned bufLen);
bool storeUpdatedDirEntry(const std::string& dirEntryPath);
FhgfsOpsErr storeUpdatedInode(const std::string& dirEntryPath);
static FhgfsOpsErr removeDirEntryName(const char* logContext, const std::string& filePath,
bool isBuddyMirrored);
FhgfsOpsErr removeBusyFile(const std::string& dirEntryBasePath, const std::string& entryID,
const std::string& entryName, unsigned unlinkTypeFlags);
FileInode* createInodeByID(const std::string& dirEntryPath, EntryInfo* entryInfo);
bool loadFromFileName(const std::string& dirEntryPath, const std::string& entryName);
bool loadFromFile(const std::string& path);
bool loadFromFileXAttr(const std::string& path);
bool loadFromFileContents(const std::string& path);
static DirEntryType loadEntryTypeFromFileXAttr(const std::string& path,
const std::string& entryName);
static DirEntryType loadEntryTypeFromFileContents(const std::string& path,
const std::string& entryName);
FhgfsOpsErr storeInitialDirEntry(const std::string& dirEntryPath);
static FhgfsOpsErr removeDirEntryFile(const std::string& filePath);
static FhgfsOpsErr removeDirEntryID(const std::string& dirEntryPath,
const std::string& entryID, bool isBuddyMirrored);
public:
// inliners
/**
* Remove file dentries.
*
* If the argument is a directory the caller already must have checked if the directory
* is empty.
*/
static FhgfsOpsErr removeFileDentry(const std::string& dirEntryPath,
const std::string& entryID, const std::string& entryName, unsigned unlinkTypeFlags,
bool isBuddyMirrored)
{
const char* logContext = DIRENTRY_LOG_CONTEXT "(remove stored file dentry)";
FhgfsOpsErr retVal;
// first we delete entry-by-name and use this retVal as return code
if (unlinkTypeFlags & DirEntry_UNLINK_FILENAME)
{
std::string namePath = dirEntryPath + '/' + entryName;
retVal = removeDirEntryName(logContext, namePath, isBuddyMirrored);
if (retVal == FhgfsOpsErr_SUCCESS && (unlinkTypeFlags & DirEntry_UNLINK_ID) )
{
// once the dirEntry-by-name was successfully unlinked, unlink dirEntry-by-ID
removeDirEntryID(dirEntryPath, entryID, isBuddyMirrored); // error code is ignored
}
else
{
/* We must not try to delete the ID file on FhgfsOpsErr_NOTEXISTS, as during a race
* (possible locking issue) the file may have been renamed and so the ID might be
* still valid.
*/
}
}
else
if (unlinkTypeFlags & DirEntry_UNLINK_ID)
retVal = removeDirEntryID(dirEntryPath, entryID, isBuddyMirrored);
else
{
/* It might happen that the code was supposed to unlink an ID only, but if the inode
* has a link count > 1, even the ID is not supposed to be unlinked. So unlink
* is a no-op then. */
retVal = FhgfsOpsErr_SUCCESS;
}
return retVal;
}
/**
* Remove directory dentries.
*
* If the argument is a directory the caller already must have checked if the directory
* is empty.
*/
static FhgfsOpsErr removeDirDentry(const std::string& dirEntryPath,
const std::string& entryName, bool isBuddyMirrored)
{
const char* logContext = DIRENTRY_LOG_CONTEXT "(remove stored directory dentry)";
std::string namePath = dirEntryPath + '/' + entryName;
FhgfsOpsErr retVal = removeDirEntryName(logContext, namePath, isBuddyMirrored);
return retVal;
}
// getters & setters
/**
* Set a new ownerNodeID, used by fsck or generic debug message.
*/
bool setOwnerNodeID(const std::string& dirEntryPath, NumNodeID newOwner)
{
bool success = true;
// only dentries without inlined inodes have an ownerNode field, trying to set the owner
// node on a dentry with an inlined inode is thus impossible.
if (getIsInodeInlined())
return false;
NumNodeID oldOwner = this->getOwnerNodeID();
this->dentryDiskData.setOwnerNodeID(newOwner);
if (!storeUpdatedDirEntry(dirEntryPath))
{ // failed to update metadata => restore old value
this->dentryDiskData.setOwnerNodeID(oldOwner);
success = false;
}
return success;
}
void setFileInodeData(FileInodeStoreData& inodeData)
{
this->inodeData = inodeData;
unsigned updatedFlags = getDentryFeatureFlags() |
(DENTRY_FEATURE_INODE_INLINE | DENTRY_FEATURE_IS_FILEINODE);
setDentryFeatureFlags(updatedFlags);
}
void setBuddyMirrorFeatureFlag()
{
addDentryFeatureFlag(DENTRY_FEATURE_BUDDYMIRRORED);
}
bool getIsBuddyMirrored()
{
return (getDentryFeatureFlags() & DENTRY_FEATURE_BUDDYMIRRORED);
}
unsigned getDentryFeatureFlags()
{
return this->dentryDiskData.getDentryFeatureFlags();
}
void setDentryFeatureFlags(unsigned featureFlags)
{
this->dentryDiskData.setDentryFeatureFlags(featureFlags);
}
void addDentryFeatureFlag(unsigned featureFlag)
{
this->dentryDiskData.addDentryFeatureFlag(featureFlag);
}
void removeDentryFeatureFlag(unsigned featureFlag)
{
this->dentryDiskData.removeDentryFeatureFlag(featureFlag);
}
// getters
const std::string& getEntryID()
{
return this->dentryDiskData.getEntryID();
}
const std::string& getID()
{
return this->dentryDiskData.getEntryID();
}
/**
* Note: Should not be changed after object init => not synchronized!
*/
DirEntryType getEntryType()
{
return this->dentryDiskData.getDirEntryType();
}
const std::string& getName()
{
return this->name;
}
NumNodeID getOwnerNodeID()
{
return this->dentryDiskData.getOwnerNodeID();
}
void getEntryInfo(const std::string& parentEntryID, int flags, EntryInfo* outEntryInfo)
{
if (getIsInodeInlined() )
flags |= ENTRYINFO_FEATURE_INLINED;
if (getIsBuddyMirrored())
flags |= ENTRYINFO_FEATURE_BUDDYMIRRORED;
outEntryInfo->set(getOwnerNodeID(), parentEntryID, getID(), name,
getEntryType(), flags);
}
/**
* Unset the DENTRY_FEATURE_INODE_INLINE flag
*/
void unsetInodeInlined()
{
uint16_t dentryFeatureFlags = this->dentryDiskData.getDentryFeatureFlags();
dentryFeatureFlags &= ~(DENTRY_FEATURE_INODE_INLINE);
this->dentryDiskData.setDentryFeatureFlags(dentryFeatureFlags);
}
/**
* Check if the inode is inlined and no flag is set to indicate the same object
* (file-as-hard-link) is also in the inode-hash directories.
*/
bool getIsInodeInlined()
{
if (this->dentryDiskData.getDentryFeatureFlags() & DENTRY_FEATURE_INODE_INLINE)
return true;
return false;
}
void serializeDentry(Serializer& ser)
{
DiskMetaData diskMetaData(&this->dentryDiskData, &this->inodeData);
diskMetaData.serializeDentry(ser);
}
void deserializeDentry(Deserializer& des)
{
DiskMetaData diskMetaData(&this->dentryDiskData, &this->inodeData);
diskMetaData.deserializeDentry(des);
}
protected:
FileInodeStoreData* getInodeStoreData(void)
{
return &this->inodeData;
}
};