/*
 * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */



#if defined( HoG_GENODE )
/**************/
static int debug = 0;
#define HoG_GENODE_TRACE(x) //x
// Plus tons of "if( debug )" lines down this file which are /not/ surrounded with #if's, for readability
#include <support/String.h>
#include <support/StopWatch.h>
//#include <base/debug.h>  // PDBG()
#	if 1
#		undef PDBG
#		define PDBG(...) ;
#	endif
#endif

#define TRACE() //printf( "    [%s]\n", __func__ );


// HoG_GENODE:
// bunch of code remed out below as it has
// no Genode support (good thing that it does not seem
// to be needed downstream anyway).



//#include <BeOSBuildCompatibility.h>

#include "fs_impl.h"

#include <dirent.h>
#if 0
	//See below for a fix that makes create_directory() work, otherwise
	//the internals return B_OK because errno is set to 2 (and not -2) because we call the Genode errno instead of _errnop in misc.cpp and its
	//		*error = B_TO_POSITIVE_ERROR(*error);
	// line !
	// Alternatively, we could create an overriding "posix_overrides_temp/errno.h" (re re-use the 'haiku build' one that relates to redirected-errno in misc.cpp which translates POSIX to haiku) ? -- then fs.cpp, Directory.cpp etc will use the correct (negative) errno error codes !
#include <errno.h>
#endif
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <utime.h>
#include <sys/stat.h>
#include <sys/time.h>

#include <map>
#include <string>

#include <fs_attr.h>
#include <NodeMonitor.h>	// for B_STAT_* flags
#include <syscalls.h>

#include "fs_descriptors.h"
#include "NodeRef.h"
#include "remapped_functions.h"

#if defined(HAIKU_HOST_PLATFORM_FREEBSD)
#	include "fs_freebsd.h"
#endif


using namespace std;
using namespace BPrivate;


#if defined( HoG_GENODE )

#include <base/log.h>
static void FixUp_Errno()
{
	if( debug >= 2 )
		Genode::log( "  FixUp_Errno: old/BSD errno is ", errno );
	
	switch( errno )
	{
///later: investigate B_USE_POSITIVE_POSIX_ERRORS: I could "turn upside down" B_NOT_ALLOWED and friends, to be compatible, I think with "-D B_USE_POSITIVE_POSIX_ERRORS" in Errors.h ; but maybe that would just revert the problem on the flip side, because then it would be Haiku code that would suffer from non-negative errors ?
		case EPERM:  errno = B_NOT_ALLOWED; break;  // EPERM==1 -> B_NOT_ALLOWED==0x8000000f  (EPERM in symlink(): The filesystem containing linkpath does not support the creation of symbolic links.) (Genode's libc e.g. returns this from OPENLINK_ERR_PERMISSION_DENIED)
		case ENOENT: errno = B_ENTRY_NOT_FOUND; break;  // ENOENT==2
		case EISDIR: errno = B_IS_A_DIRECTORY;  break;  // EISDIR==21
		/*
		case EEXIST: puts("EEXIST");
		case EIO: puts("EIO");
		case ELOOP: puts("ELOOP");
		case ENAMETOOLONG: puts("ENAMETOOLONG");
		case ENOSPC: puts("ENOSPC");
		case ENOTDIR: puts("ENOTDIR");
		case EROFS: puts("EROFS");
		*/
		default:
			Genode::error( "don't know how to fix errno <", errno, ">, leaving as-is" );
			//we can get this, when calling symlink() on an already-existing symbolic link:
			//Error: don't know how to fix errno <63>, leaving as-is
	}
}
///later-review: Fallout from that "clobbering" of errno: probed a little, grepping for "errno" or "ENOENT" etc:
// - errno is used in e.g. vfs_rump.cc, but seems to be read immediately after being affected
// - execve.cc (libports/src/lib/libc) uses this, warrants more study
// - file_operations.cc seems clobber-agnostic
// - vfs_plugin.cc only affects (but does not read) errno, should be ok
// Any other reason to worry?
// - probably more-so in downstream (i.e. Haiku applications) code, than in upstream code?


#include <base/mutex.h>///

static
void Fixup_Stat( const char * path, struct stat * st )
{
	/*
	Why do we need to fix-up stat(), everywhere ?
	
	Here's the problem with stat() and the device number it returns, one can see that
	my policy "all files inside /boot should have the same device ID, ideally ID 0"
	is not enforced for the root dir itself:
		[init -> basic-layers] genode.stat of: "/boot"   yields device: 155872
		[init -> basic-layers] genode.stat of: "/boot/Music"  yields device: 0
	Indeed, Genode affects a different dev ID for files inside /boot, and for /boot itself
	(the latter gets the parent dir's ID, i.e. the device/perspective of the "/" dir,
	whereas /boot's contents is seen from the perspective of /boot).
	
	I cannot fix that in my stat() implementation of the File System (e.g. NTFS) as that
	stat() implementation is NOT called in the edge case of the root dir:
	- only for files INSIDE (below) /boot, my own VFS plugin's stat() is called, where I can yield 0
	- however when *outside* of /boot itself, genode-FS stat() is called, which returns e.g. an addr_t integer like 155872.
	
	I could have gone with making all sub-children of /boot return the device of /boot (e.g. 155872),
	but went the other way around instead, make sure /boot is at device 0, like all sub-children.
	
	///later-review: would it be possible/easy/easier-than-the-below for the VFS plug-in to retrieve the dev ID of "/boot" (from Genode's perspective) and apply that to its whole hierarchy ?
	*/
	
#if 0
	// identify by name:
	if( strstr(path, "/boot") && st->st_dev )//
#else
	static int GenodeSide_boot_device = -1;
	if( debug >= 3 )
		printf( "    Fixup_Stat <%s> <%ld:%ld>  (t- GSbootdev: %d)\n", path, st->st_dev, st->st_ino, GenodeSide_boot_device );
	
	// we want to identify "/boot" by node/dev instead of by name, as much as possible,
	// since the name is not always available (e.g. when dealing with FDs instead of paths),
	// so retrieve it first chance we get. (heck, it's even retrieved _repeatedly_...)
	//
	{
		///xx MTSAFE_GUARD -- in fact, does not make much sense nesting the guard inside this scope, either move it up to the above (function) scope, or ditch the mutex and use atomic_set etc ?
		static Genode::Mutex lock;
		Genode::Mutex::Guard lock_guard( lock );
		
		// Detect "/boot" by name, to collect its inode/dev, for easy future detection:
		if( path && 0==strcmp(path, "/boot") )
			GenodeSide_boot_device = st->st_dev;
	}
	
	// Detect "/boot" root dir by inode and dev-id (instead of by path):
	// So test e.g.: if( st->st_dev == 155872 && st->st_ino == 1 )...
	//
	if( GenodeSide_boot_device >= 0 && st->st_dev == GenodeSide_boot_device && st->st_ino == 1 )
#endif
	{
		if( debug )
			Genode::log( "fixup dev/ino (0, 1) instead of (", GenodeSide_boot_device, ", 1) ; name param is: ", path );
		
		// !
		st->st_dev = 0; ///+: DEV_ID_FOR_BOOT_VOLUME;
		// !
	}
}

///later: oddity: at some point I was getting this assert() from Tracker, but it stopped without apparent code changes (?!): [init -> Tracker] debugger() called: haiku-on-genode/kits/tracker/Model.cpp:878: IsVolume()
///later: review oddity: some sort of confusion in VFS, /boot and /boot/home have the SAME inode (check the run file) : (bad normalization? check my normalization-optim-dodger..)
/*
[init -> Tracker] Warning: fixup dev/ino (0, 1) instead of (100256, 1) ; name param is: /boot/home/..
[init -> Tracker] Warning: fixup dev/ino (0, 1) instead of (100256, 1) ; name param is: /boot
*/
#endif

