320 lines
10 KiB
C
320 lines
10 KiB
C
#include <app/App.h>
|
|
#include <app/log/Logger.h>
|
|
#include <common/threading/Mutex.h>
|
|
#include <common/Common.h>
|
|
#include <common/toolkit/Time.h>
|
|
#include <net/filesystem/FhgfsOpsRemoting.h>
|
|
#include <filesystem/FsDirInfo.h>
|
|
#include <filesystem/FsFileInfo.h>
|
|
#include <toolkit/NoAllocBufferStore.h>
|
|
#include <common/toolkit/MetadataTk.h>
|
|
#include <common/Common.h>
|
|
#include "FhgfsOps_versions.h"
|
|
#include "FhgfsOpsInode.h"
|
|
#include "FhgfsOpsDir.h"
|
|
#include "FhgfsOpsSuper.h"
|
|
#include "FhgfsOpsHelper.h"
|
|
|
|
|
|
#include <linux/namei.h>
|
|
|
|
static int __FhgfsOps_revalidateIntent(struct dentry* parentDentry, struct dentry* dentry);
|
|
|
|
struct dentry_operations fhgfs_dentry_ops =
|
|
{
|
|
.d_revalidate = FhgfsOps_revalidateIntent,
|
|
.d_delete = FhgfsOps_deleteDentry,
|
|
};
|
|
|
|
/**
|
|
* Called when the dcache has a lookup hit (and wants to know whether the cache data
|
|
* is still valid).
|
|
*
|
|
* @return value is quasi-boolean: 0 if entry invalid, 1 if still valid (no other return values
|
|
* allowed).
|
|
*/
|
|
#ifndef KERNEL_HAS_ATOMIC_OPEN
|
|
int FhgfsOps_revalidateIntent(struct dentry* dentry, struct nameidata* nameidata)
|
|
#else
|
|
int FhgfsOps_revalidateIntent(struct dentry* dentry, unsigned flags)
|
|
#endif // LINUX_VERSION_CODE
|
|
{
|
|
App* app;
|
|
Config* cfg;
|
|
Logger* log;
|
|
const char* logContext;
|
|
|
|
int isValid = 0; // quasi-boolean (return value)
|
|
struct dentry* parentDentry;
|
|
struct inode* parentInode;
|
|
struct inode* inode;
|
|
Time now;
|
|
|
|
|
|
unsigned long nowTime;
|
|
unsigned cacheValidityMS;
|
|
|
|
#ifndef KERNEL_HAS_ATOMIC_OPEN
|
|
unsigned flags = nameidata ? nameidata->flags : 0;
|
|
|
|
IGNORE_UNUSED_VARIABLE(flags);
|
|
#endif // LINUX_VERSION_CODE
|
|
|
|
|
|
#ifdef LOOKUP_RCU
|
|
/* note: 2.6.38 introduced rcu-walk mode, which is inappropriate for us, because we need the
|
|
parentDentry and need to sleep for communication. ECHILD below tells vfs to call this again
|
|
in old ref-walk mode. (see Documentation/filesystems/vfs.txt:d_revalidate) */
|
|
|
|
if(flags & LOOKUP_RCU)
|
|
return -ECHILD;
|
|
#endif // LINUX_VERSION_CODE
|
|
|
|
inode = dentry->d_inode;
|
|
|
|
// access to parentDentry and inode needs to live below the rcu check.
|
|
|
|
app = FhgfsOps_getApp(dentry->d_sb);
|
|
cfg = App_getConfig(app);
|
|
log = App_getLogger(app);
|
|
logContext = "FhgfsOps_revalidateIntent";
|
|
|
|
parentDentry = dget_parent(dentry);
|
|
parentInode = parentDentry->d_inode;
|
|
|
|
if(unlikely(Logger_getLogLevel(log) >= 5) )
|
|
FhgfsOpsHelper_logOp(Log_SPAM, app, dentry, inode, logContext);
|
|
|
|
if(!inode || !parentInode || is_bad_inode(inode) )
|
|
{
|
|
if(inode && S_ISDIR(inode->i_mode) )
|
|
{
|
|
if(have_submounts(dentry) )
|
|
goto cleanup_put_parent;
|
|
|
|
shrink_dcache_parent(dentry);
|
|
}
|
|
|
|
// dentry->d_time is updated with the current time during the first lookup,
|
|
// the cache is only valid if the difference of CURRENT_TIME and revalidate
|
|
// time is less than the tuneENOENTCacheValidityMS from the config
|
|
|
|
Time_setToNowReal(&now);
|
|
cacheValidityMS = Config_getTuneENOENTCacheValidityMS(cfg);
|
|
nowTime = (now.tv_sec * 1000000000UL + now.tv_nsec);
|
|
if (!cacheValidityMS || ((nowTime - dentry->d_time)/1000000UL) > cacheValidityMS)
|
|
{
|
|
d_drop(dentry);
|
|
goto cleanup_put_parent;
|
|
}
|
|
else
|
|
{
|
|
isValid = 1;
|
|
goto cleanup_put_parent;
|
|
}
|
|
}
|
|
|
|
// active dentry => remote-stat and local-compare
|
|
isValid = __FhgfsOps_revalidateIntent(parentDentry, dentry );
|
|
|
|
cleanup_put_parent:
|
|
// clean-up
|
|
dput(parentDentry);
|
|
|
|
LOG_DEBUG_FORMATTED(log, 5, logContext, "'%s': isValid: %s",
|
|
dentry->d_name.name, isValid ? "yes" : "no");
|
|
|
|
return isValid;
|
|
}
|
|
|
|
/*
|
|
* sub function of FhgfsOps_revalidateIntent(), supposed to be inlined, as we resolve several
|
|
* pointers two times, in this function and also already in the caller
|
|
*
|
|
* @return value is quasi-boolean: 0 if entry invalid, 1 if still valid (no other return values
|
|
* allowed).
|
|
*/
|
|
int __FhgfsOps_revalidateIntent(struct dentry* parentDentry, struct dentry* dentry)
|
|
{
|
|
const char* logContext = "__FhgfsOps_revalidateIntent";
|
|
App* app = FhgfsOps_getApp(dentry->d_sb);
|
|
Logger* log = App_getLogger(app);
|
|
Config* cfg = App_getConfig(app);
|
|
|
|
const char* entryName = dentry->d_name.name;
|
|
|
|
fhgfs_stat fhgfsStat;
|
|
fhgfs_stat* fhgfsStatPtr;
|
|
|
|
struct inode* parentInode = parentDentry->d_inode;
|
|
FhgfsInode* parentFhgfsInode = BEEGFS_INODE(parentInode);
|
|
|
|
struct inode* inode = dentry->d_inode;
|
|
FhgfsInode* fhgfsInode = BEEGFS_INODE(inode);
|
|
|
|
bool cacheValid = FhgfsInode_isCacheValid(fhgfsInode, inode->i_mode, cfg);
|
|
int isValid = 0; // quasi-boolean (return value)
|
|
bool needDrop = false;
|
|
FhgfsIsizeHints iSizeHints;
|
|
|
|
|
|
FhgfsOpsHelper_logOp(Log_SPAM, app, dentry, inode, logContext);
|
|
|
|
|
|
if (cacheValid)
|
|
{
|
|
isValid = 1;
|
|
return isValid;
|
|
}
|
|
|
|
if(IS_ROOT(dentry) )
|
|
fhgfsStatPtr = NULL;
|
|
else
|
|
{ // any file or directory except our mount root
|
|
const EntryInfo* parentInfo;
|
|
EntryInfo* entryInfo;
|
|
uint32_t metaVersion;
|
|
|
|
|
|
FhgfsOpsErr remotingRes;
|
|
LookupIntentInfoIn inInfo; // input data for combo-request
|
|
LookupIntentInfoOut outInfo; // result data of combo-request
|
|
|
|
FhgfsInode_initIsizeHints(fhgfsInode, &iSizeHints);
|
|
|
|
FhgfsInode_entryInfoReadLock(parentFhgfsInode); // LOCK parentInfo
|
|
FhgfsInode_entryInfoWriteLock(fhgfsInode); // LOCK EntryInfo
|
|
|
|
parentInfo = FhgfsInode_getEntryInfo(parentFhgfsInode);
|
|
entryInfo = &fhgfsInode->entryInfo;
|
|
metaVersion = fhgfsInode->metaVersion;
|
|
|
|
LookupIntentInfoIn_init(&inInfo, parentInfo, entryName);
|
|
LookupIntentInfoIn_addEntryInfo(&inInfo, entryInfo);
|
|
LookupIntentInfoIn_addMetaVersion(&inInfo, metaVersion);
|
|
|
|
LookupIntentInfoOut_prepare(&outInfo, NULL, &fhgfsStat);
|
|
|
|
remotingRes = FhgfsOpsRemoting_lookupIntent(app, &inInfo, &outInfo);
|
|
|
|
// we only need to update entryInfo flags
|
|
if (remotingRes == FhgfsOpsErr_SUCCESS && outInfo.revalidateRes == FhgfsOpsErr_SUCCESS)
|
|
entryInfo->featureFlags = outInfo.revalidateUpdatedFlags;
|
|
|
|
FhgfsInode_entryInfoWriteUnlock(fhgfsInode); // UNLOCK EntryInfo
|
|
FhgfsInode_entryInfoReadUnlock(parentFhgfsInode); // UNLOCK parentInfo
|
|
|
|
if (unlikely(remotingRes != FhgfsOpsErr_SUCCESS) )
|
|
{
|
|
needDrop = true;
|
|
goto out;
|
|
}
|
|
|
|
if (outInfo.revalidateRes == FhgfsOpsErr_PATHNOTEXISTS)
|
|
{
|
|
if(unlikely(!(outInfo.responseFlags & LOOKUPINTENTRESPMSG_FLAG_REVALIDATE) ) )
|
|
Logger_logErrFormatted(log, logContext, "Unexpected revalidate info missing: %s",
|
|
entryInfo->fileName);
|
|
needDrop = true;
|
|
goto out;
|
|
}
|
|
|
|
// check the stat result here and set fhgfsStatPtr accordingly
|
|
if (outInfo.statRes == FhgfsOpsErr_SUCCESS)
|
|
fhgfsStatPtr = &fhgfsStat; // successful, so we can use existing stat values
|
|
else if (outInfo.statRes == FhgfsOpsErr_NOTOWNER)
|
|
fhgfsStatPtr = NULL; // stat values not available
|
|
else if (outInfo.statRes == FhgfsOpsErr_INTERNAL
|
|
&& outInfo.revalidateRes == FhgfsOpsErr_METAVERSIONMISMATCH
|
|
&& Config_getSysCacheInvalidationVersion(cfg) )
|
|
{
|
|
// case when we want to invalidate the inode cache due to inode version change
|
|
// this case will not drop the dentry
|
|
// must goto out, since we clear the inode stripe pattern
|
|
fhgfsStatPtr = NULL; // stat values not available
|
|
__FhgfsOps_clearInodeStripePattern(app, inode);
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
if(unlikely(!(outInfo.responseFlags & LOOKUPINTENTRESPMSG_FLAG_STAT) ) )
|
|
Logger_logErrFormatted(log, logContext, "Unexpected stat info missing: %s",
|
|
entryInfo->fileName);
|
|
|
|
// now its getting difficult as there is an unexpected error
|
|
needDrop = true;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
if (!__FhgfsOps_refreshInode(app, inode, fhgfsStatPtr, &iSizeHints) )
|
|
isValid = 1;
|
|
else
|
|
isValid = 0;
|
|
|
|
out:
|
|
if (needDrop)
|
|
d_drop(dentry);
|
|
|
|
return isValid;
|
|
}
|
|
|
|
/**
|
|
* This is called from dput() when d_count is going to 0 and dput() wants to know from us whether
|
|
* not it should delete the dentry.
|
|
*
|
|
* @return !=0 to delete dentry, 0 to keep it
|
|
*/
|
|
#ifndef KERNEL_HAS_D_DELETE_CONST_ARG
|
|
int FhgfsOps_deleteDentry(struct dentry* dentry)
|
|
#else
|
|
int FhgfsOps_deleteDentry(const struct dentry* dentry)
|
|
#endif // LINUX_VERSION_CODE
|
|
{
|
|
int shouldBeDeleted = 0; // quasi-boolean (return value)
|
|
struct inode* inode = dentry->d_inode;
|
|
|
|
// For both positive and negative dentry cases,
|
|
// dentry cache (dcache) is mainatined
|
|
|
|
if(inode)
|
|
{
|
|
if(is_bad_inode(inode) )
|
|
shouldBeDeleted = 1; // inode marked as bad => no need to keep dentry
|
|
}
|
|
|
|
return shouldBeDeleted;
|
|
}
|
|
|
|
/*
|
|
* Constructs the path from the root dentry (of the mount-point) to an arbitrary hashed dentry.
|
|
*
|
|
* Note: Acquires a buf from the pathBufStore that must be released by the caller.
|
|
* Note: This is safe for paths larger than bufSize, but returns -ENAMETOOLONG in that case
|
|
* NOTE: Do NOT call two times in the same thread, as it might deadlock if multiple threads
|
|
* try to aquire a buffer. The thread that takes the buffer two times might never finish,
|
|
* if not sufficient buffers are available. If multiple threads take multiple buffers in
|
|
* parallel an infinity deadlock of the filesystem will happen.
|
|
*
|
|
* @outBuf the buf that must be returned to the pathBufStore of the app
|
|
* @return some offset within the outStoreBuf or the linux ERR_PTR(errorCode) (already negative) and
|
|
* *outStoreBuf will be NULL then
|
|
*/
|
|
char* __FhgfsOps_pathResolveToStoreBuf(NoAllocBufferStore* bufStore, struct dentry* dentry,
|
|
char** outStoreBuf)
|
|
{
|
|
char * path;
|
|
const ssize_t storeBufLen = NoAllocBufferStore_getBufSize(bufStore);
|
|
*outStoreBuf = NoAllocBufferStore_waitForBuf(bufStore);
|
|
|
|
path = dentry_path_raw(dentry, *outStoreBuf, storeBufLen);
|
|
if (unlikely (IS_ERR(path) ) )
|
|
{
|
|
NoAllocBufferStore_addBuf(bufStore, *outStoreBuf);
|
|
*outStoreBuf = NULL;
|
|
}
|
|
|
|
return path;
|
|
}
|