#include "spadfs.h"

static void *SPADFS_FOLLOW_LINK(struct dentry *dentry, struct nameidata *nd)
{
	SPADFNODE *f = spadfnode(dentry->d_inode);
	struct fnode_ea *lnk = GET_EA((struct fnode_ea *)f->ea, f->ea_size, EA_SYMLINK_MAGIC, FNODE_EA_MAGIC_MASK);
	unsigned len;
	char *str;
	if (__unlikely(!lnk)) {
		SPADFS_ERROR(f->fs, TXFLAGS_EA_ERROR, "SPADFS_FOLLOW_LINK: CAN'T FIND SYMLINK EXTENDED ATTRIBUTE ON FNODE %LX/%X", (unsigned long long)f->fnode_block, f->fnode_pos);
		return ERR_PTR(-EFSERROR);
	}
	if (__unlikely(lnk == GET_EA_ERROR)) {
		SPADFS_ERROR(f->fs, TXFLAGS_FS_ERROR, "SPADFS_FOLLOW_LINK: ERROR PARSING EXTENDED ATTRIBUTES ON FNODE %LX/%X", (unsigned long long)f->fnode_block, f->fnode_pos);
		return ERR_PTR(-EFSERROR);
	}
	len = lnk->magic >> FNODE_EA_SIZE_SHIFT;
	str = kmalloc(len + 1, GFP_NOFS);	/* ??? GFP_KERNEL */
	if (__unlikely(!str)) return ERR_PTR(-ENOMEM);
	memcpy(str, lnk + 1, len);
	str[len] = 0;
	nd_set_link(nd, str);
	return str;
}

static void SPADFS_PUT_LINK(struct dentry *dentry, struct nameidata *nd, void *str)
{
	kfree(str);
}

static int SPADFS_SYMLINK_SETATTR(struct dentry *dentry, struct iattr *iattr)
{
	iattr->ia_valid &= ~(ATTR_MODE | ATTR_UID | ATTR_GID);
	return SPADFS_FILE_SETATTR(dentry, iattr);
}

struct inode_operations spadfs_symlink_iops = {
	.readlink = generic_readlink,
	.follow_link = SPADFS_FOLLOW_LINK,
	.put_link = SPADFS_PUT_LINK,
	.setattr = SPADFS_SYMLINK_SETATTR,
};