///ToDo: "/boot" device fix-up: the above is good enough to work on NTFS, but does not work with RAM disk.
// Running Tracker on ram-disk, "here we go again", it should say dev "0" for both, but it does not:
/*
	in BPoseView::AddPosesTask():
	[init -> Tracker]   ref.name: <boot>
	[init -> Tracker]   count = 1 with Name() <home>
	[init -> Tracker]   affecting-1 dev <0> <1>  // "/boot": okay
	[init -> Tracker]   affecting-2 dev <110848> <1>  // "/boot/home" : oops ! We revert to ram-disk's dev_t 110848
It all works on NTFS as the NTFS plug-in returns 0 dev, but not on RAM (which returns 110656 etc)

Idea: maybe it was wrong from the get-go, to fix-up here in fs.cpp,
and it should be up to the (eg NTFS) vfs plug-in to make sure its descendants
have the same dev_id as their genode-provided root dir ?

Thus I should stop hardcoding 0 for /boot, and have something like this:
	Fixup_stat()
	{
		{
			Mutex...
			
			static int boot_blah
			if( boot_blah < 0 )
				kern_stat( "/boot", &boot_blah )
			
			static int container_for_boot
			if( container_for_boot < 0 && examination of boot )
				container_for_boot = currently examined inode
		}
		
		if( inode == container_for_boot )
			inode = boot_blah  // instead of hardcoding '0' !
	}
Plus, change all refs to DEV_ID_FOR_BOOT_VOLUME everywhere to rely on stat() or such
*/


#if defined(HAIKU_HOST_PLATFORM_FREEBSD)
#	define haiku_host_platform_read		haiku_freebsd_read
#	define haiku_host_platform_write	haiku_freebsd_write
#	define haiku_host_platform_readv	haiku_freebsd_readv
#	define haiku_host_platform_writev	haiku_freebsd_writev
#	define HAIKU_HOST_STAT_ATIM(x)		((x).st_atimespec)
#	define HAIKU_HOST_STAT_MTIM(x)		((x).st_mtimespec)
#elif defined(HAIKU_HOST_PLATFORM_DARWIN)
#	define haiku_host_platform_read		read
#	define haiku_host_platform_write	write
#	define haiku_host_platform_readv	readv
#	define haiku_host_platform_writev	writev
#	define HAIKU_HOST_STAT_ATIM(x)		((x).st_atimespec)
#	define HAIKU_HOST_STAT_MTIM(x)		((x).st_mtimespec)
#else
#	define haiku_host_platform_read		read
#	define haiku_host_platform_write	write
#	define haiku_host_platform_readv	readv
#	define haiku_host_platform_writev	writev
#	define HAIKU_HOST_STAT_ATIM(x)		((x).st_atim)
#	define HAIKU_HOST_STAT_MTIM(x)		((x).st_mtim)
#endif

#define RETURN_AND_SET_ERRNO(err)			\
	do {									\
		__typeof(err) __result = (err);		\
		if (__result < 0) {					\
			errno = __result;				\
			return -1;						\
		}									\
		return __result;					\
	} while (0)


#if defined(_HAIKU_BUILD_NO_FUTIMENS) || defined(_HAIKU_BUILD_NO_FUTIMENS)

template<typename File>
static int
utimes_helper(File& file, const struct timespec times[2])
{
	if (times == NULL)
		return file.SetTimes(NULL);

	timeval timeBuffer[2];
	timeBuffer[0].tv_sec = times[0].tv_sec;
	timeBuffer[0].tv_usec = times[0].tv_nsec / 1000;
	timeBuffer[1].tv_sec = times[1].tv_sec;
	timeBuffer[1].tv_usec = times[1].tv_nsec / 1000;

	if (times[0].tv_nsec == UTIME_OMIT || times[1].tv_nsec == UTIME_OMIT) {
		struct stat st;
		if (file.GetStat(st) != 0)
			return -1;

		if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT)
			return 0;

		if (times[0].tv_nsec == UTIME_OMIT) {
			timeBuffer[0].tv_sec = st.st_atimespec.tv_sec;
			timeBuffer[0].tv_usec = st.st_atimespec.tv_nsec / 1000;
		}

		if (times[1].tv_nsec == UTIME_OMIT) {
			timeBuffer[1].tv_sec = st.st_mtimespec.tv_sec;
			timeBuffer[1].tv_usec = st.st_mtimespec.tv_nsec / 1000;
		}
	}

	if (times[0].tv_nsec == UTIME_NOW || times[1].tv_nsec == UTIME_NOW) {
		timeval now;
		gettimeofday(&now, NULL);

		if (times[0].tv_nsec == UTIME_NOW)
			timeBuffer[0] = now;

		if (times[1].tv_nsec == UTIME_NOW)
			timeBuffer[1] = now;
	}

	return file.SetTimes(timeBuffer);	
}

#endif	// _HAIKU_BUILD_NO_FUTIMENS || _HAIKU_BUILD_NO_FUTIMENS


#ifdef _HAIKU_BUILD_NO_FUTIMENS

struct FDFile {
	FDFile(int fd)
		:
		fFD(fd)
	{
	}

	int GetStat(struct stat& _st)
	{
		return fstat(fFD, &_st);
	}

	int SetTimes(const timeval times[2])
	{
		return futimes(fFD, times);
	}

private:
	int fFD;
};


int
futimens(int fd, const struct timespec times[2])
{
	FDFile file(fd);
	return utimes_helper(file, times);
}

#endif	// _HAIKU_BUILD_NO_FUTIMENS

#ifdef _HAIKU_BUILD_NO_UTIMENSAT

struct FDPathFile {
	FDPathFile(int fd, const char* path, int flag)
		:
		fFD(fd),
		fPath(path),
		fFlag(flag)
	{
	}

	int GetStat(struct stat& _st)
	{
		return fstatat(fFD, fPath, &_st, fFlag);
	}

	int SetTimes(const timeval times[2])
	{
		// TODO: fFlag (AT_SYMLINK_NOFOLLOW) is not supported here!
		return futimesat(fFD, fPath, times);
	}

private:
	int			fFD;
	const char*	fPath;
	int			fFlag;
};


int
utimensat(int fd, const char* path, const struct timespec times[2], int flag)
{
	FDPathFile file(fd, path, flag);
	return utimes_helper(file, times);
}

#endif	// _HAIKU_BUILD_NO_UTIMENSAT


static status_t get_path(dev_t device, ino_t node, const char *name,
	string &path);


