/*
 * \brief   libc_fuse_ntfs-3g
 * \author  Josef Soentgen
 * \date    2013-11-11
 */

/*
 * Copyright (C) 2013-2017 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU Affero General Public License version 3.
 */

/* Genode includes */
#include <base/log.h>
#include <util/string.h>

#include <fuse.h>
#include <fuse_private.h>

/* libc includes */
#include <stdlib.h>

extern "C" {

#include <device.h>
#include <security.h>
#include <ntfs-3g_common.h>

extern struct fuse_operations ntfs_3g_ops;

struct fuse_chan *fc;
struct fuse      *fh;

extern ntfs_fuse_context_t **ntfs_fuse_ctx();
extern int ntfs_open(const char*);
extern void ntfs_close(void);

}


#if 1  // HoG_GENODE
#include <format/snprintf.h>
static
int genode_log(
	const char * function,
	const char * file,
	int line,
	u32 level,
	void * data,
	const char * format,
	va_list list
	)
{
	///later: logging level hardcoded in genode_log() -> find out how to parametrize from init_fs() instead?
	if( ! (level & (NTFS_LOG_LEVEL_CRITICAL | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_ERROR)) )
//	if( level < 256 )
		return 0;
	
	char dst[1024] = {};
	Format::String_console sc( dst, sizeof(dst) );
	{
		sc.vprintf( format, list );
		va_end( list );
		
		// blank-out the trailing newline, since Genode::log() adds one on its own
		auto carriage = strchr( dst, '\n' );
		if( carriage )
			*carriage = ' ';
	}
	
	Genode::log( "[", level, "] ", "\033[37m", function, "\033[0m " "() :", line, ": ", (const char*)dst );
		// 30m: dark gray
		// 31m: red (à la error())
		// 32m: dark green
		// 33m: yellow
		// 34m: blue (à la warning())
		// 35m: purple
		// 36m: alt blue
		// 37m: bright pink
	return sc.len();
}
#endif


