#ifndef __COMPAT_LINUX_FS_H__
#define __COMPAT_LINUX_FS_H__

#include_next <linux/fs.h>

#ifndef SLAB_MEM_SPREAD
#define SLAB_MEM_SPREAD	((slab_flags_t __force)0)
#endif

#if defined(COMPAT_DETECT_INCLUDE_LINUX_FILELOCK)
#include <linux/filelock.h>
#endif

#if defined(COMPAT_DETECT_GENERIC_FILLATTR_6_6)
#define COMPAT_FILLATTR(request_mask) request_mask,
#else
#define COMPAT_FILLATTR(request_mask)
#endif

#if !defined(COMPAT_DETECT_INODE_GET_CTIME_6_6)
/**
 * inode_set_ctime_to_ts - set the ctime in the inode
 * @inode: inode in which to set the ctime
 * @ts: value to set in the ctime field
 *
 * Set the ctime in @inode to @ts
 */
static inline struct timespec64 inode_set_ctime_to_ts(struct inode *inode,
						      struct timespec64 ts)
{
	inode->i_ctime = ts;
	return ts;
}

/**
 * inode_set_ctime - set the ctime in the inode
 * @inode: inode in which to set the ctime
 * @sec: tv_sec value to set
 * @nsec: tv_nsec value to set
 *
 * Set the ctime in @inode to { @sec, @nsec }
 */
static inline struct timespec64 inode_set_ctime(struct inode *inode,
						time64_t sec, long nsec)
{
	struct timespec64 ts = { .tv_sec  = sec, .tv_nsec = nsec };

	return inode_set_ctime_to_ts(inode, ts);
}

static inline long inode_get_ctime_nsec(const struct inode *inode)
{
	return inode->i_ctime.tv_nsec;
}

static inline struct timespec64 inode_get_ctime(const struct inode *inode)
{
	return inode->i_ctime;
}

static inline struct timespec64 inode_set_ctime_current(struct inode *inode)
{
	struct timespec64 now = current_time(inode);

	inode_set_ctime(inode, now.tv_sec, now.tv_nsec);
	return now;
}

#endif /* COMPAT_DETECT_INODE_GET_CTIME_6_6 */

#if !defined(COMPAT_DETECT_INODE_GET_ATIME_6_7)
static inline time64_t inode_get_ctime_sec(const struct inode *inode)
{
#if !defined(COMPAT_DETECT_INODE_GET_CTIME_6_6)
	return inode->i_ctime.tv_sec;
#else
	return inode_get_ctime(inode).tv_sec;
#endif
}

static inline struct timespec64 inode_set_mtime_to_ts(struct inode *inode,
						      struct timespec64 ts)
{
	inode->i_mtime = ts;
	return ts;
}

static inline struct timespec64 inode_set_mtime(struct inode *inode,
						time64_t sec, long nsec)
{
	struct timespec64 ts = { .tv_sec  = sec, .tv_nsec = nsec };

	return inode_set_mtime_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 struct timespec64 inode_get_mtime(const struct inode *inode)
{
	return inode->i_mtime;
}

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 struct timespec64 inode_get_atime(const struct inode *inode)
{
	return inode->i_atime;
}

static inline struct timespec64 inode_set_atime_to_ts(struct inode *inode,
						      struct timespec64 ts)
{
	inode->i_atime = ts;
	return ts;
}

static inline struct timespec64 inode_set_atime(struct inode *inode,
						time64_t sec, long nsec)
{
	struct timespec64 ts = { .tv_sec  = sec, .tv_nsec = nsec };

	return inode_set_atime_to_ts(inode, ts);
}

static inline struct timespec64 simple_inode_init_ts(struct inode *inode)
{
	struct timespec64 ts = inode_set_ctime_current(inode);

	inode_set_atime_to_ts(inode, ts);
	inode_set_mtime_to_ts(inode, ts);
	return ts;
}

#endif /* COMPAT_DETECT_INODE_GET_ATIME_6_7 */

#ifndef COMPAT_DETECT_INODE_UPDATE_TIMESTAMPS_6_6
int inode_update_timestamps(struct inode *inode, int flags);
#endif

#ifdef COMPAT_DETECT_GENERIC_PERMISSION_MNT_IDMAP_6_3
#define COMPAT_STRUCT_MNT_IDMAP	struct mnt_idmap
#define COMPAT_NOP_MNT_IDMAP	nop_mnt_idmap
#else /* !COMPAT_DETECT_GENERIC_PERMISSION_MNT_IDMAP_6_3 */
#define COMPAT_STRUCT_MNT_IDMAP	struct user_namespace
#define COMPAT_NOP_MNT_IDMAP	init_user_ns
#endif /* COMPAT_DETECT_GENERIC_PERMISSION_MNT_IDMAP_6_3 */

#if defined(COMPAT_DETECT_STRUCT_FILE_LOCK_CORE_6_9)
#define COMPAT_DETECT_SET_LEASE_INT_PARAM_6_6
#endif

#if defined(COMPAT_DETECT_SET_LEASE_INT_PARAM_6_6)
#define FS_SETLEASE_ARG_TYPE int
#else
#define FS_SETLEASE_ARG_TYPE long
#endif

#if defined(COMPAT_DETECT_FILLDIR_T_RET_BOOL_6_1)
#define FILLDIR_T_RET_TYPE bool
#else
#define FILLDIR_T_RET_TYPE int
#endif

#ifndef COMPAT_DETECT_SETATTR_SHOULD_DROP_SGID_6_2
static inline int setattr_should_drop_sgid(COMPAT_STRUCT_MNT_IDMAP *idmap,
						const struct inode *inode)
{
	mode_t mode = inode->i_mode;
	if (!(mode & S_ISGID))
		return 0;
	if (mode & S_IXGRP)
		return ATTR_KILL_SGID;
	if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
		return ATTR_KILL_SGID;
	return 0;
}
#endif

static inline spinlock_t* compat_address_space_private_lock(struct address_space* mapping)
{
#ifdef COMPAT_DETECT_ADDRESS_SPACE_I_PRIVATE
	return &mapping->i_private_lock;
#else
	return &mapping->private_lock;
#endif
}

#if !defined(COMPAT_DETECT_WRITE_USES_FOLIO_6_12) && !defined(COMPAT_DETECT_WRITE_BEGIN_KIOCB_6_17)
extern ssize_t vnfs_generic_perform_write(struct kiocb *, struct iov_iter *);
#define generic_perform_write vnfs_generic_perform_write
#endif /* COMPAT_DETECT_WRITE_USES_FOLIO_6_12 */

#ifndef COMPAT_DETECT_FOLIO_SET_ERROR_6_12
static inline void folio_set_error(struct folio *folio) { /* empty */ }
#endif /* COMPAT_DETECT_WRITE_USES_FOLIO_6_12 */

#ifdef COMPAT_DETECT_VFS_MKDIR_RETURNS_DENTRY_6_15
static inline int compat_vfs_mkdir(COMPAT_STRUCT_MNT_IDMAP *idmap,
				   struct inode *dir, struct dentry *dentry,
				   umode_t mode)
{
	struct dentry *result = vfs_mkdir(idmap, dir, dentry, mode);
	return PTR_ERR_OR_ZERO(result);
}
#define vfs_mkdir compat_vfs_mkdir
#endif /* COMPAT_DETECT_VFS_MKDIR_RETURNS_DENTRY_6_15 */

#ifndef no_llseek
#define no_llseek NULL
#endif

#endif /* __COMPAT_LINUX_FS_H__ */