// find_dir_entry
static status_t
find_dir_entry(DIR *dir, const char *path, NodeRef ref, string &name,
	bool skipDot)
{
	TRACE();
	if( debug >= 2 )
		printf("      find_dir_entry  node <%lu> inside <%s> with dir %p  skipDot=%d\n", ref.node, path, dir, skipDot);
	
	// find the entry
	bool found = false;
#ifndef HoG_GENODE
	while (dirent *entry = readdir(dir)) {
#else
	char entry_buf[1024] = {};
	dirent *entry = NULL;
	while (0 == readdir_r(dir, (dirent*)entry_buf, &entry) && entry) {
#endif
		if ((strcmp(entry->d_name, ".") == 0 && skipDot)
			|| strcmp(entry->d_name, "..") == 0) {
			// skip "." and ".."
		} else /*if (entry->d_ino == ref.node)*/ {
				// Note: Linux doesn't seem to translate dirent::d_ino of
				// mount points. Thus we always have to lstat().
			// We also need to compare the device, which is generally not
			// included in the dirent structure. Hence we lstat().
			string entryPath(path);
			entryPath += '/';
			entryPath += entry->d_name;
			struct stat st;
			
			if( debug >= 5 )
				printf( "  .. trying <%s>\n", entryPath.c_str() );
			
///ToDo: here and below: lstat() (sans device fixup) called, and turned into a (bogus?) NodeRef, as here, fix them: (create a _kern_read_Lstat() ?)
//or in this one case here, just compare the inode but Not the Device, since we're just walking a dir on a known device, looking for an entry:
			if (lstat(entryPath.c_str(), &st) == 0) {
				if (NodeRef(st) == ref) {
					name = entry->d_name;
					found = true;
					
					if( debug >= 2 )
						printf("	 find_dir_entry: FOUND <%s>\n", entry->d_name);
					
					break;
				}
			}
		}
	}

	if (!found)
		return B_ENTRY_NOT_FOUND;

	return B_OK;
}

// find_dir_entry
static status_t
find_dir_entry(const char *path, NodeRef ref, string &name, bool skipDot)
{
	TRACE();
	if( debug >= 2 )
		printf("     find_dir_entry : opendir(<%s>)\n", path);
	
	// open dir
	DIR *dir = opendir(path);
	if (!dir)
		return errno;

	status_t error = find_dir_entry(dir, path, ref, name, skipDot);

	// close dir
	closedir(dir);

	return error;
}


#if 0
static bool
guess_normalized_dir_path(string path, NodeRef ref, string& _normalizedPath)
{
	TRACE();
	
	// We assume the CWD is normalized and hope that the directory is an
	// ancestor of it. We just chop off path components until we find a match or
	// hit root.
	char cwd[B_PATH_NAME_LENGTH];
	if (getcwd(cwd, sizeof(cwd)) == NULL)
		return false;

	while (cwd[0] == '/') {
		struct stat st;
		if (stat(cwd, &st) == 0) {
			if (st.st_dev == ref.device && st.st_ino == ref.node) {
				_normalizedPath = cwd;
				return true;
			}
		}

		*strrchr(cwd, '/') = '\0';
	}

	// TODO: If path is absolute, we could also try to work with that, though
	// the other way around -- trying prefixes until we hit a "." or ".."
	// component.

	return false;
}


// normalize_dir_path
static status_t
normalize_dir_path(string path, NodeRef ref, string &normalizedPath)
{
	TRACE();
	if( debug >= 2 )
		printf("   normalize_dir_path(<%s>) with NodeRef: %lu\n", path.c_str(), ref.node);
	
///ex 'fixme' (obsolete: let's just apply waddlesplash's OPTIM above)
// (obsolete): do we still need to optimize normalize_dir_path() ? Try again to run bfs without this
#if defined( HoG_GENODE )
	// OPTIM:
	// Take a shortcut when <path> is likely(?) already normalized.
	// Otherwise we take a beating, especially in qemu, where loading
	// CC then takes forever, looping on stat() and open().
	//
	if( NULL==strstr(path.c_str(), "/.")
		&& NULL==strstr(path.c_str(), "/..")
		&& NULL==strstr(path.c_str(), "//")  // be careful about double-slashes too, otherwise we pollute the registry with e.g. "//boot" and mess up node_ref handling
		&& path.c_str()[path.length() -1] != '/'
		)
	{
		normalizedPath = path;
		
		return 0;
	}
#endif

	// get parent path
	path += "/..";

	// stat the parent dir
	struct stat st;
	if (lstat(path.c_str(), &st) < 0)
		return errno;

	// root dir?
	NodeRef parentRef(st);
	if (parentRef == ref) {
		if( debug >= 2 )
			printf("      testing: <%s> is the root dir, so we normalize as '/' \n", path.c_str());
		
		normalizedPath = "/";
		return 0;
	}

	// find the entry
	string name;
	status_t error = find_dir_entry(path.c_str(), ref, name, true)				;
	if( debug >= 2 )
		printf("    normalize_dir_path -> find_dir_entry <%s> returns: <%s> & status: %d\n", path.c_str(), name.c_str(), error);
	if (error != B_OK) {
		if (error != B_ENTRY_NOT_FOUND) {
			// We couldn't open the directory. This might be because we don't
			// have read permission. We're OK with not fully normalizing the
			// path and try to guess the path in this case. Note: We don't check
			// error for B_PERMISSION_DENIED, since opendir() may clobber the
			// actual kernel error code with something not helpful.
			if (guess_normalized_dir_path(path, ref, normalizedPath))
				return B_OK;
		}

		if( debug >= 2 )
			printf("	*** find_dir_entry failed! error: %d\n", error);
		
		return error;
	}

	// recurse to get the parent dir path, if found
	error = normalize_dir_path(path, parentRef, normalizedPath);
	if (error != 0)
		return error;

	// construct the normalizedPath
	if (normalizedPath.length() > 1) // don't append "/", if parent is root
		normalizedPath += '/';
	normalizedPath += name;
	
	if( debug >= 2 )
		printf("     normalize_dir_path(<%s>) Returns => <%s>\n", path.c_str(), normalizedPath.c_str());

	return 0;
}

// normalize_dir_path
static status_t
normalize_dir_path(const char *path, string &normalizedPath)
{
	TRACE();
	if( debug >= 2 )
		printf("  normalize_dir_path(<%s>)\n", path);
	
	// stat() the dir
	struct stat st;
	if (stat(path, &st) < 0)
		return errno;

	return normalize_dir_path(path, NodeRef(st), normalizedPath);
}

#else
// back-ported this:
//		hrev55390 : libroot_build: Rework path normalization to not rely on system calls.
//		https://git.haiku-os.org/haiku/commit/?id=906fe09778cc57aa6e42931507a53b2eb91ee298
// (i.e. get rid of guess_normalized_dir_path(), get rid of 3-arg normalize_dir_path(), OPTIM 2-arg normalize_dir_path() to no longer use lstat() !)

// normalize_dir_path: Make path absolute and remove redundant entries.
static status_t
normalize_dir_path(const char *path, string &normalizedPath)
{
	const size_t pathLen = strlen(path);

	// Add CWD to relative paths.
	if (pathLen == 0 || path[0] != '/') {
		char pwd[PATH_MAX];
		if (getcwd(pwd, sizeof(pwd)) == NULL)
			return B_ERROR;

		normalizedPath = pwd;
	}

	const char *end = &path[pathLen];
	const char *next;
	for (const char *ptr = path; ptr < end; ptr = next + 1) {
		next = (char *)memchr(ptr, '/', end - ptr);
		if (next == NULL)
			next = end;

		size_t len = next - ptr;
		if (len == 2 && ptr[0] == '.' && ptr[1] == '.') {
			string::size_type pos = normalizedPath.rfind('/');
			if (pos != string::npos)
				normalizedPath.resize(pos);
			continue;
		} else if (len == 0 || (len == 1 && ptr[0] == '.')) {
			continue;
		}

		if (normalizedPath.length() != 1)
			normalizedPath += '/';

		normalizedPath.append(ptr, len);
	}

	if (normalizedPath.length() == 0)
		normalizedPath += '/';

	return B_OK;
}
#endif

// normalize_entry_path
static status_t
normalize_entry_path(const char *path, string &normalizedPath)
{
	TRACE();
	if( debug )
		printf("  normalize_ENTRY_path(<%s>)\n", path);
	
	const char *dirPath = NULL;
	const char *leafName = NULL;

	string dirPathString;
	if (const char *lastSlash = strrchr(path, '/')) {
		// found a slash: decompose into dir path and leaf name
		leafName = lastSlash + 1;
		if (leafName[0] == '\0') {
			// slash is at the end: the whole path is a dir name
			leafName = NULL;
		} else {
			dirPathString = string(path, leafName - path);
			dirPath = dirPathString.c_str();
		}

	} else {
		// path contains no slash, so it is a path relative to the current dir
		dirPath = ".";
		leafName = path;
	}

	// catch special case: no leaf, or leaf is a directory
	if (!leafName || strcmp(leafName, ".") == 0 || strcmp(leafName, "..") == 0)
		return normalize_dir_path(path, normalizedPath);
	
	if( debug >= 3 )
		puts( "  normalize_entry_path -> norm-dir-path" );
	
	// normalize the dir path
	status_t error = normalize_dir_path(dirPath, normalizedPath);
	if (error != B_OK)
		return error;

	// append the leaf name
	if (normalizedPath.length() > 1) // don't append "/", if parent is root
		normalizedPath += '/';
	normalizedPath += leafName;

	if( debug >= 3 )
		printf( "  normalize_entry_path returning <%s>\n", normalizedPath.c_str() );
	
	return B_OK;
}


// #pragma mark -

typedef map<NodeRef, string> DirPathMap;
static DirPathMap sDirPathMap;

#if defined( HoG_GENODE )
#include <base/mutex.h>
static Genode::Mutex sdirmap_Lock;
#define MTSAFE_GUARD  Genode::Mutex::Guard lock_guard( sdirmap_Lock );
#endif  // ~HoG_GENODE

// get_path
static status_t
get_path(const NodeRef *ref, const char *name, string &path)
{
///seems BQuery will need work here (for BVolume and such)
	TRACE();
	if( debug )
	{
		if( ref ) printf("    getpath by-NodeRef (ref case): (%lu, %ld) <%s>\n", ref->device, ref->node, name);
		else      printf("    getpath by-NodeRef (name case): <%s>\n", name);
	}

	if (!ref && !name)
		return B_BAD_VALUE;

	// no ref or absolute path
	if (!ref || (name && name[0] == '/')) {
		path = name;
		return B_OK;
	}

	// get the dir path
	if (ref) {
		if( debug >= 2 )
			printf("    retrieve NodeRef -Directory- (%ld, %ld) from dirmap...\n", ref->device, ref->node);
		
		MTSAFE_GUARD;
		
		DirPathMap::iterator it = sDirPathMap.find(*ref);
		if (it == sDirPathMap.end())
		{
			if( debug >= 4 )
				printf("   cannot find ref (%ld, %ld) in sDirPathMap !\n", ref->device, ref->node);
			
			return B_ENTRY_NOT_FOUND;
		}

		path = it->second;

		// stat the path to check, if it is still valid
		struct stat st;
		if (stat(path.c_str(), &st) < 0) {  // HoG_GENODE: backport May 2022's hrev56121 "libroot_build: fix stat of files in a symlinked directory" (ticket #17750) -> use stat() instead of lstat()
			HoG_GENODE_TRACE( printf("*** lstat shows <%s> has disappeared, clearing it !\n", path.c_str()) );
			
			sDirPathMap.erase(it);
			return errno;
		}

#if defined( HoG_GENODE )
		Fixup_Stat( path.c_str(), &st );
#endif
		// compare the NodeRef
		if (NodeRef(st) != *ref) {
			HoG_GENODE_TRACE( printf("*** noderef comparison fails, clearing it ! (%ld, %ld) vs (%ld, %ld)\n", st.st_dev, st.st_ino, ref->device, ref->node) );
			
			sDirPathMap.erase(it);
			return B_ENTRY_NOT_FOUND;
		}

		// still a directory?
		if (!S_ISDIR(st.st_mode)) {
			HoG_GENODE_TRACE( printf("*** <%s> is no longer a dir, clearing it !\n", path.c_str()) );
			
			sDirPathMap.erase(it);
			return B_NOT_A_DIRECTORY;
		}
	}

	// if there's a name, append it
	if (name) {
#ifndef HoG_GENODE
		path += '/';
#else  // HoG:
		// make sure BPath.SetTo( BEntry* ) does not result in e.g.: "path.Path() : <//boot>"
		if( path.length() > 0 && path.c_str()[path.length() -1] != '/' )
			path += '/';
#endif  // ~HoG

		path += name;
	}

	if( debug >= 2 )
		puts("    -> getpath success!");
	
	return B_OK;
}

// get_path
status_t
BPrivate::get_path(int fd, const char *name, string &path)
{
	TRACE();
	if( debug )
		printf("   getpath fd %d <%s>\n", fd, name);
	
	// get the node ref for the fd, if any, and the path part is not absolute
	if (fd >= 0 && !(name && name[0] == '/')) {
		// get descriptor
		Descriptor *descriptor = get_descriptor(fd);
		if (!descriptor)
			return B_FILE_ERROR;

		// Handle symlink descriptors here explicitly, so this function can be
		// used more flexibly.
		if (SymlinkDescriptor* symlinkDescriptor
				= dynamic_cast<SymlinkDescriptor*>(descriptor)) {
			path = symlinkDescriptor->path;
			if (name == NULL)
				return B_OK;
			path += '/';
			path += name;
			return B_OK;
		}

		// get node ref for the descriptor
		NodeRef ref;
		status_t error = descriptor->GetNodeRef(ref);
		if (error != B_OK)
			return error;

		return ::get_path(&ref, name, path);

	} else	// no descriptor or absolute path
		return ::get_path((NodeRef*)NULL, name, path);
}

// get_path
static status_t
get_path(dev_t device, ino_t directory, const char *name, string &path)
{
	TRACE();
	if( debug >= 2 )
		printf("  get_path on Device %ld, dir-node %lu name <%s>\n", device, directory, name);
	
	NodeRef ref;
	ref.device = device;
	ref.node = directory;

	return get_path(&ref, name, path);
}

// add_dir_path
static void
add_dir_path(const char *path, const NodeRef &ref)
{
	TRACE();
	if( debug )
		printf("   >> add_dir_path <%s>......(inode: %lu) .........\n", path, ref.node);
		//printf("  >>>>  add_dir_path : register a (normalized) map:: entry for <%s> ref: dev %ld node %lu\n", path, ref.device, ref.node);
	
	MTSAFE_GUARD;
	
	// add the normalized path
	string normalizedPath;
	if (normalize_dir_path(path, normalizedPath) == B_OK)
	{
		HoG_GENODE_TRACE( printf("    >>>  mapIno[%s] = (%ld, %lu) dev/ino .......\n", normalizedPath.c_str(), ref.device, ref.node) );
		sDirPathMap[ref] = normalizedPath;
	}
}


// #pragma mark -

// _kern_entry_ref_to_path
status_t
_kern_entry_ref_to_path(dev_t device, ino_t node, const char *leaf,
	char *userPath, size_t pathLength)
{
	TRACE();
	if( debug )
		printf("  _kern_entry_ref_to_path (%ld, %ld) leaf <%s>\n", device, node, leaf);
	
	// get the path
	string path;
	status_t error = get_path(device, node, leaf, path);
	if (error != B_OK)
		return error;

	// copy it back to the user buffer
	if (strlcpy(userPath, path.c_str(), pathLength) >= pathLength)
		return B_BUFFER_OVERFLOW;

	return B_OK;
}


// #pragma mark -

// _kern_create_dir
status_t
_kern_create_dir(int fd, const char *path, int perms)
{
	TRACE();
	PDBG( "path <", path, "> fd: ", fd, " perms: ", perms );
	
	// get a usable path
	string realPath;
	status_t error = get_path(fd, path, realPath);

	if( debug )
		printf("  kcreatedir(): get_path -> <%s> -> err %d\n", realPath.c_str(), error);
	
	if (error != B_OK)
		return error;

	// mkdir
	if (mkdir(realPath.c_str(), perms) < 0)
#ifndef HoG_GENODE
		return errno;
#else
	{
		FixUp_Errno();
		return errno;
	}
#endif

	return B_OK;
}

// _kern_create_dir_entry_ref
status_t
_kern_create_dir_entry_ref(dev_t device, ino_t node, const char *name,
	int perms)
{
	TRACE();
	PDBG( "dev ", device, " inode ", node, " name <", name, "> perms ", perms );
	
	// get a usable path
	string realPath;
	status_t error = get_path(device, node, name, realPath);
	if (error != B_OK)
		return error;

	// mkdir
	if (mkdir(realPath.c_str(), perms) < 0)
		return errno;

	return B_OK;
}

// open_dir
static int
open_dir(const char *path)
{
//BString s; s << "opendir: " << path;
//BStopWatch sw( s,   false   );//"opendir" );
	TRACE();
	if( debug )
		printf("    open_dir <%s>\n", path);
	
	// open the dir
	if( debug >= 2 )
		printf("    open_dir: OpenDir..\n");
	DIR *dir = opendir(path);
//sw.Lap();
	if (!dir)
		return errno;
	
	//HoG_GENODE_TRACE( printf("    open_dir: stat()..\n") );
	
	// stat the entry
	struct stat st;
	if (stat(path, &st) < 0) {
		closedir(dir);
		return errno;
	}
//sw.Lap();
	
	//HoG_GENODE_TRACE( printf("    open_dir: stat() ok, checking for /boot workaround...\n") );
#if defined( HoG_GENODE )
	Fixup_Stat( path, &st );
#endif

	if (!S_ISDIR(st.st_mode)) {
		closedir(dir);
		return B_NOT_A_DIRECTORY;
	}

	// cache dir path
///ToDo: review: we sometime get this : """map[</boot/..>] = (0, 1)""" -> that is not "normalized" anything ! And that registers the (functionally) "/" path as (0,1) instead of (285176240, 1) !
	if( debug >= 2 )
		printf("    open_dir: Node Ref + Register map[<%s>] = (%ld, %ld)\n", path, st.st_dev, st.st_ino);
	NodeRef ref(st);
	add_dir_path(path, ref);

	// create descriptor
	DirectoryDescriptor *descriptor = new DirectoryDescriptor(dir, ref);
//sw.Lap();
	if( debug >= 2 )
		printf("    open_dir <%s> DONE, returning descriptor %p\n", path, descriptor);
	
	return add_descriptor(descriptor);
}

// _kern_open_dir
int
_kern_open_dir(int fd, const char *path)
{
	TRACE();
	PDBG( "path: ", path );
	if( debug )
		printf("  _Kern_open_dir (fd <%d> path <%s>)\n", fd, path);
	
	// get a usable path
	string realPath;
	status_t error = get_path(fd, path, realPath);
	if (error != B_OK)
		return error;

	if( debug >= 2 )
		puts("  > open_dir");
	
	return open_dir(realPath.c_str());
}

// _kern_open_dir_entry_ref
int
_kern_open_dir_entry_ref(dev_t device, ino_t node, const char *name)
{
//	BStopWatch sw( "k_opendir_entryref",       true   );//, debug );
	TRACE();
	PDBG( device, ":", node, "  ref.name: ", name );
	
	if( debug )
		printf("  _Kern_open_dir (Entry-Ref: dev <%lu> inode <%ld> name <%s>)\n", device, node, name);
	
	// get a usable path
	string realPath;
//sw.Lap();
	status_t error = get_path(device, node, name, realPath);
	if (error != B_OK)
		return error;

#if 0
	return open_dir(realPath.c_str());
#else
//sw.Lap();
	auto a = open_dir(realPath.c_str());
//sw.Lap();
	return a;
#endif
}

// _kern_open_parent_dir
int
_kern_open_parent_dir(int fd, char *name, size_t nameLength)
{
	TRACE();
	if( debug )
		printf("  _Kern_open_Parent-dir (fd <%d> name <%s>)\n", fd, name);
	
	// get a usable path
	string realPath;
	status_t error = get_path(fd, NULL, realPath);
	if (error != B_OK)
		return error;

	// stat the entry
	struct stat st;
	if (stat(realPath.c_str(), &st) < 0)
		return errno;

	if (!S_ISDIR(st.st_mode))
		return B_NOT_A_DIRECTORY;

	// get the entry name
	if( debug >= 2 )
		printf("    _kern_open_parent_dir -> append /.. ! **\n");

	realPath += "/..";
	string entryName;

	error = find_dir_entry(realPath.c_str(), NodeRef(st),
		entryName, false);
	
	if( debug >= 2 )
		printf("    _kern_open_parent_dir -> find_dir_entry <%s> <%s> -> Result: %d\n", realPath.c_str(), entryName.c_str(), error);
	
	if (error != B_OK)
		return error;

	if (strlcpy(name, entryName.c_str(), nameLength) >= nameLength)
		return B_BUFFER_OVERFLOW;

	// open the parent directory

	return open_dir(realPath.c_str());
}

// _kern_read_dir
ssize_t
_kern_read_dir(int fd, struct dirent *buffer, size_t bufferSize,
	uint32 maxCount)
{
	TRACE();
	PDBG( "fd: ", fd );
	if( debug )
		printf("  _Kern_read_dir(fd <%d>)\n", fd);
	
	if (maxCount <= 0)
		return B_BAD_VALUE;

	// get the descriptor
	DirectoryDescriptor *descriptor
		= dynamic_cast<DirectoryDescriptor*>(get_descriptor(fd));
	if (!descriptor)
		return B_FILE_ERROR;

	// get the next entry
#if defined( HoG_GENODE )
	char entry_buf[1024] = {};
#endif
	dirent *entry;
	errno = 0;
	if (dynamic_cast<AttrDirDescriptor*>(descriptor))
		entry = fs_read_attr_dir(descriptor->dir);
	else
	{
#ifndef HoG_GENODE
		entry = readdir(descriptor->dir);
#else
		// use readdir_r() (re-entrant) for MT safety:
		int res = readdir_r( descriptor->dir, (dirent*)entry_buf, &entry );
		//if( 0 == res )
		//	entry = (dirent*)entry_buf;
#endif
	}
	
	if (!entry)
		return errno;

	// copy the entry
	int entryLen = &entry->d_name[strlen(entry->d_name) + 1] - (char*)entry;
	if (entryLen > (int)bufferSize)
		return B_BUFFER_OVERFLOW;

	memcpy(buffer, entry, entryLen);
	if( debug >= 2 )
		printf("  ..kernreaddir: found entry <%s>\n", entry->d_name);

	return 1;
}

// _kern_rewind_dir
status_t
_kern_rewind_dir(int fd)
{
	TRACE();
	
	// get the descriptor
	DirectoryDescriptor *descriptor
		= dynamic_cast<DirectoryDescriptor*>(get_descriptor(fd));
	if (!descriptor)
		return B_FILE_ERROR;

	// rewind
	if (dynamic_cast<AttrDirDescriptor*>(descriptor))
		fs_rewind_attr_dir(descriptor->dir);
	else
		rewinddir(descriptor->dir);

	return B_OK;
}


// #pragma mark -

// open_file
static int
open_file(const char *path, int openMode, int perms)
{
	TRACE();
	if( debug )
		printf("  open_file <%s>  mode=0x%x  perms=%d\n", path, openMode, perms);
	
	// stat the node
	bool exists = true;
	struct stat st;
	if( debug >= 2 )
		puts("  ..exists?");
	if (lstat(path, &st) < 0) {
		if( debug >= 2 )
			printf("  ..nope! and errno is <%d>\n", errno);

		exists = false;
		if (!(openMode & O_CREAT))
		{
			FixUp_Errno();
			return errno;
		}
		
		if( debug >= 2 )
			puts("  file needs to be created..");
	}
#if defined( HoG_GENODE )
	Fixup_Stat( path, &st );
#endif

	Descriptor *descriptor;
	if (exists && S_ISLNK(st.st_mode) && (openMode & O_NOTRAVERSE) != 0) {
		if( debug >= 2 )
			puts( "  this is a SymLink, and we won't traverse..." );
		
		// a symlink not to be followed: create a special descriptor
		// normalize path first
		string normalizedPath;
		status_t error = normalize_entry_path(path, normalizedPath);
		if (error != B_OK)
		{
			printf( "  open_file() ERROR : %d\n", error );///
			return error;
		}

		if( debug >= 2 )
			puts( "  create SymLink descriptor\n" );
		
		descriptor = new SymlinkDescriptor(normalizedPath.c_str());
	} else {
		// open the file
		openMode &= ~O_NOTRAVERSE;
		int newFD = open(path, openMode, perms);
		if( debug )
			printf("   open() <%s> 0x%x <%d> , returned fd=%d\n", path, openMode, perms, newFD);

		if (newFD < 0)
		{
			FixUp_Errno();
			return errno;
		}

		descriptor = new FileDescriptor(newFD);
	}

	// cache path, if this is a directory
	if (exists && S_ISDIR(st.st_mode))
		add_dir_path(path, NodeRef(st));
	
	if( debug >= 2 )
		puts( "  ..add descriptor for node" );
	
	return add_descriptor(descriptor);
}

// _kern_open
int
_kern_open(int fd, const char *path, int openMode, int perms)
{
	TRACE();
	PDBG( "path: ", path );
	if( debug )
		printf("  _Kern_open (fd %d <%s>)\n", fd, path);
	
	// get a usable path
	string realPath;
	status_t error = get_path(fd, path, realPath);

	if( debug >= 2 )
		printf("  _kern_open(): get_path -> err %d yields <%s>\n", error, realPath.c_str());

	if (error != B_OK)
		return error;

	if( debug >= 3 )
		printf("  .. call open_file with <%s>\n", realPath.c_str());

	return open_file(realPath.c_str(), openMode, perms);
}

// _kern_open_entry_ref
int
_kern_open_entry_ref(dev_t device, ino_t node, const char *name,
	int openMode, int perms)
{
	TRACE();
	PDBG( "name: ", name );
	if( debug )
		printf("  _Kern_open_Entry-Ref (dev <%lu> inode <%ld> name <%s>)\n", device, node, name);
	
	// get a usable path
	string realPath;
	status_t error = get_path(device, node, name, realPath);
	if (error != B_OK)
		return error;

	return open_file(realPath.c_str(), openMode, perms);
}

// _kern_seek
off_t
_kern_seek(int fd, off_t pos, int seekType)
{
	TRACE();
	
	// get the descriptor
	FileDescriptor *descriptor
		= dynamic_cast<FileDescriptor*>(get_descriptor(fd));
	if (!descriptor)
		return B_FILE_ERROR;

	// seek
	off_t result = lseek(descriptor->fd, pos, seekType);
	if (result < 0)
		return errno;

	return result;
}

// _kern_read
ssize_t
_kern_read(int fd, off_t pos, void *buffer, size_t bufferSize)
{
	TRACE();
	
	// get the descriptor
	FileDescriptor *descriptor
		= dynamic_cast<FileDescriptor*>(get_descriptor(fd));
	if (!descriptor)
		return B_FILE_ERROR;

	// seek
	if (pos != -1) {
		off_t result = lseek(descriptor->fd, pos, SEEK_SET);
		if (result < 0)
			return errno;
	}

	// read
	ssize_t bytesRead = haiku_host_platform_read(descriptor->fd, buffer,
		bufferSize);
	if (bytesRead < 0)
		return errno;

	return bytesRead;
}

// _kern_write
ssize_t
_kern_write(int fd, off_t pos, const void *buffer, size_t bufferSize)
{
	TRACE();
	
	// get the descriptor
	FileDescriptor *descriptor
		= dynamic_cast<FileDescriptor*>(get_descriptor(fd));
	if (!descriptor)
		return B_FILE_ERROR;

	// seek
	if (pos != -1) {
		off_t result = lseek(descriptor->fd, pos, SEEK_SET);
		if (result < 0)
			return errno;
	}

	// read
	ssize_t bytesWritten = haiku_host_platform_write(descriptor->fd, buffer,
		bufferSize);
	if (bytesWritten < 0)
		return errno;

	return bytesWritten;
}

// _kern_close
status_t
_kern_close(int fd)
{
	TRACE();
	
	return delete_descriptor(fd);
}

// _kern_dup
int
_kern_dup(int fd)
{
	TRACE();
	
	// get the descriptor
	Descriptor *descriptor = get_descriptor(fd);
	if (!descriptor)
		return B_FILE_ERROR;

	// clone it
	Descriptor *clone;
	status_t error = descriptor->Dup(clone);
	if (error != B_OK)
		return error;

	return add_descriptor(clone);
}

// _kern_fsync
status_t
_kern_fsync(int fd)
{
	TRACE();
	
	// get the descriptor
	FileDescriptor *descriptor
		= dynamic_cast<FileDescriptor*>(get_descriptor(fd));
	if (!descriptor)
		return B_FILE_ERROR;

	// sync
	if (fsync(descriptor->fd) < 0)
		return errno;

	return B_OK;
}

// _kern_read_stat
status_t
_kern_read_stat(int fd, const char *path, bool traverseLink,
	struct stat *st, size_t statSize)
{
	TRACE();
	PDBG( "path <", path, "> fd: ", fd );
	if( debug )
		printf("  _kern_read_stat fd <%d> path <%s>   traverse=%s\n", fd, path, traverseLink?"True":"false");
	
	if (path) {
		// get a usable path
		string realPath;
		status_t error = get_path(fd, path, realPath);
		if (error != B_OK)
			return error;

		// stat
		int result;
		if (traverseLink)
			result = stat(realPath.c_str(), st);
		else
			result = lstat(realPath.c_str(), st);
#if defined( HoG_GENODE )
		Fixup_Stat( realPath.c_str(), st );
#endif

		if (result < 0)
			return errno;
	} else {
		Descriptor *descriptor = get_descriptor(fd);
		if (!descriptor)
			return B_FILE_ERROR;

#ifndef HoG_GENODE
		return descriptor->GetStat(traverseLink, st);
#else
		auto ss = descriptor->GetStat(traverseLink, st);
		// it seems the above is the *only* place where a descriptor's GetStat()
		// hook is ever called, so no need to fix all of fs_descriptors.cpp, just
		// call Fixup_Stat() once below:
		
		/*
		std::string foo;
		descriptor->GetPath( foo );
		if( 0==memcmp(foo.c_str(), "/boo", 4) )
			Genode::warning("should we fixup or not?  dev <", st->st_dev, "> <", foo.c_str(), "> is-sys-fd: ", descriptor->IsSystemFD() );
		*/
		
		///xxx Sometimes, like below, we do not have the file path (because we work on an FD), so we
		// call Fixup_stat() with a NULL path, hoping it has already "seen" the corrective device value ;
		// this leaves open the vulnerability/possibility of hitting the "no name, we pass NULL" case BEFORE we hit the "/boot is at so and so" ?
		Fixup_Stat( NULL/**/, st );
		
		return ss;
#endif
	}

	return B_OK;
}

// _kern_write_stat
status_t
_kern_write_stat(int fd, const char *path, bool traverseLink,
	const struct stat *st, size_t statSize, int statMask)
{
	TRACE();
	
	// get a usable path
	int realFD = -1;
	string realPath;
	status_t error;
	bool isSymlink = false;
	if (path) {
		error = get_path(fd, path, realPath);
		if (error != B_OK)
			return error;

		// stat it to see, if it is a symlink
		struct stat tmpStat;
		if (lstat(realPath.c_str(), &tmpStat) < 0)
			return errno;

		isSymlink = S_ISLNK(tmpStat.st_mode);

	} else {
		Descriptor *descriptor = get_descriptor(fd);
		if (!descriptor)
			return B_FILE_ERROR;

		if (FileDescriptor *fileFD
				= dynamic_cast<FileDescriptor*>(descriptor)) {
			realFD = fileFD->fd;

		} else if (dynamic_cast<DirectoryDescriptor*>(descriptor)) {
			error = get_path(fd, NULL, realPath);
			if (error != B_OK)
				return error;

		} else if (SymlinkDescriptor *linkFD
				= dynamic_cast<SymlinkDescriptor*>(descriptor)) {
			realPath = linkFD->path;
			isSymlink = true;

		} else
			return B_FILE_ERROR;
	}

	// We're screwed, if the node to manipulate is a symlink. All the
	// available functions traverse symlinks.
	if (isSymlink && !traverseLink)
		return B_ERROR;

	if (realFD >= 0) {
		if (statMask & B_STAT_MODE) {
			if (fchmod(realFD, st->st_mode) < 0)
				return errno;
		}

		if (statMask & B_STAT_UID) {
			if (fchown(realFD, st->st_uid, (gid_t)-1) < 0)
				return errno;
		}

		if (statMask & B_STAT_GID) {
			if (fchown(realFD, (uid_t)-1, st->st_gid) < 0)
				return errno;
		}

		if (statMask & B_STAT_SIZE) {
			if (ftruncate(realFD, st->st_size) < 0)
				return errno;
		}

		// The timestamps can only be set via utime(), but that requires a
		// path we don't have.
		if (statMask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME
				| B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME)) {
			return B_ERROR;
		}

		return 0;

	} else {
		if (statMask & B_STAT_MODE) {
			if (chmod(realPath.c_str(), st->st_mode) < 0)
				return errno;
		}

		if (statMask & B_STAT_UID) {
			if (chown(realPath.c_str(), st->st_uid, (gid_t)-1) < 0)
				return errno;
		}

		if (statMask & B_STAT_GID) {
			if (chown(realPath.c_str(), (uid_t)-1, st->st_gid) < 0)
				return errno;
		}

		if (statMask & B_STAT_SIZE) {
			if (truncate(realPath.c_str(), st->st_size) < 0)
				return errno;
		}

		if (statMask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) {
			// Grab the previous mod and access times so we only overwrite
			// the specified time and not both
			struct stat oldStat;
			if (~statMask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) {
				if (stat(realPath.c_str(), &oldStat) < 0)
					return errno;
			}

			utimbuf buffer;
			buffer.actime = (statMask & B_STAT_ACCESS_TIME) ? st->st_atime : oldStat.st_atime;
			buffer.modtime = (statMask & B_STAT_MODIFICATION_TIME) ? st->st_mtime : oldStat.st_mtime;
			if (utime(realPath.c_str(), &buffer) < 0)
				return errno;
		}

		// not supported
		if (statMask & (B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME))
			return B_ERROR;
	}

	return B_OK;
}