#if 1  // HoG_GENODE
bool Fuse::init_fs( const char * devicepath, bool configured_as_readwrite )
#else
bool Fuse::init_fs(void)
#endif
{
	ntfs_set_locale();
#if 1  // HoG_GENODE
	// xx this one is ignored for some reason, so let's hardcode up there instead
	ntfs_log_set_levels( NTFS_LOG_LEVEL_CRITICAL | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_ERROR );
	ntfs_log_set_handler( genode_log );
#else
	ntfs_log_set_handler(ntfs_log_handler_stderr);
#endif

	ntfs_fuse_context_t **ctx = ntfs_fuse_ctx();

	*ctx = reinterpret_cast<ntfs_fuse_context_t *>(malloc(sizeof (ntfs_fuse_context_t)));
	if (!*ctx) {
		Genode::error("out of memory");
		return false;
	}

	Genode::memset(*ctx, 0, sizeof (ntfs_fuse_context_t));
#if defined( HAVE_SETXATTR )
	(*ctx)->streams = NF_STREAMS_INTERFACE_XATTR;  /* Map named data streams to xattrs. */
#else  // no SETXATTR:
	(*ctx)->streams = NF_STREAMS_INTERFACE_NONE;
#endif  // ~no SETXATTR
	(*ctx)->atime   = ATIME_RELATIVE;
#if 1  // HoG_GENODE
//ATIME_DISABLED instead? so that ntfs won't attempt to write back to disk (the atime) ?
	//.. these require calling ntfs_set_shown_files() separately from ntfs_mount()..?
	//+ show_sys_files = true;
	//+ hide_hid_files = false;
	//+ hide_dot_files = false;
	//+ windows_names = false;
	//+ ignore_case = false;  OOPS, what about it ?
	(*ctx)->blkdev = TRUE;  // thought this would help with either block-sized access, or read-write, but seems irrelevant in fact.. Maybe it helps with fcntl(exclusive) ?
#endif  // ~HoG_GENODE
	(*ctx)->silent  = TRUE;  // silence the ntfs_mst_post_read_fixup_warn notices...
	(*ctx)->recover = TRUE;
#if 1  // HoG_GENODE
	(*ctx)->ro = configured_as_readwrite ? FALSE : TRUE;  // !
		/*
		READ-ONLY versus read/write flag:
		Careful to *only* enable read-write ("ro == FALSE") if the backing
		components provide access all the way down (vfs server, part_block, ahci_drv...).
		Otherwise NTFS will fail not only to write, but also to *read*, in some cases!
		Hence, if a partition is read-only for any reason then libntfs-3g must be made
		aware of that using that one flag above and not other schemes,
		otherwise libntfs will attempt to write stuff to disk even when asked to READ (!), e.g.
		readdir() might fail to read some directories in mysterious ways, with the below cryptic
		errors as the only hints as to what's going on:
			[init -> vfs_ntfs] ntfs_fuse_readdir () :1121: </hog_genode_AudioStation> -------------------------
			[init -> vfs_ntfs] ntfs_attr_mst_pwrite () :2629: ntfs_attr_mst_pwrite: written=-1
			[init -> vfs_ntfs] ntfs_ib_write () :92: Failed to write index block 0, inode 5
			[init -> vfs_ntfs] ntfs_attr_mst_pwrite () :2629: ntfs_attr_mst_pwrite: written=-1
			[init -> vfs_ntfs] ntfs_mft_records_write () :188: Error writing $Mft record(s)
			[init -> vfs_ntfs] ntfs_inode_sync_in_dir () :1033: MFT record sync failed, inode 167
			[init -> vfs_ntfs] ntfs_attr_mst_pwrite () :2629: ntfs_attr_mst_pwrite: written=-1
			[init -> vfs_ntfs] ntfs_mft_records_write () :188: Error writing $Mft record(s)
			[init -> vfs_ntfs] ntfs_inode_sync_in_dir () :1033: MFT record sync failed, inode 167
			[init -> vfs_ntfs] Error: ntfs.num_dirent</hog_genode_AudioStation>: fuse.readdir() failed, code: -16
		*/
	(*ctx)->hiberfile = configured_as_readwrite ? FALSE : TRUE;  // disable hiberfile detection, i.e. allow mounting a partition even if it's hibernated (only in Read-Only mode, would probably be dangerous to do read-write fiddling with an hibernated partition!)
	//+ sync = ..?
	//(*ctx)->debug = TRUE;  // does not seem to output anything...
#endif  // ~HoG_GENODE

	/*
	*ctx = (ntfs_fuse_context_t) {
		.uid = 0,
		.gid = 0,
		.streams = NF_STREAMS_INTERFACE_NONE,
		.atime   = ATIME_RELATIVE,
		.silent  = TRUE,
		.recover = TRUE
	};
	*/

#if 0
	Genode::log("libc_fuse_ntfs-3g: try to mount /dev/blkdev...");

	int err = ntfs_open("/dev/blkdev");
	if (err) {
		Genode::error("libc_fuse_ntfs-3g: could not mount /dev/blkdev");
		return false;
	}
#else  // HoG_GENODE:
	Genode::log("libc_fuse_ntfs-3g: try to mount ", devicepath, " ...");
	
	int err = ntfs_open( devicepath );  // e.g. "/dev/blkdev"
	if (err) {
		Genode::error("libc_fuse_ntfs-3g: could not mount ", devicepath);
		return false;
	}
#endif

	fh = fuse_new(fc, NULL, &ntfs_3g_ops, sizeof (ntfs_3g_ops), NULL);
	if (fh == 0) {
		Genode::error("libc_fuse_ntfs-3g: fuse_new() failed");
		ntfs_close();
		return false;
	}

	(*ctx)->mounted = TRUE;

	return true;
}


void Fuse::deinit_fs(void)
{
	Genode::log("libc_fuse_ntfs-3g: unmount /dev/blkdev...");
	ntfs_close();

	free(*ntfs_fuse_ctx());
}


void Fuse::sync_fs(void)
{
	Genode::log("libc_fuse_ntfs-3g: sync file system...");
	ntfs_device_sync((*ntfs_fuse_ctx())->vol->dev);
}


bool Fuse::support_symlinks(void)
{
	return true;
}
