2025-08-10 01:34:16 +02:00

514 lines
18 KiB
C

#ifndef FHGFSOPSINODE_H_
#define FHGFSOPSINODE_H_
#include <app/App.h>
#include <common/threading/Mutex.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/Common.h>
#include "FhgfsInode.h"
#include "FhgfsOps_versions.h"
#include <linux/fs.h>
#include <linux/vfs.h>
// forward declaration
struct App;
struct FhgfsInodeComparisonInfo;
typedef struct FhgfsInodeComparisonInfo FhgfsInodeComparisonInfo;
#if defined(KERNEL_HAS_CURRENT_TIME_SPEC64)
typedef struct timespec64 inode_timespec;
#else
typedef struct timespec inode_timespec;
#endif
#if ! defined(KERNEL_HAS_INODE_GET_SET_CTIME)
/* These functions have been adapted from kernel code to work for older kernels.
The function signatures were introduced in version ~6.5 */
static inline time64_t inode_get_ctime_sec(const struct inode *inode)
{
return inode->i_ctime.tv_sec;
}
static inline long inode_get_ctime_nsec(const struct inode *inode)
{
return inode->i_ctime.tv_nsec;
}
static inline inode_timespec inode_get_ctime(const struct inode *inode)
{
return inode->i_ctime;
}
static inline inode_timespec inode_set_ctime_to_ts(struct inode *inode,
inode_timespec ts)
{
inode->i_ctime = ts;
return ts;
}
static inline inode_timespec inode_set_ctime(struct inode *inode,
time64_t sec, long nsec)
{
inode_timespec ts = { .tv_sec = sec,
.tv_nsec = nsec };
return inode_set_ctime_to_ts(inode, ts);
}
#endif
#if ! defined(KERNEL_HAS_INODE_GET_SET_CTIME_MTIME_ATIME)
/* These functions have been adapted from kernel code to work for older kernels.
The function signatures were introduced in version ~6.6 */
static inline time64_t inode_get_atime_sec(const struct inode *inode)
{
return inode->i_atime.tv_sec;
}
static inline long inode_get_atime_nsec(const struct inode *inode)
{
return inode->i_atime.tv_nsec;
}
static inline inode_timespec inode_get_atime(const struct inode *inode)
{
return inode->i_atime;
}
static inline inode_timespec inode_set_atime_to_ts(struct inode *inode,
inode_timespec ts)
{
inode->i_atime = ts;
return ts;
}
static inline inode_timespec inode_set_atime(struct inode *inode,
time64_t sec, long nsec)
{
inode_timespec ts = { .tv_sec = sec,
.tv_nsec = nsec };
return inode_set_atime_to_ts(inode, ts);
}
static inline time64_t inode_get_mtime_sec(const struct inode *inode)
{
return inode->i_mtime.tv_sec;
}
static inline long inode_get_mtime_nsec(const struct inode *inode)
{
return inode->i_mtime.tv_nsec;
}
static inline inode_timespec inode_get_mtime(const struct inode *inode)
{
return inode->i_mtime;
}
static inline inode_timespec inode_set_mtime_to_ts(struct inode *inode,
inode_timespec ts)
{
inode->i_mtime = ts;
return ts;
}
static inline inode_timespec inode_set_mtime(struct inode *inode,
time64_t sec, long nsec)
{
inode_timespec ts = { .tv_sec = sec,
.tv_nsec = nsec };
return inode_set_mtime_to_ts(inode, ts);
}
#endif
static inline void inode_set_mc_time(struct inode *inode,
inode_timespec ts)
{
inode_set_mtime_to_ts(inode, ts);
inode_set_ctime_to_ts(inode, ts);
}
#ifndef KERNEL_HAS_ATOMIC_OPEN
extern struct dentry* FhgfsOps_lookupIntent(struct inode* parentDir, struct dentry* dentry,
struct nameidata* nameidata);
#else
extern struct dentry* FhgfsOps_lookupIntent(struct inode* parentDir, struct dentry* dentry,
unsigned flags);
#endif // KERNEL_HAS_ATOMIC_OPEN
#ifdef KERNEL_HAS_STATX
#if defined(KERNEL_HAS_IDMAPPED_MOUNTS)
extern int FhgfsOps_getattr(struct mnt_idmap* idmap, const struct path* path,
struct kstat* kstat, u32 request_mask, unsigned int query_flags);
#elif defined(KERNEL_HAS_USER_NS_MOUNTS)
extern int FhgfsOps_getattr(struct user_namespace* ns, const struct path* path,
struct kstat* kstat, u32 request_mask, unsigned int query_flags);
#else
extern int FhgfsOps_getattr(const struct path* path, struct kstat* kstat, u32 request_mask,
unsigned int query_flags);
#endif
#else
extern int FhgfsOps_getattr(struct vfsmount* mnt, struct dentry* dentry, struct kstat* kstat);
#endif
#if defined(KERNEL_HAS_IDMAPPED_MOUNTS)
extern int FhgfsOps_setattr(struct mnt_idmap* idmap, struct dentry* dentry, struct iattr* iattr);
#elif defined(KERNEL_HAS_USER_NS_MOUNTS)
extern int FhgfsOps_setattr(struct user_namespace* ns, struct dentry* dentry, struct iattr* iattr);
#else
extern int FhgfsOps_setattr(struct dentry* dentry, struct iattr* iattr);
#endif
extern ssize_t FhgfsOps_listxattr(struct dentry* dentry, char* value, size_t size);
#ifdef KERNEL_HAS_DENTRY_XATTR_HANDLER
extern ssize_t FhgfsOps_getxattr(struct dentry* dentry, const char* name, void* value, size_t size);
extern int FhgfsOps_setxattr(struct dentry* dentry, const char* name, const void* value,
size_t size, int flags);
#else
extern ssize_t FhgfsOps_getxattr(struct inode* inode, const char* name, void* value, size_t size);
extern int FhgfsOps_setxattr(struct inode* inode, const char* name, const void* value,
size_t size, int flags);
#endif // KERNEL_HAS_DENTRY_XATTR_HANDLER
extern int FhgfsOps_removexattr(struct dentry* dentry, const char* name);
extern int FhgfsOps_removexattrInode(struct inode* inode, const char* name);
#if defined(KERNEL_HAS_GET_INODE_ACL)
extern struct posix_acl* FhgfsOps_get_inode_acl(struct inode* inode, int type, bool rcu);
#endif
#ifdef KERNEL_HAS_GET_ACL
#if defined(KERNEL_HAS_POSIX_GET_ACL_IDMAP)
extern struct posix_acl * FhgfsOps_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, int type);
#elif defined(KERNEL_HAS_POSIX_GET_ACL_NS)
extern struct posix_acl * FhgfsOps_get_acl(struct user_namespace *userns, struct dentry *dentry, int type);
#elif defined(KERNEL_POSIX_GET_ACL_HAS_RCU)
extern struct posix_acl* FhgfsOps_get_acl(struct inode* inode, int type, bool rcu);
#else
extern struct posix_acl* FhgfsOps_get_acl(struct inode* inode, int type);
#endif
int FhgfsOps_aclChmod(struct iattr* iattr, struct dentry* dentry);
#endif
#if defined(KERNEL_HAS_SET_ACL)
#if defined(KERNEL_HAS_SET_ACL_DENTRY)
#if defined(KERNEL_HAS_IDMAPPED_MOUNTS)
extern int FhgfsOps_set_acl(struct mnt_idmap* mnt_userns, struct dentry* dentry,
struct posix_acl* acl, int type);
#else
extern int FhgfsOps_set_acl(struct user_namespace* mnt_userns, struct dentry* dentry,
struct posix_acl* acl, int type);
#endif
#else
#if defined(KERNEL_HAS_SET_ACL_NS_INODE)
extern int FhgfsOps_set_acl(struct user_namespace* mnt_userns, struct inode* inode,
struct posix_acl* acl, int type);
#else
extern int FhgfsOps_set_acl(struct inode* inode, struct posix_acl* acl, int type);
#endif
#endif //KERNEL_HAS_SET_ACL_DENTRY
#endif // KERNEL_HAS_SET_ACL
#if defined(KERNEL_HAS_IDMAPPED_MOUNTS)
extern int FhgfsOps_mkdir(struct mnt_idmap* idmap, struct inode* dir,
struct dentry* dentry, umode_t mode);
extern int FhgfsOps_mknod(struct mnt_idmap* idmap, struct inode* dir,
struct dentry* dentry, umode_t mode, dev_t dev);
#elif defined(KERNEL_HAS_USER_NS_MOUNTS)
extern int FhgfsOps_mkdir(struct user_namespace* mnt_userns, struct inode* dir,
struct dentry* dentry, umode_t mode);
extern int FhgfsOps_mknod(struct user_namespace* mnt_userns, struct inode* dir,
struct dentry* dentry, umode_t mode, dev_t dev);
#elif defined(KERNEL_HAS_UMODE_T)
extern int FhgfsOps_mkdir(struct inode* dir, struct dentry* dentry, umode_t mode);
extern int FhgfsOps_mknod(struct inode* dir, struct dentry* dentry, umode_t mode, dev_t dev);
#else
extern int FhgfsOps_mkdir(struct inode* dir, struct dentry* dentry, int mode);
extern int FhgfsOps_mknod(struct inode* dir, struct dentry* dentry, int mode, dev_t dev);
#endif
#if defined KERNEL_HAS_ATOMIC_OPEN
int FhgfsOps_atomicOpen(struct inode* dir, struct dentry* dentry, struct file* file,
unsigned openFlags, umode_t createMode
#ifndef FMODE_CREATED
, int* outOpenedFlags
#endif
);
#if defined(KERNEL_HAS_IDMAPPED_MOUNTS)
extern int FhgfsOps_createIntent(struct mnt_idmap* idmap, struct inode* dir,
struct dentry* dentry, umode_t mode, bool isExclusiveCreate);
#elif defined(KERNEL_HAS_USER_NS_MOUNTS)
extern int FhgfsOps_createIntent(struct user_namespace* mnt_userns, struct inode* dir,
struct dentry* dentry, umode_t mode, bool isExclusiveCreate);
#else
extern int FhgfsOps_createIntent(struct inode* dir, struct dentry* dentry, umode_t mode,
bool isExclusiveCreate);
#endif
#elif defined KERNEL_HAS_UMODE_T
extern int FhgfsOps_createIntent(struct inode* dir, struct dentry* dentry, umode_t mode,
struct nameidata* nameidata);
#else
extern int FhgfsOps_createIntent(struct inode* dir, struct dentry* dentry, int mode,
struct nameidata* nameidata);
#endif // KERNEL_HAS_ATOMIC_OPEN
extern int FhgfsOps_rmdir(struct inode* dir, struct dentry* dentry);
extern int FhgfsOps_unlink(struct inode* dir, struct dentry* dentry);
#if defined(KERNEL_HAS_IDMAPPED_MOUNTS)
extern int FhgfsOps_symlink(struct mnt_idmap* idmap, struct inode* dir,
struct dentry* dentry, const char* to);
#elif defined(KERNEL_HAS_USER_NS_MOUNTS)
extern int FhgfsOps_symlink(struct user_namespace* mnt_userns, struct inode* dir,
struct dentry* dentry, const char* to);
#else
extern int FhgfsOps_symlink(struct inode* dir, struct dentry* dentry, const char* to);
#endif
extern int FhgfsOps_link(struct dentry* dentryFrom, struct inode* inode, struct dentry* dentryTo);
extern int FhgfsOps_hardlinkAsSymlink(struct dentry* oldDentry, struct inode* dir,
struct dentry* newDentry);
#if defined KERNEL_HAS_GET_LINK
extern const char* FhgfsOps_get_link(struct dentry* dentry, struct inode* inode,
struct delayed_call* done);
#elif defined(KERNEL_HAS_FOLLOW_LINK_COOKIE)
extern const char* FhgfsOps_follow_link(struct dentry* dentry, void** cookie);
extern void FhgfsOps_put_link(struct inode* inode, void* cookie);
#else
extern void* FhgfsOps_follow_link(struct dentry* dentry, struct nameidata* nd);
extern void FhgfsOps_put_link(struct dentry* dentry, struct nameidata* nd, void* p);
#endif
#if defined(KERNEL_HAS_IDMAPPED_MOUNTS)
extern int FhgfsOps_rename(struct mnt_idmap* idmap, struct inode* inodeDirFrom,
struct dentry* dentryFrom, struct inode* inodeDirTo, struct dentry* dentryTo, unsigned flags);
#elif defined(KERNEL_HAS_USER_NS_MOUNTS)
extern int FhgfsOps_rename(struct user_namespace* mnt_userns, struct inode* inodeDirFrom,
struct dentry* dentryFrom, struct inode* inodeDirTo, struct dentry* dentryTo, unsigned flags);
#elif defined(KERNEL_HAS_RENAME_FLAGS)
extern int FhgfsOps_rename(struct inode* inodeDirFrom, struct dentry* dentryFrom,
struct inode* inodeDirTo, struct dentry* dentryTo, unsigned flags);
#else
extern int FhgfsOps_rename(struct inode* inodeDirFrom, struct dentry* dentryFrom,
struct inode* inodeDirTo, struct dentry* dentryTo);
#endif
extern int FhgfsOps_vmtruncate(struct inode* inode, loff_t offset);
extern bool FhgfsOps_initInodeCache(void);
extern void FhgfsOps_destroyInodeCache(void);
extern struct inode* FhgfsOps_alloc_inode(struct super_block* sb);
extern void FhgfsOps_destroy_inode(struct inode* inode);
extern struct inode* __FhgfsOps_newInodeWithParentID(struct super_block* sb, struct kstat* kstat,
dev_t dev, EntryInfo* entryInfo, NumNodeID parentNodeID, FhgfsIsizeHints* iSizeHints, unsigned int metaVersion);
extern int __FhgfsOps_instantiateInode(struct dentry* dentry, EntryInfo* entryInfo,
fhgfs_stat* fhgfsStat, FhgfsIsizeHints* iSizeHints);
int __FhgfsOps_compareInodeID(struct inode* cachedInode, void* newInodeInfo);
int __FhgfsOps_initNewInodeDummy(struct inode* newInode, void* newInodeInfo);
static inline int __FhgfsOps_refreshInode(App* app, struct inode* inode, fhgfs_stat* fhgfsStat,
FhgfsIsizeHints* iSizeHints);
extern int __FhgfsOps_doRefreshInode(App* app, struct inode* inode, fhgfs_stat* fhgfsStat,
FhgfsIsizeHints* iSizeHints, bool noFlush);
extern int __FhgfsOps_revalidateMapping(App* app, struct inode* inode);
extern int __FhgfsOps_flushInodeFileCache(App* app, struct inode* inode);
extern void __FhgfsOps_clearInodeStripePattern(App* app, struct inode* inode);
// inliners
static inline void __FhgfsOps_applyStatDataToInode(struct kstat* kstat, FhgfsIsizeHints* iSizeHints,
struct inode* outInode);
static inline void __FhgfsOps_applyStatDataToInodeUnlocked(struct kstat* kstat,
FhgfsIsizeHints* iSizeHints, struct inode* outInode);
static inline void __FhgfsOps_applyStatAttribsToInode(struct kstat* kstat, struct inode* outInode);
static inline void __FhgfsOps_applyStatSizeToInode(struct kstat* kstat,
FhgfsIsizeHints* iSizeHints, struct inode* inOutInode);
static inline struct inode* __FhgfsOps_newInode(struct super_block* sb, struct kstat* kstat,
dev_t dev, EntryInfo* entryInfo, FhgfsIsizeHints* iSizeHints, unsigned int metaVersion);
static inline bool __FhgfsOps_isPagedMode(struct super_block* sb);
/**
* This structure is passed to _compareInodeID().
*/
struct FhgfsInodeComparisonInfo
{
ino_t inodeHash; // (=> inode::i_ino)
const char* entryID;
};
/**
* Note: acquires i_lock spinlock to protect i_size and i_blocks
*/
void __FhgfsOps_applyStatDataToInode(struct kstat* kstat, FhgfsIsizeHints* iSizeHints,
struct inode* outInode)
{
__FhgfsOps_applyStatAttribsToInode(kstat, outInode);
spin_lock(&outInode->i_lock); // I _ L O C K
__FhgfsOps_applyStatSizeToInode(kstat, iSizeHints, outInode);
spin_unlock(&outInode->i_lock); // I _ U N L O C K
}
/**
* Note: Caller must hold i_lock.
*/
void __FhgfsOps_applyStatDataToInodeUnlocked(struct kstat* kstat, FhgfsIsizeHints* iSizeHints,
struct inode* outInode)
{
__FhgfsOps_applyStatAttribsToInode(kstat, outInode);
__FhgfsOps_applyStatSizeToInode(kstat, iSizeHints, outInode);
}
/**
* Note: Don't call this directly - use the _applyStatDataToInode... wrappers.
*/
void __FhgfsOps_applyStatAttribsToInode(struct kstat* kstat, struct inode* outInode)
{
App* app = FhgfsOps_getApp(outInode->i_sb);
Config* cfg = App_getConfig(app);
// remote attribs (received from nodes)
outInode->i_mode = kstat->mode;
outInode->i_uid = kstat->uid;
outInode->i_gid = kstat->gid;
inode_set_atime_to_ts(outInode, kstat->atime);
inode_set_mtime_to_ts(outInode, kstat->mtime);
inode_set_ctime_to_ts(outInode, kstat->ctime);
set_nlink(outInode, kstat->nlink);
outInode->i_blkbits = Config_getTuneInodeBlockBits(cfg);
}
/**
* Note: Don't call this directly - use the _applyStatDataToInode... wrappers.
* Note: Caller must hold i_lock.
*
* @param iSizeHints might be NULL
*/
void __FhgfsOps_applyStatSizeToInode(struct kstat* kstat, FhgfsIsizeHints* iSizeHints,
struct inode* inOutInode)
{
FhgfsInode* fhgfsInode = BEEGFS_INODE(inOutInode);
if (S_ISREG(inOutInode->i_mode) && FhgfsInode_getHasPageWriteFlag(fhgfsInode) )
{
loff_t oldSize = i_size_read(inOutInode);
uint64_t lastWriteBackOrIsizeWriteTime;
/* We don't allow to decrease the file size in paged mode, as
* this may/will cause data corruption (zeros/holes instead of real data).
* The detection when to apply the server i_size is rather complex. */
if (oldSize > kstat->size)
{
if (FhgfsInode_getNoIsizeDecrease(fhgfsInode) )
return;
// there are current write-back threads running
if (FhgfsInode_getWriteBackCounter(fhgfsInode) )
return;
// must be read after reading the write-back counter
lastWriteBackOrIsizeWriteTime = FhgfsInode_getLastWriteBackOrIsizeWriteTime(fhgfsInode);
if (iSizeHints)
{
if (iSizeHints->ignoreIsize)
return; // iSizeHints tells us to ignore the value
if (time_after_eq((unsigned long) lastWriteBackOrIsizeWriteTime,
(unsigned long) iSizeHints->timeBeforeRemoteStat) )
return; /* i_size was updated or a writeback thread finished after we send the
* remote stat call, so concurrent stat/isize updates and we need to ignore
the remote value. */
}
#ifdef BEEGFS_DEBUG
{
static bool didLog = false;
struct super_block* sb = inOutInode->i_sb;
App* app = FhgfsOps_getApp(sb);
Logger* log = App_getLogger(app);
if (!didLog)
{
// unlock, as the logger gets a mutex lock -> possible lock order issue
spin_unlock(&inOutInode->i_lock);
LOG_DEBUG_FORMATTED(log, Log_WARNING, __func__,
"Warn-once: (possibly valid) isize-shrink oldSize: %lld newSize: %lld "
"lastWBTime: %llu, timeBeforeRemote; %llu\n",
oldSize, kstat->size, lastWriteBackOrIsizeWriteTime, iSizeHints->timeBeforeRemoteStat);
dump_stack();
didLog = true;
spin_lock(&inOutInode->i_lock);
}
}
#endif
}
}
i_size_write(inOutInode, kstat->size);
inOutInode->i_blocks = kstat->blocks;
}
/**
* See __FhgfsOps_newInodeWithParentID for details. This is just a wrapper function.
*/
struct inode* __FhgfsOps_newInode(struct super_block* sb, struct kstat* kstat, dev_t dev,
EntryInfo* entryInfo, FhgfsIsizeHints* iSizeHints, unsigned int metaVersion)
{
return __FhgfsOps_newInodeWithParentID(sb, kstat, dev, entryInfo, (NumNodeID){0}, iSizeHints, metaVersion);
}
bool __FhgfsOps_isPagedMode(struct super_block* sb)
{
App* app = FhgfsOps_getApp(sb);
Config* cfg = App_getConfig(app);
if (Config_getTuneFileCacheTypeNum(cfg) == FILECACHETYPE_Paged)
return true;
if (Config_getTuneFileCacheTypeNum(cfg) == FILECACHETYPE_Native)
return true;
return false;
}
/**
* See __FhgfsOps_doRefreshInode for details.
*/
int __FhgfsOps_refreshInode(App* app, struct inode* inode, fhgfs_stat* fhgfsStat,
FhgfsIsizeHints* iSizeHints)
{
// do not disable inode flushing from this (default) refreshInode function
return __FhgfsOps_doRefreshInode(app, inode, fhgfsStat, iSizeHints, false);
}
#endif /* FHGFSOPSINODE_H_ */