// #pragma mark -

// _kern_create_symlink
status_t
_kern_create_symlink(int fd, const char *path, const char *toPath, int mode)
{
	TRACE();
	if( debug )
		printf(" _kern_create_symlink <%s> <%s>\n", path, toPath);
	
	// Note: path must not be NULL, so this will always work.
	// get a usable path
	string realPath;
	status_t error = get_path(fd, path, realPath);
	if (error != B_OK)
		return error;

	if( debug >= 2 )
		printf("  kern:symlink <%s> <%s>\n", toPath, realPath.c_str());
	
	// symlink
	if (symlink(toPath, realPath.c_str()) < 0)
	{
		FixUp_Errno();
		//printf("libc:symlink() FAILED, errno 0x%x\n", errno);
///if symlink already exists, we get:  "Error: don't know how to fix errno <63>, leaving as-is"
		return errno;
	}

	return B_OK;
}

// _kern_read_link
status_t
_kern_read_link(int fd, const char *path, char *buffer, size_t *_bufferSize)
{
	TRACE();
	if( debug )
		printf(" _kern_read_link fd = %d or <%s>\n", fd, path);
	
	// get the path
	string realPath;
	status_t error = get_path(fd, path, realPath);
	if (error != B_OK)
		return error;

	// readlink
	ssize_t bytesRead = readlink(realPath.c_str(), buffer, *_bufferSize);
	if (bytesRead < 0)
	{
		FixUp_Errno();
		return errno;
	}

	if (*_bufferSize > 0) {
///note: Harmless(?) bug: on overflow, this if() will trigger, which truncates the value/length, which 'sabotages' overflow detection in BSymlink::ReadLink() (it then fails to return B_BUFFER_OVERFLOW)... But seems all users of read_link have a big buffer (B_PATH_NAME_LENGTH aka 1024 bytes), and nobody in the code watches for B_BUFFER_OVERFLOW anyway.
		if ((size_t)bytesRead == *_bufferSize)
			bytesRead--;

		buffer[bytesRead] = '\0';
	}

	*_bufferSize = bytesRead;

	return B_OK;
}

