/**
 * sdp_xattr.c
 *
 * get/set sdp context to xattr
 */
#include "../fscrypt_private.h"
#include "fscrypto_sdp_xattr_private.h"
#include <linux/xattr.h>

#define FSCRYPT_SDP_FS_TYPE_EXT4_STR "ext4"
#define FSCRYPT_SDP_FS_TYPE_F2FS_STR "f2fs"
#define FSCRYPT_SDP_XATTR_NAME_ENCRYPTION_SDP_CONTEXT "sdp"

//Must sync with ext4/xattr.h
#define FSCRYPT_SDP_EXT4_XATTR_INDEX_ENCRYPTION 9
extern int ext4_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t);
//Must sync with f2fs/xattr.h
#define FSCRYPT_SDP_F2FS_XATTR_INDEX_ENCRYPTION 9
#ifdef CONFIG_F2FS_FS_XATTR
extern int f2fs_setxattr(struct inode *, int, const char *,
				const void *, size_t, struct page *, int);
extern int f2fs_getxattr(struct inode *, int, const char *, void *,
						size_t, struct page *);
#else
static inline int f2fs_setxattr(struct inode *inode, int index,
		const char *name, const void *value, size_t size,
		struct page *page, int flags)
{
	return -EOPNOTSUPP;
}
static inline int f2fs_getxattr(struct inode *inode, int index,
			const char *name, void *buffer,
			size_t buffer_size, struct page *dpage)
{
	return -EOPNOTSUPP;
}
#endif

int fscrypt_sdp_get_context(struct inode *inode, void *ctx, size_t len)
{
	const char *fs_type_name = inode->i_mapping->host->i_sb->s_type->name;

	if (!strcmp(FSCRYPT_SDP_FS_TYPE_EXT4_STR, fs_type_name)) {
		return ext4_xattr_get(inode, FSCRYPT_SDP_EXT4_XATTR_INDEX_ENCRYPTION,
				FSCRYPT_SDP_XATTR_NAME_ENCRYPTION_SDP_CONTEXT, ctx, len);
	} else if (!strcmp(FSCRYPT_SDP_FS_TYPE_F2FS_STR, fs_type_name)) {
		return f2fs_getxattr(inode, FSCRYPT_SDP_F2FS_XATTR_INDEX_ENCRYPTION,
				FSCRYPT_SDP_XATTR_NAME_ENCRYPTION_SDP_CONTEXT, ctx, len, NULL);
	}

	return -ENOTTY;
}

static int __fscrypt_sdp_set_context(struct inode *inode, void *ctx, size_t len, bool is_lock, void *fs_data)
{
	int retval = -ENOTTY;
	const char *fs_type_name = inode->i_mapping->host->i_sb->s_type->name;

	if (is_lock) inode_lock(inode);
	if (!strcmp(FSCRYPT_SDP_FS_TYPE_EXT4_STR, fs_type_name)) {
		retval = ext4_xattr_set(inode, FSCRYPT_SDP_EXT4_XATTR_INDEX_ENCRYPTION,
				FSCRYPT_SDP_XATTR_NAME_ENCRYPTION_SDP_CONTEXT, ctx, len, 0);
	} else if (!strcmp(FSCRYPT_SDP_FS_TYPE_F2FS_STR, fs_type_name)) {
		retval = f2fs_setxattr(inode, FSCRYPT_SDP_F2FS_XATTR_INDEX_ENCRYPTION,
				FSCRYPT_SDP_XATTR_NAME_ENCRYPTION_SDP_CONTEXT, ctx, len, fs_data, 0);
	}
	if (is_lock) inode_unlock(inode);

	return retval;
}

int fscrypt_sdp_set_context(struct inode *inode, void *ctx, size_t len, void *fs_data)
{
	return __fscrypt_sdp_set_context(inode, ctx, len, true, fs_data);
}

int fscrypt_sdp_set_context_nolock(struct inode *inode, void *ctx, size_t len, void *fs_data)
{
	return __fscrypt_sdp_set_context(inode, ctx, len, false, fs_data);
}

static int __fscrypt_knox_set_context(struct inode *inode, void *ctx, size_t len)
{
	if (inode->i_sb->s_cop->android_oem_data1[1]) {
		return ((int (*)(struct inode*, const char*, const void*, size_t, void *))(inode->i_sb->s_cop->android_oem_data1[1]))(inode, NULL, ctx, len, NULL);
	} else {
		return -EOPNOTSUPP;
	}
}

int fscrypt_knox_set_context(struct inode *inode, void *ctx, size_t len)
{
	return __fscrypt_knox_set_context(inode, ctx, len);
}