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

654 lines
19 KiB
C++

#pragma once
#include <common/storage/striping/StripePattern.h>
#include <common/storage/RemoteStorageTarget.h>
#include <common/storage/Metadata.h>
#include <common/storage/StorageDefinitions.h>
#include <common/storage/StorageErrors.h>
#include <common/threading/UniqueRWLock.h>
#include <common/storage/StatData.h>
#include <common/Common.h>
#include "DirEntryStore.h"
#include "MetadataEx.h"
#include "InodeFileStore.h"
/* Note: Don't forget to update DiskMetaData::getSupportedDirInodeFeatureFlags() if you add new
* flags here. */
#define DIRINODE_FEATURE_EARLY_SUBDIRS 2 // indicate proper alignment for statData
#define DIRINODE_FEATURE_MIRRORED 4 // indicate old mirrored directory (for compatibility)
#define DIRINODE_FEATURE_STATFLAGS 8 // StatData have a flags field
#define DIRINODE_FEATURE_BUDDYMIRRORED 16 // indicate buddy mirrored directory
#define DIRINODE_FEATURE_HAS_RST 32 // indicates remote target availability
// limit number of stripes per file to a high but safe number. too many stripe targets will cause
// the serialized stripe pattern to be too large to store reliably, so choose a value well below
// that limit (but still high enough to not bother anyone).
// the number of stripe targets is limited by:
// * stripe patterns in inodes: for each target, 32 bits of target ID are stored
// * chunk size infos: for each target, 64 bits of #used_blocks may be stored
// at 256 targets, those two structures may use up to 3072 bytes, leaving ample room for other
// inode data.
#define DIRINODE_MAX_STRIPE_TARGETS 256
/**
* Our inode object, but for directories only. Files are in class FileInode.
*/
class DirInode
{
friend class MetaStore;
friend class InodeDirStore;
friend class DiskMetaData;
public:
DirInode(const std::string& id, int mode, unsigned userID, unsigned groupID,
NumNodeID ownerNodeID, const StripePattern& stripePattern, bool isBuddyMirrored);
/**
* Constructur used to load inodes from disk
* Note: Not all values are set, as we load those from disk.
*/
DirInode(const std::string& id, bool isBuddyMirrored)
: id(id),
stripePattern(NULL),
featureFlags(isBuddyMirrored ? DIRINODE_FEATURE_BUDDYMIRRORED : 0),
exclusive(false),
entries(id, isBuddyMirrored),
isLoaded(false)
{ }
~DirInode()
{
LOG_DEBUG("Delete DirInode", Log_SPAM, std::string("Deleting inode: ") + this->id);
SAFE_DELETE_NOSET(stripePattern);
}
static DirInode* createFromFile(const std::string& id, bool isBuddyMirrored);
StripePattern* createFileStripePattern(const UInt16List* preferredTargets,
unsigned numtargets, unsigned chunksize, StoragePoolId storagePoolId);
FhgfsOpsErr listIncremental(int64_t serverOffset,
unsigned maxOutNames, StringList* outNames, int64_t* outNewServerOffset);
FhgfsOpsErr listIncrementalEx(int64_t serverOffset,
unsigned maxOutNames, bool filterDots, ListIncExOutArgs& outArgs);
FhgfsOpsErr listIDFilesIncremental(int64_t serverOffset, uint64_t incrementalOffset,
unsigned maxOutNames, ListIncExOutArgs& outArgs);
bool exists(const std::string& entryName);
FhgfsOpsErr makeDirEntry(DirEntry& entry);
FhgfsOpsErr linkFileInodeToDir(const std::string& inodePath, const std::string& fileName);
FhgfsOpsErr removeDir(const std::string& entryName, DirEntry** outDirEntry);
FhgfsOpsErr unlinkDirEntry(const std::string& entryName, DirEntry* entry,
unsigned unlinkTypeFlags);
bool loadIfNotLoaded(void);
void invalidate();
FhgfsOpsErr refreshMetaInfo();
// non-inlined getters & setters
FhgfsOpsErr setOwnerNodeID(const std::string& entryName, NumNodeID ownerNode);
StripePattern* getStripePatternClone();
FhgfsOpsErr setStripePattern(const StripePattern& newPattern, uint32_t actorUID = 0);
FhgfsOpsErr setRemoteStorageTarget(const RemoteStorageTarget& rst);
FhgfsOpsErr clearRemoteStorageTarget();
FhgfsOpsErr getStatData(StatData& outStatData,
NumNodeID* outParentNodeID = NULL, std::string* outParentEntryID = NULL);
void setStatData(StatData& statData);
bool setAttrData(int validAttribs, SettableFileAttribs* attribs);
FhgfsOpsErr setDirParentAndChangeTime(EntryInfo* entryInfo, NumNodeID parentNodeID);
std::pair<FhgfsOpsErr, StringVector> listXAttr(const EntryInfo* file);
std::tuple<FhgfsOpsErr, std::vector<char>, ssize_t> getXAttr(const EntryInfo* file,
const std::string& xAttrName, size_t maxSize);
FhgfsOpsErr removeXAttr(EntryInfo* file, const std::string& xAttrName);
FhgfsOpsErr setXAttr(EntryInfo* file, const std::string& xAttrName,
const CharVector& xAttrValue, int flags, bool updateTimestamps = true);
private:
std::string id; // filesystem-wide unique string
NumNodeID ownerNodeID; // 0 means undefined
StripePattern* stripePattern; // is the default for new files and subdirs
RemoteStorageTarget rstInfo; // remote storage target information
std::string parentDirID; // must be reliable for NFS
NumNodeID parentNodeID; // must be reliable for NFS
uint16_t featureFlags;
bool exclusive; // if set, we do not allow other references
// StatData
StatData statData;
uint32_t numSubdirs; // indirectly updated by subdir creation/removal
uint32_t numFiles; // indirectly updated by subfile creation/removal
RWLock rwlock;
DirEntryStore entries;
/* if not set we have an object that has not read data from disk yet, the dir might not
even exist on disk */
bool isLoaded;
Mutex loadLock; // protects the disk load
InodeFileStore fileStore; /* We must not delete the DirInode as long as this
* InodeFileStore still has entries. Therefore a dir reference
* has to be taken for entry in this InodeFileStore */
StripePattern* createFileStripePatternUnlocked(const UInt16List* preferredTargets,
unsigned numtargets, unsigned chunksize, StoragePoolId storagePoolId);
FhgfsOpsErr storeInitialMetaData();
FhgfsOpsErr storeInitialMetaData(const CharVector& defaultACLXAttr,
const CharVector& accessACLXAttr);
FhgfsOpsErr storeInitialMetaDataInode();
bool storeUpdatedMetaDataBuf(char* buf, unsigned bufLen);
bool storeUpdatedMetaDataBufAsXAttr(char* buf, unsigned bufLen);
bool storeUpdatedMetaDataBufAsContents(char* buf, unsigned bufLen);
bool storeUpdatedMetaDataBufAsContentsInPlace(char* buf, unsigned bufLen);
bool storeUpdatedMetaDataUnlocked();
bool storeRemoteStorageTargetInfoUnlocked();
bool storeRemoteStorageTargetDataBufAsXAttr(char* buf, unsigned bufLen);
FhgfsOpsErr renameDirEntry(const std::string& fromName, const std::string& toName,
DirEntry* overWriteEntry);
FhgfsOpsErr renameDirEntryUnlocked(const std::string& fromName, const std::string& toName,
DirEntry* overWriteEntry);
static bool removeStoredMetaData(const std::string& id);
static bool removeStoredMetaDataDir(const std::string& id);
static bool removeStoredMetaDataFile(const std::string& id, bool isBuddyMirrored);
FhgfsOpsErr refreshMetaInfoUnlocked();
FhgfsOpsErr makeDirEntryUnlocked(DirEntry* entry);
FhgfsOpsErr linkFileInodeToDirUnlocked(const std::string& inodePath,
const std::string &fileName);
FhgfsOpsErr removeDirUnlocked(const std::string& entryName, DirEntry** outDirEntry);
FhgfsOpsErr unlinkDirEntryUnlocked(const std::string& entryName, DirEntry* inEntry,
unsigned unlinkTypeFlags);
FhgfsOpsErr refreshSubentryCountUnlocked();
bool loadFromFile();
bool loadFromFileXAttr();
bool loadFromFileContents();
bool loadIfNotLoadedUnlocked();
bool loadRstFromFileXAttr();
FhgfsOpsErr getEntryData(const std::string& entryName, EntryInfo* outInfo,
FileInodeStoreData* outInodeMetaData);
bool setAttrDataUnlocked(int validAttribs, SettableFileAttribs* attribs);
FhgfsOpsErr setDirParentAndChangeTimeUnlocked(EntryInfo* entryInfo, NumNodeID parentNodeID);
bool unlinkBusyFileUnlocked(const std::string& fileName, DirEntry* dentry,
unsigned unlinkTypeFlags);
protected:
FhgfsOpsErr linkFilesInDirUnlocked(const std::string& fromName, FileInode& fromInode,
const std::string& toName);
FhgfsOpsErr setIsBuddyMirrored(const bool isBuddyMirrored);
public:
// inliners
/**
* Note: Must be called before any of the mutator methods (otherwise they will fail)
*/
FhgfsOpsErr storePersistentMetaData()
{
return storeInitialMetaData();
}
FhgfsOpsErr storePersistentMetaData(const CharVector& defaultACLXAttr,
const CharVector& accessACLXAttr)
{
return storeInitialMetaData(defaultACLXAttr, accessACLXAttr);
}
/**
* Unlink the dir-inode file on disk.
* Note: Assumes that the caller already verified that the directory is empty
*/
static bool unlinkStoredInode(const std::string& id, bool isBuddyMirrored)
{
bool dirRes = DirEntryStore::rmDirEntryStoreDir(id, isBuddyMirrored);
if(!dirRes)
return dirRes;
return removeStoredMetaDataFile(id, isBuddyMirrored);
}
/**
* Note: Intended to be used by fsck only.
*/
FhgfsOpsErr storeAsReplacementFile(const std::string& id)
{
// note: creates new dir metadata file for non-existing or invalid one => no locking needed
removeStoredMetaDataFile(id, this->getIsBuddyMirrored());
return storeInitialMetaDataInode();
}
/**
* Return create a DirEntry for the given file name
*
* note: this->rwlock already needs to be locked
*/
DirEntry* dirEntryCreateFromFileUnlocked(const std::string& entryName)
{
return this->entries.dirEntryCreateFromFile(entryName);
}
/**
* Get a dentry
* note: the difference to getDirEntryInfo/getFileEntryInfo is that this works independent
* of the entry-type
*/
bool getDentry(const std::string& entryName, DirEntry& outEntry)
{
bool retVal;
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
retVal = getDentryUnlocked(entryName, outEntry);
safeLock.unlock();
return retVal;
}
/**
* Get a dentry
* note: the difference to getDirEntryInfo/getFileEntryInfo is that this works independent
* of the entry-type
*/
bool getDentryUnlocked(const std::string& entryName, DirEntry& outEntry)
{
return this->entries.getDentry(entryName, outEntry);
}
/**
* Get the dentry (dir-entry) of a directory
*/
bool getDirDentry(const std::string& dirName, DirEntry& outEntry)
{
bool retVal;
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
retVal = entries.getDirDentry(dirName, outEntry);
safeLock.unlock();
return retVal;
}
/*
* Get the dentry (dir-entry) of a file
*/
bool getFileDentry(const std::string& fileName, DirEntry& outEntry)
{
bool retVal;
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
retVal = entries.getFileDentry(fileName, outEntry);
safeLock.unlock();
return retVal;
}
/**
* Get the EntryInfo
* note: the difference to getDirEntryInfo/getFileEntryInfo is that this works independent
* of the entry-type
*/
bool getEntryInfo(const std::string& entryName, EntryInfo& outEntryInfo)
{
bool retVal;
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
retVal = this->entries.getEntryInfo(entryName, outEntryInfo);
safeLock.unlock();
return retVal;
}
/**
* Get the EntryInfo of a directory
*/
bool getDirEntryInfo(const std::string& dirName, EntryInfo& outEntryInfo)
{
bool retVal;
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
retVal = entries.getDirEntryInfo(dirName, outEntryInfo);
safeLock.unlock();
return retVal;
}
/*
* Get the dir-entry of a file
*/
bool getFileEntryInfo(const std::string& fileName, EntryInfo& outEntryInfo)
{
bool retVal;
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
retVal = entries.getFileEntryInfo(fileName, outEntryInfo);
safeLock.unlock();
return retVal;
}
const std::string& getID() const
{
return id;
}
NumNodeID getOwnerNodeID()
{
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
NumNodeID owner = ownerNodeID;
safeLock.unlock();
return owner;
}
bool setOwnerNodeID(NumNodeID newOwner)
{
bool success = true;
SafeRWLock safeLock(&rwlock, SafeRWLock_WRITE);
bool loadSuccess = loadIfNotLoadedUnlocked();
if (!loadSuccess)
{
safeLock.unlock();
return false;
}
NumNodeID oldOwner = this->ownerNodeID;
this->ownerNodeID = newOwner;
if(!storeUpdatedMetaDataUnlocked() )
{ // failed to update metadata => restore old value
this->ownerNodeID = oldOwner;
success = false;
}
safeLock.unlock();
return success;
}
void getParentInfo(std::string* outParentDirID, NumNodeID* outParentNodeID)
{
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
*outParentDirID = this->parentDirID;
*outParentNodeID = this->parentNodeID;
safeLock.unlock();
}
/**
* Note: Initial means for newly created objects (=> unlocked, unpersistent)
*/
void setParentInfoInitial(const std::string& parentDirID, NumNodeID parentNodeID)
{
this->parentDirID = parentDirID;
this->parentNodeID = parentNodeID;
}
void setFeatureFlags(unsigned flags)
{
this->featureFlags = flags;
}
void addFeatureFlag(unsigned flag)
{
this->featureFlags |= flag;
}
void removeFeatureFlag(unsigned flag)
{
this->featureFlags &= ~flag;
}
unsigned getFeatureFlags() const
{
return this->featureFlags;
}
void setIsBuddyMirroredFlag(const bool isBuddyMirrored)
{
UniqueRWLock lock(rwlock, SafeRWLock_WRITE);
if (isBuddyMirrored)
featureFlags |= DIRINODE_FEATURE_BUDDYMIRRORED;
else
featureFlags &= ~DIRINODE_FEATURE_BUDDYMIRRORED;
entries.setParentID(entries.getParentEntryID(), isBuddyMirrored);
}
FhgfsOpsErr setAndStoreIsBuddyMirrored(bool isBuddyMirrored)
{
SafeRWLock safeLock(&rwlock, SafeRWLock_WRITE);
const FhgfsOpsErr result = setIsBuddyMirrored(isBuddyMirrored);
if (result == FhgfsOpsErr_SUCCESS)
storeUpdatedMetaDataUnlocked();
safeLock.unlock();
return result;
}
bool getIsBuddyMirrored() const
{
return (getFeatureFlags() & DIRINODE_FEATURE_BUDDYMIRRORED);
}
bool getIsRstAvailable() const
{
return (getFeatureFlags() & DIRINODE_FEATURE_HAS_RST);
}
bool getExclusive()
{
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
bool retVal = this->exclusive;
safeLock.unlock();
return retVal;
}
void setExclusive(bool exclusive)
{
SafeRWLock safeLock(&rwlock, SafeRWLock_WRITE);
this->exclusive = exclusive;
safeLock.unlock();
}
unsigned getUserID()
{
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
unsigned retVal = this->statData.getUserID();
safeLock.unlock();
return retVal;
}
unsigned getUserIDUnlocked()
{
return this->statData.getUserID();
}
unsigned getGroupID()
{
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
unsigned retVal = this->statData.getGroupID();
safeLock.unlock();
return retVal;
}
int getMode()
{
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
int retVal = this->statData.getMode();
safeLock.unlock();
return retVal;
}
unsigned getNumHardlinks()
{
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
unsigned retVal = this->statData.getNumHardlinks();
safeLock.unlock();
return retVal;
}
size_t getNumSubEntries()
{
SafeRWLock safeLock(&rwlock, SafeRWLock_READ);
size_t retVal = numSubdirs + numFiles;
safeLock.unlock();
return retVal;
}
bool getIsLoaded()
{
UniqueRWLock lock(rwlock, SafeRWLock_READ);
return isLoaded;
}
static FhgfsOpsErr getStatData(const std::string& dirID, bool isBuddyMirrored,
StatData& outStatData, NumNodeID* outParentNodeID, std::string* outParentEntryID)
{
DirInode dir(dirID, isBuddyMirrored);
if(!dir.loadFromFile() )
return FhgfsOpsErr_PATHNOTEXISTS;
return dir.getStatData(outStatData, outParentNodeID, outParentEntryID);
}
StripePattern* getStripePattern() const
{
return stripePattern;
}
RemoteStorageTarget* getRemoteStorageTargetInfo()
{
return &this->rstInfo;
}
private:
bool updateTimeStampsAndStoreToDisk(const char* logContext)
{
int64_t nowSecs = TimeAbs().getTimeval()->tv_sec;;
this->statData.setAttribChangeTimeSecs(nowSecs);
this->statData.setModificationTimeSecs(nowSecs);
if(unlikely(!storeUpdatedMetaDataUnlocked() ) )
{
LogContext(logContext).logErr(std::string("Failed to update dir-info on disk: "
"Dir-ID: ") + this->getID() + std::string(". SysErr: ") + System::getErrString() );
return false;
}
return true;
}
bool increaseNumSubDirsAndStoreOnDisk(void)
{
const char* logContext = "DirInfo update: increase number of SubDirs";
numSubdirs++;
return updateTimeStampsAndStoreToDisk(logContext);
}
bool increaseNumFilesAndStoreOnDisk(void)
{
const char* logContext = "DirInfo update: increase number of Files";
numFiles++;
return updateTimeStampsAndStoreToDisk(logContext);
}
bool decreaseNumFilesAndStoreOnDisk(void)
{
const char* logContext = "DirInfo update: decrease number of Files";
if (numFiles) // make sure it does not get sub-zero
numFiles--;
return updateTimeStampsAndStoreToDisk(logContext);
}
bool decreaseNumSubDirsAndStoreOnDisk(void)
{
const char* logContext = "DirInfo update: decrease number of SubDirs";
if (numSubdirs) // make sure it does not get sub-zero
numSubdirs--;
return updateTimeStampsAndStoreToDisk(logContext);
}
};