// _kern_unlink
status_t
_kern_unlink(int fd, const char *path)
{
	TRACE();
	
	// get a usable path
	string realPath;
	status_t error = get_path(fd, path, realPath);
	if (error != B_OK)
		return error;

	// unlink
	if (unlink(realPath.c_str()) < 0)
		return errno;

	return B_OK;
}

// _kern_rename
status_t
_kern_rename(int oldDir, const char *oldPath, int newDir, const char *newPath)
{
	TRACE();
	
	// get usable paths
	string realOldPath;
	status_t error = get_path(oldDir, oldPath, realOldPath);
	if (error != B_OK)
		return error;

	string realNewPath;
	error = get_path(newDir, newPath, realNewPath);
	if (error != B_OK)
		return error;

	// rename
	if (rename(realOldPath.c_str(), realNewPath.c_str()) < 0)
		return errno;

	return B_OK;
}


// #pragma mark -

// _kern_lock_node
status_t
_kern_lock_node(int fd)
{
	TRACE();
	
	return B_ERROR;
}

// _kern_unlock_node
status_t
_kern_unlock_node(int fd)
{
	TRACE();
	
	return B_ERROR;
}


// #pragma mark -

// read_pos
ssize_t
read_pos(int fd, off_t pos, void *buffer, size_t bufferSize)
{
	TRACE();
	
	// seek
	off_t result = lseek(fd, pos, SEEK_SET);
	if (result < 0)
		return errno;

	// read
	ssize_t bytesRead = haiku_host_platform_read(fd, buffer, bufferSize);
	if (bytesRead < 0) {
		errno = bytesRead;
		return -1;
	}

	return bytesRead;
}

