#ifndef xattr_fuse_H
#define xattr_fuse_H
/*
 * Copyright 2021-2023, ttcoder of TTS-LLC
 */

/* Genode includes */
#include <vfs/vfs_handle.h>


#ifndef DISABLE_XATTR

struct XaListing_vfs_handle : public Vfs::Vfs_handle
{
private:

	static const int debug = 1;
	
		String<256/*MAX_PATH_LEN*/> _path;

public:
	XaListing_vfs_handle(Vfs::Directory_service &ds,
	                Vfs::File_io_service   &fs,
	                Genode::Allocator &alloc,
	                Directory::Path path)
	:	Vfs_handle(ds, fs, alloc, 0)
		,_path(path)
	{
	}
	
	Vfs::File_io_service::Read_result
		read(char *dest, Vfs::file_size len, size_t &out_count)
	{
		//Genode::log("   xa-handle.read attrname=", _xattr_name, " file=", _path);
		
		int ret = Fuse::fuse()->op.listxattr(_path.string(), dest, len);
		
		if( debug )
		{
			log("Fuse XALIST RES: ", ret);
			log("Fuse XALIST buf: ", (const char*)dest);
		}
		
		if (ret < 0) return Vfs::File_io_service::READ_ERR_INVALID;//
		
		out_count = ret;
		return Vfs::File_io_service::READ_OK;
	}
	
	Vfs::File_io_service::Write_result
		write(char const *src, Vfs::file_size len, size_t &out_count)
	{
		Genode::warning("XaListing_vfs_handle write denied -- we are not supposed to be written to ; path=", _path);
		
		return Vfs::File_io_service::WRITE_ERR_INVALID;//
	}
};


struct Xattr_vfs_handle : public Vfs::Vfs_handle
{
private:
	static const int debug = 1;
	
		String<256> /**/ _path;
		String<256> _xattr_name;

public:
	Xattr_vfs_handle(Vfs::Directory_service &ds,
	                Vfs::File_io_service   &fs,
	                Genode::Allocator &alloc,
	                Directory::Path path,
	                String<256> xattr_name)
	:	Vfs_handle(ds, fs, alloc, 0)
		,_path(path)
		,_xattr_name(xattr_name)
	{
	}
	
	// Static helper, used in open():
	//
	static bool FuseHasAttr( const char * node_path, const char * attr_name )
	{
		char minibuf[8];  // tiny buf: we don't want to retrieve (all) the payload, just get a status code.
		
		int ret = Fuse::fuse()->op.getxattr( node_path, attr_name, minibuf, sizeof(minibuf) );
		
		if( debug )
			log( "  fuse-has-attr -> ", ret, " for ", attr_name, "  <", node_path, ">" );
		
		return
			ret >= 0  // attr does exist
			||
			ret == -34  // buffer-too-small error: means the attr *does exist*, even if too large to fit the teeny-weeny test buffer above
			;
	}
	
	Vfs::File_io_service::Read_result
		read(char *dest, Vfs::file_size len, size_t &out_count)
	{
		//if( debug )
		//Genode::log("   xa-handle.read attrname=", _xattr_name, " file=", _path);
		
		memset( dest, '\0', len );  // otherwise the below log( dest ) might show garbage...
		
		int ret = Fuse::fuse()->op.getxattr(_path.string(), _xattr_name.string(), dest, len);
		if( debug )
		{
			log("fuse.xa.Read <", _xattr_name, "> : ", ret);
				// ret is '-34' when dest buffer is too small (?)
				// ret is '-45' when libntfs-3g is missing HAVE_SETXATTR or NF_STREAMS_INTERFACE_XATTR
				// ret is '-74565' when attr is absent...
	//		log("fuse.xa.Read buf: ", (const char*)dest);
				///later: casting as char* does not make for useful tracing if the attribute is a Float or Int32... (tunetracker:trim_start and others)
		}
		
		// we no longer need this for handling an absent xattr (& interpolate READ_ERR_ to EINVAL/ENOATTR), since
		// a non-existent attr is now caught upstream, in open() ; so in this code path we now log an error:
		if (ret < 0)
		{
			error( "Xattr_vfs_handle.read(): FUSE.getxattr returns -> ", ret, " -> returning READ_ERR_INVALID, which might trigger a VFS stall/lock-up !" );
			
			return Vfs::File_io_service::READ_ERR_INVALID;
		}
		
		out_count = ret;  // unlike in the write() case, getxattr() does provide us with the number of retrieved bytes in 'ret', so we can use it here
		return Vfs::File_io_service::READ_OK;
	}
	
	Vfs::File_io_service::Write_result
		write(char const *src, Vfs::file_size len, size_t &out_count)
	{
		//if( debug )
		//Genode::log("   xa-handle.write attrname=", _xattr_name, " file=", _path, " len=", len);
		
		int ret = Fuse::fuse()->op.setxattr(_path.string(), _xattr_name.string(), src, len, 0);
		if( debug )
			log("fuse.xa.Write <", _xattr_name, "> (res ought to be zero): ", ret);
		
		if (ret < 0)
		{
			error( "Xattr_vfs_handle.write(): FUSE.setxattr returns -> ", ret, " -> returning WRITE_ERR_INVALID, which might trigger a VFS stall/lock-up !" );
			
			return Vfs::File_io_service::WRITE_ERR_INVALID;
		}
		
		out_count = len;  // cannot return "ret" here, as it's always 0 in case of success...
		return Vfs::File_io_service::WRITE_OK;
	}
};

#endif  // ~DISABLE_XATTR


#if 0
{
	log("-- running tests on: ", virt.phys_path());
	
	const char * value = "Pink Floyd";/**/
	char buf[1024]={};
	int res = 0;
	
	char list[2048] = {};
	
	res = Fuse::fuse()->op.listxattr(virt.phys_path(), list, sizeof(list));
	log("List  RES: ", res, " buf contents: ", (const char*)list);

	// prefix with "user." otherwise NTFS returns error -45:
	res = Fuse::fuse()->op.setxattr(virt.phys_path(), "user.Audio:Title"/**/, value, strlen(value)+sizeof('\0'), 0);  // let's include the trailing 0 to be safe
	log("Write RES: ", res);

	res = Fuse::fuse()->op.getxattr(virt.phys_path(), "user.Audio:Title", buf, sizeof(buf));
	log("Read  RES: ", res, " buf contents: ", (const char*)buf);

	res = Fuse::fuse()->op.listxattr(virt.phys_path(), list, sizeof(list));
	log("List  RES: ", res, " buf contents: ", (const char*)list);
}
#endif



#endif // xattr_fuse_H