// write_pos
ssize_t
write_pos(int fd, off_t pos, const void *buffer, size_t bufferSize)
{
	TRACE();
	
	// If this is an attribute descriptor, let it do the job.
	AttributeDescriptor* descriptor
		= dynamic_cast<AttributeDescriptor*>(get_descriptor(fd));
	if (descriptor != NULL) {
		status_t error = descriptor->Write(pos, buffer, bufferSize);
		if (error != B_OK) {
			errno = error;
			return -1;
		}

		return bufferSize;
	}

	// seek
	off_t result = lseek(fd, pos, SEEK_SET);
	if (result < 0)
		return errno;

	// write
	ssize_t bytesWritten = haiku_host_platform_write(fd, buffer, bufferSize);
	if (bytesWritten < 0) {
		errno = bytesWritten;
		return -1;
	}

	return bytesWritten;
}

// readv_pos
ssize_t
readv_pos(int fd, off_t pos, const struct iovec *vec, size_t count)
{
	TRACE();
	
	// seek
	off_t result = lseek(fd, pos, SEEK_SET);
	if (result < 0)
		return errno;

	// read
	ssize_t bytesRead = haiku_host_platform_readv(fd, vec, count);
	if (bytesRead < 0) {
		errno = bytesRead;
		return -1;
	}

	return bytesRead;
}


// writev_pos
ssize_t
writev_pos(int fd, off_t pos, const struct iovec *vec, size_t count)
{
	TRACE();
	
	// seek
	off_t result = lseek(fd, pos, SEEK_SET);
	if (result < 0)
		return errno;

	// read
	ssize_t bytesWritten = haiku_host_platform_writev(fd, vec, count);
	if (bytesWritten < 0) {
		errno = bytesWritten;
		return -1;
	}

	return bytesWritten;
}


// #pragma mark -


#if 0  // HoG_GENODE: disable those, not supported by Genode's libc it seems
int
_haiku_build_fchmod(int fd, mode_t mode)
{
	return _haiku_build_fchmodat(fd, NULL, mode, AT_SYMLINK_NOFOLLOW);
}


int
_haiku_build_fchmodat(int fd, const char* path, mode_t mode, int flag)
{
	if (fd >= 0 && fd != AT_FDCWD && get_descriptor(fd) == NULL)
		return fchmodat(fd, path, mode, flag);

	struct stat st;
	st.st_mode = mode;

	RETURN_AND_SET_ERRNO(_kern_write_stat(fd, path,
		(flag & AT_SYMLINK_NOFOLLOW) == 0, &st, sizeof(st), B_STAT_MODE));
}


int
_haiku_build_fstat(int fd, struct stat* st)
{
	return _haiku_build_fstatat(fd, NULL, st, AT_SYMLINK_NOFOLLOW);
}


int
_haiku_build_fstatat(int fd, const char* path, struct stat* st, int flag)
{
	if (fd >= 0 && fd != AT_FDCWD && get_descriptor(fd) == NULL)
		return fstatat(fd, path, st, flag);

	RETURN_AND_SET_ERRNO(_kern_read_stat(fd, path,
		(flag & AT_SYMLINK_NOFOLLOW) == 0, st, sizeof(*st)));
}


int
_haiku_build_mkdirat(int fd, const char* path, mode_t mode)
{
	if (fd >= 0 && fd != AT_FDCWD && get_descriptor(fd) == NULL)
		return mkdirat(fd, path, mode);

	RETURN_AND_SET_ERRNO(_kern_create_dir(fd, path, mode));
}


int
_haiku_build_mkfifoat(int fd, const char* path, mode_t mode)
{
	return mkfifoat(fd, path, mode);

	// TODO: Handle non-system FDs.
}
#endif


// HoG_GENODE: seems Genode does not support these, rem out:

#if 0
int
_haiku_build_utimensat(int fd, const char* path, const struct timespec times[2],
	int flag)
{
	if (fd >= 0 && fd != AT_FDCWD && get_descriptor(fd) == NULL)
		return utimensat(fd, path, times, flag);

	struct stat stat;
	status_t status;
	uint32 mask = 0;

	// Init the stat time fields to the current time, if at least one time is
	// supposed to be set to it.
	if (times == NULL || times[0].tv_nsec == UTIME_NOW
		|| times[1].tv_nsec == UTIME_NOW) {
		timeval now;
		gettimeofday(&now, NULL);
		HAIKU_HOST_STAT_ATIM(stat).tv_sec
			= HAIKU_HOST_STAT_MTIM(stat).tv_sec = now.tv_sec;
		HAIKU_HOST_STAT_ATIM(stat).tv_nsec
			= HAIKU_HOST_STAT_MTIM(stat).tv_nsec = now.tv_usec * 1000;
	}

	if (times != NULL) {
		// access time
		if (times[0].tv_nsec != UTIME_OMIT) {
			mask |= B_STAT_ACCESS_TIME;

			if (times[0].tv_nsec != UTIME_NOW) {
				if (times[0].tv_nsec < 0 || times[0].tv_nsec > 999999999)
					RETURN_AND_SET_ERRNO(EINVAL);
			}

			HAIKU_HOST_STAT_ATIM(stat) = times[0];
		}

		// modified time
		if (times[1].tv_nsec != UTIME_OMIT) {
			mask |= B_STAT_MODIFICATION_TIME;

			if (times[1].tv_nsec != UTIME_NOW) {
				if (times[1].tv_nsec < 0 || times[1].tv_nsec > 999999999)
					RETURN_AND_SET_ERRNO(EINVAL);
			}

			HAIKU_HOST_STAT_MTIM(stat) = times[1];
		}
	} else
		mask |= B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME;

	// set the times -- as per spec we even need to do this, if both have
	// UTIME_OMIT set
	status = _kern_write_stat(fd, path, (flag & AT_SYMLINK_NOFOLLOW) == 0,
		&stat, sizeof(struct stat), mask);

	RETURN_AND_SET_ERRNO(status);
}


int
_haiku_build_futimens(int fd, const struct timespec times[2])
{
	return _haiku_build_utimensat(fd, NULL, times, AT_SYMLINK_NOFOLLOW);
}


int
_haiku_build_faccessat(int fd, const char* path, int accessMode, int flag)
{
	if (fd >= 0 && fd != AT_FDCWD && get_descriptor(fd) == NULL)
		return faccessat(fd, path, accessMode, flag);

	// stat the file
	struct stat st;
	status_t error = _kern_read_stat(fd, path, false, &st, sizeof(st));
	if (error != B_OK)
		RETURN_AND_SET_ERRNO(error);

	// get the current user
	uid_t uid = (flag & AT_EACCESS) != 0 ? geteuid() : getuid();

	int fileMode = 0;

	if (uid == 0) {
		// user is root
		// root has always read/write permission, but at least one of the
		// X bits must be set for execute permission
		fileMode = R_OK | W_OK;
		if ((st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)
			fileMode |= X_OK;
	} else if (st.st_uid == uid) {
		// user is node owner
		if ((st.st_mode & S_IRUSR) != 0)
			fileMode |= R_OK;
		if ((st.st_mode & S_IWUSR) != 0)
			fileMode |= W_OK;
		if ((st.st_mode & S_IXUSR) != 0)
			fileMode |= X_OK;
	} else if (st.st_gid == ((flag & AT_EACCESS) != 0 ? getegid() : getgid())) {
		// user is in owning group
		if ((st.st_mode & S_IRGRP) != 0)
			fileMode |= R_OK;
		if ((st.st_mode & S_IWGRP) != 0)
			fileMode |= W_OK;
		if ((st.st_mode & S_IXGRP) != 0)
			fileMode |= X_OK;
	} else {
		// user is one of the others
		if ((st.st_mode & S_IROTH) != 0)
			fileMode |= R_OK;
		if ((st.st_mode & S_IWOTH) != 0)
			fileMode |= W_OK;
		if ((st.st_mode & S_IXOTH) != 0)
			fileMode |= X_OK;
	}

	if ((accessMode & ~fileMode) != 0)
		RETURN_AND_SET_ERRNO(EACCES);

	return 0;
}
#endif


int
_haiku_build_fchdir(int fd)
{
	if (is_unknown_or_system_descriptor(fd))
		return fchdir(fd);

	RETURN_AND_SET_ERRNO(B_FILE_ERROR);
}


int
_haiku_build_close(int fd)
{
	if (get_descriptor(fd) == NULL)
		return close(fd);

	RETURN_AND_SET_ERRNO(_kern_close(fd));
}


int
_haiku_build_dup(int fd)
{
	if (get_descriptor(fd) == NULL)
		return close(fd);

	RETURN_AND_SET_ERRNO(_kern_dup(fd));
}


int
_haiku_build_dup2(int fd1, int fd2)
{
	if (is_unknown_or_system_descriptor(fd1))
		return dup2(fd1, fd2);

	// TODO: Handle non-system FDs.
	RETURN_AND_SET_ERRNO(B_NOT_SUPPORTED);
}


#if 0
int
_haiku_build_linkat(int toFD, const char* toPath, int pathFD, const char* path,
	int flag)
{
	return linkat(toFD, toPath, pathFD, path, flag);

	// TODO: Handle non-system FDs.
}


int
_haiku_build_unlinkat(int fd, const char* path, int flag)
{
	if (fd >= 0 && fd != AT_FDCWD && get_descriptor(fd) == NULL)
		return unlinkat(fd, path, flag);

	RETURN_AND_SET_ERRNO(_kern_unlink(fd, path));
}


ssize_t
_haiku_build_readlinkat(int fd, const char* path, char* buffer,
	size_t bufferSize)
{
	if (fd >= 0 && fd != AT_FDCWD && get_descriptor(fd) == NULL)
		return readlinkat(fd, path, buffer, bufferSize);

	status_t error = _kern_read_link(fd, path, buffer, &bufferSize);
	if (error != B_OK)
		RETURN_AND_SET_ERRNO(error);

	return bufferSize;
}


int
_haiku_build_symlinkat(const char* toPath, int fd, const char* symlinkPath)
{
	if (fd >= 0 && fd != AT_FDCWD && get_descriptor(fd) == NULL)
		return symlinkat(toPath, fd, symlinkPath);

	RETURN_AND_SET_ERRNO(_kern_create_symlink(fd, symlinkPath, toPath,
		S_IRWXU | S_IRWXG | S_IRWXO));
}


int
_haiku_build_ftruncate(int fd, off_t newSize)
{
	if (fd >= 0 && fd != AT_FDCWD && get_descriptor(fd) == NULL)
		return ftruncate(fd, newSize);

	struct stat st;
	st.st_size = newSize;

	RETURN_AND_SET_ERRNO(_kern_write_stat(fd, NULL, false, &st, sizeof(st),
		B_STAT_SIZE));
}


int
_haiku_build_fchown(int fd, uid_t owner, gid_t group)
{
	return _haiku_build_fchownat(fd, NULL, owner, group, AT_SYMLINK_NOFOLLOW);
}


int
_haiku_build_fchownat(int fd, const char* path, uid_t owner, gid_t group,
	int flag)
{
	if (fd >= 0 && fd != AT_FDCWD && get_descriptor(fd) == NULL)
		return fchownat(fd, path, owner, group, flag);

	struct stat st;
	st.st_uid = owner;
	st.st_gid = group;

	RETURN_AND_SET_ERRNO(_kern_write_stat(fd, path,
		(flag & AT_SYMLINK_NOFOLLOW) == 0, &st, sizeof(st),
		B_STAT_UID | B_STAT_GID));
}


int
_haiku_build_mknodat(int fd, const char* name, mode_t mode, dev_t dev)
{
	return mknodat(fd, name, mode, dev);

	// TODO: Handle non-system FDs.
}


int
_haiku_build_creat(const char* path, mode_t mode)
{
	return _haiku_build_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
}


int
_haiku_build_open(const char* path, int openMode, mode_t permissions)
{
	return _haiku_build_openat(AT_FDCWD, path, openMode, permissions);
}


int
_haiku_build_openat(int fd, const char* path, int openMode, mode_t permissions)
{
	// adapt the permissions as required by POSIX
	mode_t mask = umask(0);
	umask(mask);
	permissions &= ~mask;

	RETURN_AND_SET_ERRNO(_kern_open(fd, path, openMode, permissions));
}


int
_haiku_build_fcntl(int fd, int op, int argument)
{
	if (is_unknown_or_system_descriptor(fd))
		return fcntl(fd, op, argument);

	RETURN_AND_SET_ERRNO(B_NOT_SUPPORTED);
}


int
_haiku_build_renameat(int fromFD, const char* from, int toFD, const char* to)
{
	if ((fromFD >= 0 && fromFD != AT_FDCWD && get_descriptor(fromFD) == NULL)
		|| (toFD >= 0 && toFD != AT_FDCWD && get_descriptor(toFD) == NULL)) {
		return renameat(fromFD, from, toFD, to);
	}

	RETURN_AND_SET_ERRNO(_kern_rename(fromFD, from, toFD, to));
}
#endif
