
#include <string.h>
#include "bnet_xfs.h"

static	WORD get_frm_remote, already_opened = 0, fake_dcre, refcnt;
static	LONG bnet_baspag, *cook_val;
BNET_F	*bnet_f;

void	creat_drives (X_NOD *nodes, BNETX_FD *, int host);

#pragma warn -par

LONG bnet_sync (DMD *d)
{
/*	TRACE(("Last i need, a sync!\r\n", 0));	*/
	return (E_OK);
}

/* This is MiNT (Thomas) stuff !! (but uses the __LINE__ option...) */
#ifdef DEBUG
#define return(x)	{LONG abccba = (LONG)(x);\
	TRACE(("return(%L), Code Line %L\r\n", 2, abccba,\
		(LONG)__LINE__));\
	return (abccba);}
#endif

void bnet_pterm (DMD *d, BASPAG *pd)
{
	WORD	i;

/*	bnet_f->Trace ("pterm %ld", 1, pd);	*//* fucked me up!! (my fault) */
/*	(kernel->_sprintf)(buf, "%L \r\n", (void *)pd);	*/

	for (i = 0; i < MAX_DHD; i++)
	{
		if (dhd[i].dhd_owner == pd)
			dhd[i].dhd_dir = NULL;
	}
	if (bnet_baspag == (long)pd)
	{
		*cook_val = (LONG)bnet_drive;
		*(ULONG *)(&bnet_f) = 0L;
		already_opened = 0;
	}
}

LONG bnet_garbcoll (DMD *d)
{
	/* Nothing to collect anymore (no mallocs)	*/
	TRACE(("garbcoll\r\n", 0));
	return (E_OK);
}

/* Doesn't needed in the non-malloc version but used to make the pointers null */

void bnet_freeDD (void *dd)
{
	BNETX_FD	*i;
	WORD		j;

	TRACE(("freeDD - DD = %L\r\n", 1, dd));
	i = (BNETX_FD *)dd;

	if (i->fd_refcnt != 0)
	{
		TRACE(("freeDD: fd_refcnt == %L!\r\n", 1, (LONG)i->fd_refcnt));
		return;
	}

	i->fd_is_parent++;
	while (i != NULL)
	{
		if (i->fd_is_parent)
			i->fd_is_parent--;

		if (!i->fd_is_parent && !i->fd_refcnt)
		{
			TRACE(("freeDD: DD %L freed!\r\n", 1, i));
			i->fd_file = NULL;
		}
		i = i->fd_parent;
	}

	for (j = ROOT + 1; j < MAX_FD; j++)
	{
		if ((fd[j].fd_file != NULL) && !fd[j].fd_refcnt &&
			!fd[j].fd_is_parent)
		{
			TRACE(("freeDD: Gives \"parent\"-DD %L free!\r\n", 1,
				&fd[j]));
			fd[j].fd_file = NULL;
		}
	}
}

/* Main entry point when the system is ready. The first time the drive is opened
 * it checks for STinG's presence and initializes the ROOT etc.
 * After the first time, it only checks if BNET.APP has terminated
*/

LONG bnet_drv_open (DMD *d)
{
	TRACE(("drv_open - drive %L\r\n", 1, (LONG)d->d_drive));

	if (d->d_xfs == NULL)
	{
		if (d->d_drive == bnet_drive)
		{
			d->d_xfs = real_xfs;
			d->d_root = &fd[ROOT];
			d->d_biosdev = -1;
			d->d_driver = d->d_dfs = 0L;
			d->d_devcode = 0L;
			if (!already_opened)
			{
				if (! is_STING () )
				{
				/*	Cconws("TCP/IP Stack not found!\r\n"); */
					return (EDRIVE);
				}
				if ((cook_val = get_cookie ('BNeT', (ULONG *)&bnet_f)) == NULL )
				{
				/*	Cconws ("Run BNET.APP first!\r\n"); */
					return (EDRIVE);
				}
				if ((*(ULONG *)(&bnet_f) >> 8) == 0L)
					return (EDRIVE);
				already_opened = 1;
				bnet_baspag = *(long *)&bnet_f->bnet_baspag;
			/*	bnet_f->Trace ("Base Page %ld", 1, bnet_baspag); */
				(kernel->fast_clrmem)(root, &root[ROOTSIZE]);
				prepare_dir (root, ROOTSIZE, ROOT_DE);
				(kernel->fast_clrmem)(dhd, &dhd[MAX_DHD]);
				(kernel->fast_clrmem)(fd, &fd[MAX_FD]);
				fd[ROOT].fd_dmd = bnet_dmd = d;
				fd[ROOT].fd_file = &root_de;
				fd[ROOT].fd_parent = NULL;
				fd[ROOT].fd_refcnt = 1;
				root_de = root[1];
				strcpy (root_de.de_fname, "");
				root_de.de_faddr = (char *)root;
			}
			return (E_OK);
		}
		else
			return (EDRIVE);
	}
	if (d->d_xfs != real_xfs || *(ULONG *)(&bnet_f) == 0L)
		return (EDRIVE);
	return (E_OK);
}

LONG bnet_drv_close (DMD *d, WORD mode)
{
	WORD	i;

	TRACE(("drv_close - %S\r\n", 1, mode ? "forced" : "requested"));

	if (d->d_xfs != real_xfs)
		return (EDRIVE);
	for (i = 0; i < MAX_DHD; i++)
	{
		if (dhd[i].dhd_dir != NULL)
		{
			if (mode)
				dhd[i].dhd_dir = NULL;
			else
				return (EACCDN);
		}
	}
	return (E_OK);
}

/*
 * path2DD: Better forget it!! It's completely different from the Gemdos.
 * Checking Thomas' code didn't made things easier...
 */

LONG bnet_path2DD (void *reldir, char *path, WORD mode, char **lastpath,
				LONG *linkdir, char **symlink)
{
	char		*next, *current, *eos, *temp, *orig;
	BNETX_FD	*new, *dd;
	DIR_SLOT	*local;
	CM_PACKET	*packet;
	XATTR		*xattr;
	WORD		fil_or_fol = mode, firsttime = 0;
	LONG		res;

#undef return
#define return(x)	{(kernel->int_mfree)(temp);\
	TRACE(("-> %L, %S, %L, %S; %L\r\n", 5, (LONG)(x), *lastpath,\
		*linkdir, *symlink, (LONG)__LINE__));\
	return(x);}

#ifdef DEBUG
	*lastpath = *symlink = "";
	*linkdir = 0L;
#endif
	TRACE(("path2DD - %L, %S (%L), %S; root = %L\r\n", 5, reldir,
		path , path, fil_or_fol ? "Folder" : "File", &fd[ROOT]));

	temp = (kernel->int_malloc)();
	temp[32] = 0;

	dd = (BNETX_FD *)reldir;
	if (check_dd(dd) < 0)
		return(check_dd(dd));

	if (path == NULL)
		return (fil_or_fol ? EPTHNF : EFILNF);

	eos = strchr (path, 0);		/* Get the pointer to the end of the pathname */
	orig = path;	/* keep a copy */

/* Try to make components out of the path. */
	for (next = path;;)
	{
		for (; *next == '\\'; next++)	/* remove (possible) leading backslashes */
			;
		if (! *next)
		{
			inc_refcnt (dd);
			*lastpath = next;
			return ((LONG)dd);
		}
		path = current = next;

		if ((next = strchr (path, '\\')) != NULL)
		{
			*next = 0;
			strncpy (temp, path, 32L);
			current = temp;
			if (current[1] == 'X' && ((current[2] == '\\') || (current[2] == '\0')) )
				current[1] = 'x';
			*next = '\\';

			for (; *next == '\\'; next++)	/* Remove backslashes (if any) */
				;
		}
		else
		{
			if (! fil_or_fol)
			{
				inc_refcnt (dd);
				*lastpath = path;
				return ((LONG)dd);
			}
			next = eos;		/* end of string */
		}
		if (!strcmp (current, ".."))
		{
			if (dd == &fd[ROOT])
			{
				*lastpath = next;
				*linkdir = (LONG)dd;
				*symlink = NULL;
				return (ELINK);
			}
			dd = dd->fd_parent;
			continue;
		}
		if (!strcmp (current, ".") || *current == '*')
			continue;				/* "." and "*.xxx" will be completely ignored */

/* First, search localy */
		if ((local = find_obj (dd, current, 2, FF_SEARCH, 0)) == NULL)
		{
			if (! firsttime)
			{
				if ((orig = strchr (orig+1, '\\')) != NULL )
				{
					firsttime = 1;
					orig++;
					if (orig[1] == 'x' || orig[1] == 'X')
					{
						packet = bnet_f->packet;
						strcpy (&packet->buf[1], orig);
						packet->buf[2] = ':';
						*packet->buf = (char)strlen (orig);
						*bnet_f->cur_id = dd->fd_file->de_host;
						packet->cmd = FIL_XAT;
						packet->dummy = 0;
					/*	Cconws (&packet->buf[1]); Cconws (" <-P2DD\r\n"); */
						if (! req_tx (*packet->buf + PACSIZ + 2) )
						{
							get_ransw ((char *)&res);
							if (! res)
							{
								xattr = (XATTR *)bnet_f->t_hdr->buf;
								if ((xattr->mode & S_IFMT) != S_IFDIR)
									return (EPTHNF);
							}
							else
								return (EPTHNF);
						}
						else
							return (EPTHNF);
					}
				}
				else
					return (EPTHNF);
			}
			if (dd->fd_parent != &fd[ROOT] || dd->fd_parent != NULL)
			{
				fake_dcre = 1;
				if (! bnet_dcreate (dd, current) )
				{
				/*	Cconws ("building dir... ");
					Cconws (current); Cconws("\r\n");
				*/
					local = find_obj (dd, current, 2, FF_SEARCH, 0);
					fake_dcre = 0;
				}
				else
				{
					fake_dcre = 0;
					return (EPTHNF);
				}
			}
		}

		if (is_link (local->de_xattr.mode))
		{
			TRACE(("path2DD: Found symbolic Link to %S!\r\n", 1,
				&local->de_faddr[2]));
			inc_refcnt(dd);
			*lastpath = next;
			*linkdir = (LONG)dd;
			*symlink = local->de_faddr;
			return (ELINK);
		}

		if (!is_dir (local->de_xattr.mode))		/* impossible, but... */
			return (EPTHNF);

		if ((new = findfd (local)) == NULL)
			return (ENSMEM);
		new->fd_dmd = bnet_dmd;
		new->fd_file = local;
/*
 * Set the current DD as "father" of the new and copy it
 * to the new DD
 */
		new->fd_parent = dd;
		dd = new;
	}
/* Important: The top defined return-macro must become deleted! */
#undef return
}

/*
 * Restore old TRACE
 */
#ifdef DEBUG
#define return(x)	{LONG abccba = (LONG)(x);\
	TRACE(("return(%L), Line %L\r\n", 2, abccba,\
		(LONG)__LINE__));\
	return(abccba);}
#endif

/* A new host has connected, fill its drives	*/

WORD	fill_drives (BNETX_FD *dd)
{
	WKH		*hosts;
	WORD	j;

	j = dd->fd_file->de_host;
	if (j && j < MAXHOSTS)
		hosts = bnet_f->wkh_arr + j;
	if (j  && !hosts->cnh )
	{
		if (conn_host (j) )
		{
			return (ENHNDL);
		}
		else
		{
			creat_drives ((X_NOD *)bnet_f->N_nodes, dd, j);
			hosts->connected = 0;
		}
	}
	else if (j && hosts->connected)
	{
		creat_drives ((X_NOD *)bnet_f->N_nodes, dd, j);
		hosts->connected = 0;
	}
	return (0);
}

LONG bnet_sfirst (void *srchdir, char *name, MX_DTA *dta, WORD attrib, char **symlink)
{
	BNETX_FD	*dd;
	BNETX_DTA	*my_dta;
	DIR_SLOT	*local;
	CM_PACKET	*packet;
	LONG		res;
	WORD		i;
	char		pname[256];
	char		*temp, *ptr;

	TRACE(("sfirst - %L\\%S, %L\r\n", 3, srchdir, name, (LONG)attrib));
	dd = (BNETX_FD *)srchdir;
	if (check_dd(dd) < 0)
		return(check_dd(dd));
	if (fill_drives (dd) )
		return (EFILNF);
	temp = (kernel->int_malloc)();
	my_dta = (BNETX_DTA *)dta;	/* Convert MagiC's DTA to BNET DTA	*/
	tostrunc (temp, name, 1);
	fill_tosname (my_dta->dta_mask, temp);
	(kernel->int_mfree)(temp);
	my_dta->dta_pos = 0;

	if ((strchr (my_dta->dta_mask, '?') != NULL))
	{
		if (!raccess(dd->fd_file))
			return (EACCDN);
	}
/*	else
	{
*/
	/*	Cconws (name); Cconws (" <- Sfirst \r\n"); */
		if ((local = find_obj (dd, name, 0, FF_SEARCH, 1)) == NULL)
		{
		get_remote:
			if (bnet_DD2name (dd, pname, sizeof(pname) ) != E_OK)
				return (EPTHNF);

			ptr = strchr (pname+1, '\\');
			if (ptr)
			{
				ptr++;
				if ((ptr[1] == 'X' || ptr[1] == 'x') && (ptr[2] == '\\' || ptr[2] == '\0'))
				{
					ptr[1] = ':';
					packet = bnet_f->packet;
					packet->cmd = 0x4E;
					packet->path_yn = 1;
					packet->dummy = attrib;
					packet->timeout = *bnet_f->cur_id = dd->fd_file->de_host;

					strcpy (&packet->buf[1], ptr);
					strcat (&packet->buf[1], "\\");
					strcat (&packet->buf[1], name);
				/*	Cconws (&packet->buf[1]);	Cconws (" -> Fsfirst\r\n"); */
					i = (int)strlen (&packet->buf[1]);
					*packet->buf = i;

					if (! req_tx (i + PACSIZ + 2) )
					{
						get_ransw ((char *)&res);
						if (res == 0L)
						{
							ptr = (char *)dta;
							memcpy (&ptr[20], bnet_f->t_hdr->buf, sizeof(DTA) - 20);
							ptr[1] = 0xF0 + packet->timeout; /* ie cur_id */
							my_dta->dta_drive = (char)bnet_drive;
						}
						return (res);
					}
					else
						return (EFILNF);
				}
			}
		}
		else
			my_dta->dta_pos = -local->de_nr;
/*	} */
	my_dta->dta_drive = (char)bnet_drive;
	my_dta->dta_attr = (char)attrib;
	my_dta->dta_dir = (DIR_SLOT *)dd->fd_file->de_faddr;

	return (bnet_snext ( (MX_DTA *)my_dta, bnet_dmd, symlink) );
}

LONG bnet_snext (MX_DTA *dta, DMD *dmd, char **symlink)
{
	BNETX_DHD	handle;
	BNETX_DTA	*my_dta;
	XATTR		xattr;
	CM_PACKET	*packet;
	WORD		first_call,
				matched;
	LONG		dummy,
				r, res;
	char		*name, *ptr = (char *)dta;

	TRACE(("snext - %S\r\n", 1, ((BNETX_DTA *)dta)->dta_mask));
	if (dmd != bnet_dmd)
		return(EDRIVE);
	my_dta = (BNETX_DTA *)dta;

	if (ptr[1] > 0xF0)
	{
		packet = bnet_f->packet;
		packet->cmd = 0x4F;
		packet->path_yn = 0;
		packet->dummy = 0;
		packet->timeout = *bnet_f->cur_id = ptr[1] - 0xF0;
		if (! req_tx (PACSIZ))
		{
			get_ransw ((char *)&res);
			if (res == 0L)
			{
				memcpy (&ptr[20], bnet_f->t_hdr->buf, sizeof(DTA) - 20);
				ptr[1] = 0xF0 + packet->timeout;
				my_dta->dta_drive = (char)bnet_drive;
			}
		/*	Cconws (dta->dta_name);	Cconws (" <- Fsnext !\r\n"); */
			return (res);
		}
		else
			return (EACCDN);
	}

	first_call = (my_dta->dta_pos <= 0);
	if (my_dta->dta_pos < 0)
		my_dta->dta_pos = -my_dta->dta_pos;

	if (!first_call && (strchr(my_dta->dta_mask, '?') == NULL))
		return (ENMFIL);

	handle.dhd_dmd = dmd;
	handle.dhd_dir = my_dta->dta_dir;
	handle.dhd_pos = my_dta->dta_pos;
	handle.dhd_tosmode = 1;

	handle.dhd_owner = *real_kernel->act_pd;
	get_frm_remote = 0;
/*	Cconws (my_dta->dta_name); Cconws (" <- Snext\r\n"); */
	for (;;)
	{
		r = bnet_dreaddir (&handle, 13, my_dta->dta_name, &xattr, &dummy);
		my_dta->dta_pos = handle.dhd_pos;
	/*	bnet_f->Trace ("%s Pos = %d \r\n", 2, my_dta->dta_name, my_dta->dta_pos); */
		if (r)
		{
			if (first_call && (r == ENMFIL))
				r = EFILNF;
			return (r);
		}
		name = (kernel->int_malloc)();
		fill_tosname (name, my_dta->dta_name);
		matched = match_tosname (name, my_dta->dta_mask);
		(kernel->int_mfree)(name);
		if (matched)
		{
			if (is_link (xattr.mode))
			{
				*symlink = (char *)xattr.index;
				return (ELINK);
			}
			if (xattr.attr == 0)
				break;
			if (xattr.attr & 0x21)
				break;
			if (my_dta->dta_attr & xattr.attr)
				break;
		}
	}
	my_dta->dta_attribute = xattr.attr;
	my_dta->dta_time = xattr.mtime;
	my_dta->dta_date = xattr.mdate;
	my_dta->dta_len = xattr.size;
	return(E_OK);
}


LONG bnet_fopen (void *dir, char *name, WORD omode, WORD attrib, char **symlink)
{
	BNETX_FD	*dd, *new_fd;
	CM_PACKET	*packet;
	DIR_SLOT	*local;
	FILEBLOCK	*file, *next;
	LONG		res;
	WORD		han, i;
	char		pname[256], *ptr, test[2];

	TRACE(("fopen - %L\\%S, %L, %L\r\n", 4, dir, name, (LONG)omode, (LONG)attrib));
	dd = (BNETX_FD *)dir;

	if (bnet_DD2name (dd, pname, sizeof(pname) ) != E_OK )
		return (EPTHNF);

	ptr = strchr (pname+1, '\\');
	if (ptr)
	{
		ptr++;
		if ((ptr[1] == 'x' || ptr[1] == 'X') && (ptr[2] == '\\' || ptr[2] == '\0'))
		{
			ptr[1] = ':';
			packet = bnet_f->packet;
			strcpy (&packet->buf[1], ptr);
			strcat (&packet->buf[1], "\\");
			strcat (&packet->buf[1], name);
		/*	Cconws (&packet->buf[1]);	Cconws (" -> Fopen\r\n"); */
			i = (int)strlen (&packet->buf[1]);
			*packet->buf = (char)i;
			*bnet_f->cur_id = dd->fd_file->de_host;

	/* is it Fopen or Fcreate ?	*/
			if ((omode & O_CREAT) == O_CREAT)
			{
				packet->cmd = 0x3C;
				packet->dummy = attrib;
			}
			else
			{
				packet->cmd = 0x3D;
				packet->dummy = ((omode >> 1) & 0x3);
			/*
				test[0] = packet->dummy + '0';	test[1] = '\0';
				Cconws (test);
			*/
			}
			packet->path_yn = 1;
			if ( ! req_tx (i + 2 + PACSIZ))
			{
				get_ransw ((char *)&res);
				if ((han = res) > 0)
					goto	build_cache;	/* not so cached anymore */
			/*
				else
					Cconws (" Fopen Failed ");
			*/
			}
		}
	}
/*	Cconws (" Wrong Path \r\n"); */
	return (EFILNF);

build_cache:
	local = find_obj (dd, name, 2, FF_SEARCH, 0);
	if ((new_fd = findfd (local)) == NULL)
		return (ENSMEM);
	if (new_fd->fd_parent != NULL)
	{
		return(EACCDN);
	}
	if (local == NULL)
	{
		if ((local = new_file (dd, name)) == NULL)
		{
			return (EACCDN);
		}

		file = &def_file;	/* test */
		local->de_faddr = (char *)file;
		local->de_xattr.mode = S_IFREG;

		if ((omode & OM_EXEC) || has_xext (name))	/* Thomas */
			local->de_xattr.mode |= 0777;
		else
			local->de_xattr.mode |= 0666;

		if (attrib & FA_RDONLY)
		{
			local->de_xattr.mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
			attrib = FA_RDONLY | FA_CHANGED;
		}
		else
			attrib = FA_CHANGED;					/* end Thomas! */
		local->de_xattr.index = (LONG)file;
		local->de_xattr.size = 0L;
		local->de_xattr.nblocks = 1L;
		local->de_xattr.attr = attrib;
		local->de_host = dd->fd_file->de_host;
	}
	new_fd->fd_dmd = bnet_dmd;
	new_fd->fd_refcnt = 1;
	new_fd->fd_mode = omode;

	new_fd->fd_dev = &bnet_dev;
	new_fd->fd_fpos = 0L;
	new_fd->fd_file = local;
	new_fd->fd_parent = dd;
	new_fd->fd_han = han;		/* check for negative	*/
/*
	test[0] = han + '0'; test[1] = '\0';
	Cconws (test); Cconws (" <-Rem Han \r\n");
*/
	new_fd->fd_owner = *real_kernel->act_pd;	/* Who has opened the file ? */
	return ((LONG)new_fd);
}

/* If fdelete is for a local object, ignore it	*/

LONG bnet_fdelete (void *dir, char *name)
{
	BNETX_FD	*dd;
	CM_PACKET	*packet;
	LONG		res;
	WORD		i;
	char		pname[256], *ptr;

	TRACE(("fdelete - %L\\%S\r\n", 2, dir, name));
	dd = (BNETX_FD *)dir;
	if (bnet_DD2name (dd, pname, sizeof(pname) ) != E_OK )
		return (EPTHNF);

	ptr = strchr (pname+1, '\\');
	if (ptr)
	{
		ptr++;
		if ((ptr[1] == 'x' || ptr[1] == 'X') && (ptr[2] == '\\' || ptr[2] == '\0'))
		{
			ptr[1] = ':';
			packet = bnet_f->packet;
			strcpy (&packet->buf[1], ptr);
			strcat (&packet->buf[1], "\\");
			strcat (&packet->buf[1], name);
		/*	Cconws (&packet->buf[1]);	Cconws (" -> Fdelete\r\n"); */
			i = (int)strlen (&packet->buf[1]);
			*packet->buf = (char)i;
			*bnet_f->cur_id = dd->fd_file->de_host;

			packet->cmd = 0x41;
			if ( ! req_tx (i + 2 + PACSIZ))
			{
				get_ransw ((char *)&res);
				return (res);
			}
		}
	}
	return (EPTHNF);
}

/* Allow links to remote, ignore hard links (Andreas says so!) */

LONG bnet_link (void *olddir, void *newdir, char *oldname, char *newname, WORD hard_link)
{
	BNETX_FD	*old, *new;
	CM_PACKET	*packet;
	WORD		i, j;
	LONG		res;
	char		*temp, pname[256], *ptr;

	if (hard_link)
	{
		TRACE(("link - hardlinks not supported!\r\n", 0));
		return (EINVFN);
	}
	if (olddir != newdir)
		return (EINVFN);

	if (bnet_DD2name (olddir, pname, sizeof(pname) ) != E_OK )
		return (EPTHNF);
	ptr = strchr (pname+1, '\\');
	if (ptr)
	{
		ptr++;
		if ((ptr[1] == 'x' || ptr[1] == 'X') && (ptr[2] == '\\' || ptr[2] == '\0'))
		{
			ptr[1] = ':';
			old = (BNETX_FD *)olddir;
			packet = bnet_f->packet;
			strcpy (&packet->buf[1], ptr);
			strcat (&packet->buf[1], "\\");
			strcat (&packet->buf[1], oldname);
			i = (int)strlen (&packet->buf[1]);
			*packet->buf = (char)i;
	/*	Cconws (&packet->buf[1]); Cconws ("  "); */
			strcpy (&packet->buf[i+3], ptr);
			strcat (&packet->buf[i+3], "\\");
			strcat (&packet->buf[i+3], newname);
			j = (int)strlen (&packet->buf[i+3]);
			packet->buf[i+2] = (char)j;
	/*	Cconws (&packet->buf[i+3]); Cconws (" \r\n"); */
			*bnet_f->cur_id = old->fd_file->de_host;

			packet->cmd = 0x56;		/* Rename */
			if (! req_tx (i + j + PACSIZ + 4) )
			{
				get_ransw ((char *)&res);
				return(res);
			}
			else
				return (EPTHNF);
		}
	}
	return (EINVFN);
}

LONG bnet_xattr (void *dir, char *name, XATTR *xattr, WORD mode, char **symlink)
{
	BNETX_FD	*dd;
	DIR_SLOT	*local;
	CM_PACKET	*packet;
	LONG		res;
	WORD		i;
	char		pname[256], *ptr;

	TRACE(("xattr - %L\\%S (%L)\r\n", 3, dir, name, name));
	dd = (BNETX_FD *)dir;
	if (check_dd(dd) < 0)
	{
		TRACE(("xattr: check_dd error!\r\n", 0));
		return(check_dd(dd));
	}
	TRACE(("xattr: %S\r\n", 1, name));

	if ((local = find_obj (dd, name, 0, FF_SEARCH, 1)) != NULL)
	{
		if (!mode && is_link (local->de_xattr.mode))
		{
			TRACE(("xattr: Found symbolic Link to %S!\r\n", 1, &local->de_faddr[2]));
			*symlink = local->de_faddr;
			return (ELINK);
		}
		if (dd == &fd[ROOT])
		{
			*xattr = local->de_xattr;
			return (E_OK);
		}
	}

	if (bnet_DD2name (dd, pname, sizeof(pname) ) != E_OK )
		return (EPTHNF);
/*	Cconws ("Fxattr -> "); Cconws (pname);	Cconws("\r\n");	*/
	ptr = strchr (pname+1, '\\');
	if (ptr)
	{
		ptr++;
		if ((ptr[1] == 'x' || ptr[1] == 'X') && (ptr[2] == '\\' || ptr[2] == '\0'))
		{
			ptr[1] = ':';
			packet = bnet_f->packet;
			strcpy (&packet->buf[1], ptr);
			strcat (&packet->buf[1], "\\");
			strcat (&packet->buf[1], name);

			i = (int)strlen (&packet->buf[1]);
			*packet->buf = (char)i;
			*bnet_f->cur_id = dd->fd_file->de_host;
			packet->cmd = FIL_XAT;
			packet->dummy = mode;
			if (! req_tx (i + PACSIZ + 2) )
			{
				get_ransw ((char *)&res);
				if (! res)
				{
					memcpy (xattr, bnet_f->t_hdr->buf, sizeof(XATTR) );
					return (E_OK);
				}
			/*
				else
					Cconws ("Fxattr Negative!\r\n");
			*/
			}
		}
	}
	return (EFILNF);
}

LONG bnet_attrib (void *dir, char *name, WORD rwflag, WORD attrib, char **symlink)
{
	LONG		retcode, res;
	CM_PACKET	*packet;
	BNETX_FD	*dd;
	WORD		i;
	char		pname[256], *ptr;

	retcode = (work_entry ((BNETX_FD *)dir, name, symlink, rwflag,
		rwflag, attrib, attrib_action));
	if (retcode != EFILNF)
		return (retcode);

	dd = (BNETX_FD *)dir;
	if (bnet_DD2name (dd, pname, sizeof(pname) ) != E_OK )
		return (retcode);
/*	Cconws (pname);	*/
	ptr = strchr (pname+1, '\\');
	if (ptr)
	{
		ptr++;
		if ((ptr[1] == 'x' || ptr[1] == 'X') && (ptr[2] == '\\' || ptr[2] == '\0'))
		{
			ptr[1] = ':';
			packet = bnet_f->packet;
			strcpy (&packet->buf[1], ptr);
			strcat (&packet->buf[1], "\\");
			strcat (&packet->buf[1], name);

			i = (int)strlen (&packet->buf[1]);
			*packet->buf = (char)i;
			*bnet_f->cur_id = dd->fd_file->de_host;
			packet->cmd = 0x43;
			packet->path_yn = rwflag;
			packet->dummy = attrib;
			if (! req_tx (i + PACSIZ + 2) )
			{
				get_ransw ((char *)&res);
				return (res);
			}
		}
	}
	return (retcode);
}

LONG attrib_action (DIR_SLOT *entry, LONG rwflag, LONG attrib)
{
	if (rwflag)
	{
		if (attrib & FA_RDONLY)
		{
			entry->de_xattr.mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
			entry->de_xattr.attr |= FA_RDONLY;
		}
		else
		{
			entry->de_xattr.mode |= S_IWUSR | S_IWGRP | S_IWOTH;
			entry->de_xattr.attr &= ~FA_RDONLY;
		}
		if ((attrib & FA_CHANGED) && is_file(entry->de_xattr.mode))
			entry->de_xattr.attr |= FA_CHANGED;
		else
			entry->de_xattr.attr &= ~FA_CHANGED;
	}
	return (entry->de_xattr.attr);
}

LONG bnet_chown (void *dir, char *name, UWORD uid, UWORD gid,
	char **symlink)
{
	TRACE(("chown - not supported\r\n", 0));

	return (work_entry((BNETX_FD *)dir, name, symlink, 0, uid, gid,
		(LONG)NULL));
}

LONG bnet_chmod (void *dir, char *name, UWORD mode, char **symlink)
{
	TRACE(("chmod - %L\\%S, %L\r\n", 3, dir, name, (LONG)mode));

	return (work_entry((BNETX_FD *)dir, name, symlink, 1, mode, 0L,
		chmod_action));
}

LONG chmod_action (DIR_SLOT *entry, LONG _mode, LONG dummy)
{
	UWORD	mode;

	mode = (UWORD)_mode;
	entry->de_xattr.mode &= S_IFMT;
	entry->de_xattr.mode |= mode;
	if (!waccess(entry))
		entry->de_xattr.attr |= FA_RDONLY;
	else
		entry->de_xattr.attr &= ~FA_RDONLY;
	return(E_OK);
}

LONG bnet_dcreate (void *dir, char *name)
{
	BNETX_FD	*dd, *td;
	WKH			*hosts;
	CM_PACKET	*packet;
	DIR_SLOT	*entry, *new;
	LONG		res;
	WORD		j, len;
	char		pname[256], *ptr, i;

	TRACE(("dcreate - %L\\%S, rootDD = %L\r\n", 3, dir, name, &fd[ROOT]));

	dd = (BNETX_FD *)dir;

/* Same names are not allowed !	*/
	if (find_obj (dir, name, 0, FF_EXIST, 0) != NULL)
	{
		TRACE(("dcreate: Folder already exists!\r\n", 0));
		return (EACCDN);
	}
	if (fake_dcre)
		goto	is_drive;
	if (bnet_DD2name (dd, pname, sizeof(pname) ) != E_OK )
		return (EPTHNF);
	ptr = strchr (pname+1, '\\');
	if (ptr)
	{
		ptr++;
		if ((ptr[1] == 'x' || ptr[1] == 'X') && (ptr[2] == '\\' || ptr[2] == '\0') )
		{
			ptr[1] = ':';
			packet = bnet_f->packet;
			strcpy (&packet->buf[1], ptr);
			strcat (&packet->buf[1], "\\");
			strcat (&packet->buf[1], name);

		/*	Cconws (&packet->buf[1]);	Cconws (" -> Dcreate\r\n"); */
			i = (char)strlen (&packet->buf[1]);
			*packet->buf = i;
			*bnet_f->cur_id = dd->fd_file->de_host;

			packet->cmd = 0x39;	/* Dcreate	*/
			if ( ! req_tx (i + 2 + PACSIZ))
			{
				get_ransw ((char *)&res);
				if (res != 0L)
					return (res);
			}
		}
		else
			return (EACCDN);
	}
is_drive:
	if (check_dd(dd) < 0)
	{
		TRACE(("dcreate: dd error!\r\n", 0));
		return(check_dd(dd));
	}

/* Request a new entry, if remote. */
	if ((entry = new_file (dd, name)) == NULL)
	{
		TRACE(("dcreate: No more space!\r\n", 0));
		return (EACCDN);
	}

/* 	Cconws (name);	Cconws ("\r\n");	*/
 	hosts = bnet_f->wkh_arr;
	j = MAXHOSTS;
	hosts += (j - 1);
	while (--j)
	{
		len = strlen (hosts->name);
		if (len && !strncmp (hosts->name, name, len))
			break;
		hosts--;
	}
/*	Cconws ("Dcreate\r\n"); */
	if (j)
	{
		new = &rem_slot[j][0];	/* btest */
		prepare_dir (new, ROOTSIZE*2, (DIR_SLOT *)dd->fd_file->de_faddr); 	/* btest */
	}
	else
	{
		if (dd->fd_parent == NULL || dd->fd_parent->fd_file->de_host == 0)
			return (EACCDN);
		new = (DIR_SLOT *)&def_slot[refcnt * DEFSIZE];	/* btest */
		if (++refcnt >= ROOTSIZE * 2)
			refcnt = 0;
		prepare_dir (new, (WORD)DEF_DIR, (DIR_SLOT *)dd->fd_file->de_faddr); 	/* btest */
	}

	entry->de_faddr = (char *)new;
	entry->de_xattr.mode = S_IFDIR | 0777;
	entry->de_xattr.index = (LONG)new;
	entry->de_xattr.size = 0L;
	entry->de_xattr.nblocks = 1L;
	entry->de_xattr.attr = FA_DIR;
	if (j)
		entry->de_host = j;
	else		/* Get host no from prevoius dir */
		entry->de_host = dd->fd_parent->fd_file->de_host;
	return (E_OK);
}

LONG bnet_ddelete (void *dir)
{
	BNETX_FD	*dd, parent, copy;
	CM_PACKET	*packet;
	LONG		res;
	DIR_SLOT	*the_dir;
	WORD		i, cnt, max;
	char		pname[256], *ptr;

	TRACE(("ddelete - %L\r\n", 1, dir));
	dd = (BNETX_FD *)dir;

	if (bnet_DD2name (dd, pname, sizeof(pname) ) != E_OK )
		return (EPTHNF);

	ptr = strchr (pname+1, '\\');
	if (ptr)
	{
		ptr++;
		if ((ptr[1] == 'x' || ptr[1] == 'X') && (ptr[2] == '\\'))
		{
			ptr[1] = ':';
			packet = bnet_f->packet;
			strcpy (&packet->buf[1], ptr);
		/*	strcat (&packet->buf[1], "\\");
			strcat (&packet->buf[1], dd->fd_file->de_fname);
		*/
		/*	Cconws (&packet->buf[1]);	Cconws (" -> Ddelete\r\n"); */
			i = (int)strlen (&packet->buf[1]);
			*packet->buf = (char)i;
			*bnet_f->cur_id = dd->fd_file->de_host;

			packet->cmd = 0x3A;
			if ( ! req_tx (i + 2 + PACSIZ))
			{
				get_ransw ((char *)&res);
				if (res != 0L)
					return (res);
			}
		}
		else
			return (EACCDN);
	}
	else
		return (EACCDN);
	if (check_dd (dd) < 0)
		return (check_dd(dd));
	TRACE(("ddelete: %L entspricht %L\\%S\r\n", 3, dir, dd->fd_parent,
		dd->fd_file->de_fname));
	if (real_kernel->version < 3)
	{
		if (--dd->fd_refcnt > 0)
		{
			TRACE(("ddelete: refcnt == %L!\r\n", 1,
				(LONG)dd->fd_refcnt));
			return (EACCDN);
		}
	}
	else
	{
		TRACE(("ddelete: Kernel version > 2, no fd_refcnt-check!"
			"\r\n", 0));
	}
	copy = *dd;
	parent = *(dd->fd_parent);

	if (real_kernel->version < 3)
		bnet_freeDD (dd);

	if (!waccess (parent.fd_file))
	{
		return (EACCDN);
	}
	if (!waccess (copy.fd_file))
	{
		return (EACCDN);
	}
	the_dir = (DIR_SLOT *)copy.fd_file->de_faddr;
	if (dir_is_open (the_dir))
	{
		TRACE(("ddelete: Dir is still open!\r\n", 0));
		return (EACCDN);
	}

	max = the_dir->de_maxnr;
	for (cnt = i = 0; i < max; i++)
	{
		if (the_dir[i].de_faddr != NULL)
		{
			if (++cnt > 2)
			{
				return (EACCDN);
			}
		}
	}
	copy.fd_file->de_faddr = NULL;
	return (res);
}

/* MagiC specific: Make a path out of a DD	*/

LONG bnet_DD2name (void *dir, char *name, WORD bufsize)
{
	BNETX_FD	*dd;
	char		*temp;

	dd = (BNETX_FD *)dir;
	if (check_dd(dd) < 0)
		return(check_dd(dd));

	if (bufsize < 1)
		return (ERANGE);
	*name = 0;
	temp = (kernel->int_malloc)();

	for (; dd->fd_parent != NULL; dd = dd->fd_parent)
	{
		if (Pdomain(-1) == 0)
			tostrunc (temp, dd->fd_file->de_fname, 0);
		else
			strcpy (temp, dd->fd_file->de_fname);
		if ((WORD)(strlen(name) + strlen(temp) + 1) >= bufsize)
		{
			(kernel->int_mfree)(temp);
			return (ERANGE);
		}
		strrev (temp);
		strcat (name, temp);
		strcat (name, "\\");
	}
	strrev (name);
	(kernel->int_mfree)(temp);
	return (E_OK);
}

LONG bnet_dopendir (void *dir, WORD tosflag)
{
	WORD		i, j = 0;
	BNETX_FD	*dd;
	char		name[256], *ptr;
	CM_PACKET	*packet;
	LONG 		last_dir = 0L;

	dd = (BNETX_FD *)dir;
	if (check_dd (dd) < 0)
		return (check_dd(dd));

/*	Cconws (dd->fd_file->de_fname);
	Cconws (" Dopendir \r\n");
*/
	if ( bnet_DD2name (dd, name, sizeof(name)) == E_OK )
	{
		ptr = strchr (name+1, '\\');
		if (ptr)
		{
			ptr++;
			if ((ptr[1] == 'x' || ptr[1] == 'X') && (ptr[2] == '\\' || ptr[2] == '\0'))
			{
				ptr[1] = ':';
				packet = bnet_f->packet;
				if (ptr[2] == '\0')		/* If only a drive append a backslash */
				{
					ptr[2] = '\\'; ptr[3] = '\0';
				}
				i = (int)strlen (ptr);
				strcpy (&packet->buf[1], ptr);
				*packet->buf = (char)i;
			/*	Cconws (&packet->buf[1]);	Cconws("\r\n"); */
				packet->cmd = DIR_OPN;
				packet->dummy = tosflag;

				*bnet_f->cur_id = dd->fd_file->de_host;
				if (! req_tx (i + PACSIZ + 2) )
					get_ransw ((char *)&last_dir);
				if ((last_dir & 0xFF000000L) == 0xFF000000L)
				{
					return (ENHNDL);
				}
				else
				{
					j = 1;
					get_frm_remote = 1;
				}
			}
		}
	}

/* Search for a free Dir Handle */
	for (i = 0; i < MAX_DHD; i++)
	{
		if (dhd[i].dhd_dir == NULL)
		{
/*
 * Found a free entry, check if it is from remote or a new host.
 */
			if (j)
				goto	fill_dhd;
			get_frm_remote = 0;

			if (fill_drives (dd) )
				return (ENHNDL);
		fill_dhd:
			dhd[i].dhd_dmd = bnet_dmd;
			dhd[i].dhd_dir = (last_dir) ? (DIR_SLOT *)last_dir : (DIR_SLOT *)dd->fd_file->de_faddr;
			dhd[i].dhd_pos = 0;
			dhd[i].dhd_host = dd->fd_file->de_host;
			dhd[i].dhd_tosmode = tosflag;
			dhd[i].dhd_owner = *real_kernel->act_pd;
			return ((LONG)&dhd[i]);
		}
	}
/* No free structure ?? return error */
	return (ENHNDL);
}

LONG bnet_dreaddir (void *dhd, WORD size, char *buf, XATTR *xattr, LONG *xr)
{
	BNETX_DHD	*handle;
	BNETX_FD	help;
	DIR_SLOT	*dir;
	CM_PACKET	*packet;
	RDXDIR		*dx_buf;
	LONG		res;
	WORD		pos;

	TRACE(("%S\r\n", 1, (xattr == NULL) ? "dreaddir" : "dxreaddir"));
	handle = (BNETX_DHD *)dhd;

	if ((handle == NULL) || (handle->dhd_dmd != bnet_dmd) ||
		(handle->dhd_owner != *real_kernel->act_pd))
	{
		return (EIHNDL);
	}

	if (get_frm_remote)
	{
		packet = bnet_f->packet;
		packet->cmd = DIR_RD;
		packet->dummy = size;
		packet->timeout = (xattr == NULL) ? 0 : 1;
		*(long *)packet->buf = (long)handle->dhd_dir;
		*bnet_f->cur_id = handle->dhd_host;
		if (!req_tx (PACSIZ + 4) )
			get_ransw ((char *)&res);
		if (res == 0)
		{
			if (xattr)
			{
				dx_buf = (RDXDIR *)bnet_f->t_hdr;
				*(LONG *)buf = *(LONG *)dx_buf->name;
				strcpy (&buf[4], &dx_buf->name[4]);
				memcpy (xattr, &dx_buf->xattr, sizeof (XATTR) );
				*xr = dx_buf->xr;
			}
			else
			{
				*(LONG *)buf = *(LONG *)bnet_f->t_hdr->buf;
				strcpy(&buf[4], &bnet_f->t_hdr->buf[4]);
			}
			handle->dhd_pos++;
			return (E_OK);
		}
		else
			return (res);
	}
	dir = handle->dhd_dir;
	pos = handle->dhd_pos;
/*	bnet_f->Trace (" dhd_pos = %d ", 1, pos);	*/
/* Search for the next not-empty dir entry 		*/
	for (; (pos < dir->de_maxnr) && (dir[pos].de_faddr == NULL); pos++)
		;
	if (pos >= dir->de_maxnr)
	{
	/*	bnet_f->Trace (" Abort_pos = %d maxnr = %d \r\n", 2, pos, dir->de_maxnr); */
		return (ENMFIL);
	}

	if (handle->dhd_tosmode)
	{
		if (size < 13)
			return (ERANGE);
		tostrunc (buf, dir[pos].de_fname, 0);	/* Truncate to TOS 8+3	*/
		if (buf[1] == 'X' && buf[2] == '\0')	/* Beware of Fsfirst/next */
			buf[1] = 'x';
	}
	else
	{
		if (((WORD)strlen (dir[pos].de_fname) + 4) >= size)
		{
			TRACE(("%S is longer than the buffer!", 1, (LONG)dir[pos].de_fname));
			return(ERANGE);
		}
		*(LONG *)buf = (LONG)dir[pos].de_xattr.index;
		strcpy(&buf[4], dir[pos].de_fname);
	}

	if (xattr != NULL)
	{
		*xattr = dir[pos].de_xattr;
		*xr = 0L;
	}
	handle->dhd_pos = pos + 1;
	return (E_OK);
}

LONG bnet_drewinddir (void *dhd)
{
	BNETX_DHD	*handle;
	CM_RCSEEK	*packet;
	LONG		res = E_OK;

	TRACE(("drewinddir\r\n", 0));
	handle = (BNETX_DHD *)dhd;

	if ((handle == NULL) || (handle->dhd_dmd != bnet_dmd) ||
		(handle->dhd_owner != *real_kernel->act_pd))
	{
		return (EIHNDL);
	}
	if (get_frm_remote)
	{
	/*	Cconws ("Drewinddir to remote \r\n"); */
		packet = (CM_RCSEEK *)bnet_f->packet;
		packet->cmd = DIR_RWD;
		packet->dummy = (long)handle->dhd_dir;
		*bnet_f->cur_id = handle->dhd_host;
		if (! req_tx (PACSIZ) )
			get_ransw ((char *)&res);
	}
	if (res == 0L)
		handle->dhd_pos = 0;
	return (res);
}

LONG bnet_dclosedir (void *dhd)
{
	BNETX_DHD	*handle;
	CM_RCSEEK	*packet;
	LONG		res = E_OK;

	TRACE(("dclosedir\r\n", 0));
	handle = (BNETX_DHD *)dhd;

	if ((handle == NULL) || (handle->dhd_dmd != bnet_dmd) ||
		(handle->dhd_owner != *real_kernel->act_pd))
	{
		return (EIHNDL);
	}
	if (get_frm_remote)
	{
		packet = (CM_RCSEEK *)bnet_f->packet;
		packet->cmd = DIR_CLS;
		packet->dummy = (long)handle->dhd_dir;
		*bnet_f->cur_id = handle->dhd_host;
		if ( ! req_tx (PACSIZ) )
			get_ransw ((char *)&res);
	}
	/* Free handle */
	if (res == 0L)
		handle->dhd_dir = NULL;
	return (res);
}

LONG bnet_dpathconf (void *dir, WORD which)
{
	BNETX_FD	*dd = (BNETX_FD *)dir;
	CM_PACKET	*packet;
	WORD		i;
	LONG		res;
	char		pname[256], *ptr;

	TRACE(("dpathconf - %L, %L\r\n", 2, dir, (LONG)which));
	if (check_dd (dir) < 0)
		return (check_dd (dir));
	if (bnet_DD2name (dd, pname, sizeof(pname) ) != E_OK )
		return (EPTHNF);
	ptr = strchr (pname+1, '\\');
	if (ptr)
	{
		ptr++;
		if ((ptr[1] == 'x' || ptr[1] == 'X') && (ptr[2] == '\\'))
		{
			ptr[1] = ':';
			packet = bnet_f->packet;
			strcpy (&packet->buf[1], ptr);
			i = (int)strlen (&packet->buf[1]);
			*packet->buf = (char)i;
			*bnet_f->cur_id = dd->fd_file->de_host;
			packet->dummy = which;
			packet->cmd = DPH_CNF;
			if ( ! req_tx (i + 2 + PACSIZ))
			{
				get_ransw ((char *)&res);
				return (res);
			}
			else
				return (EPTHNF);
		}
	}
	switch (which)
	{
		case -1:
/* Maximum 6 modes */
			return (6);
		case 0:
/* We can open available FD files at once ( -1 for the Root_DD)	*/
			return (MAX_FD - 1);
		case 1:
/* No Hardlinks, so, maximum 1 Link per file */
			return (1);
		case 2:
/*
 * Pathnames can expand to 254 chars (to be able to send to the other side)
 * but station name (ie "myfalcon") can be added.
 */
			return (254L);
		case 3:
/* Filename's maximum 32 chars */
			return (32L);
		case 4:
/* "On one piece" can maximum SECSIZ (from BNET_APP.H) bytes become written */
			return (SECSIZ);
		case 5:
/* The XFS drive switches automatically to long_filenames */
			return (1L);
		case 6:
/* Full differentiation of upper/lower case */
			return (0L);
		default:
/* Other Dpathconf modes are not known to this filesystem */
			return (EINVFN);
	}
}

LONG bnet_dfree (void *dd, DISKINFO *free)
{
	LONG	freeblocks, usedblocks;

	if (check_dd(dd) < 0)
		return(check_dd(dd));

	freeblocks = (512L * 1024L);	/* dummy */
	usedblocks = (100L * 1024L);	/* about this size... */

	free->b_free = freeblocks;
	free->b_total = freeblocks + usedblocks;
	free->b_secsiz = (LONG)SECSIZ;
	free->b_clsiz = 1L;
	return (E_OK);
}

LONG bnet_wlabel (void *dir, char *name)
{
	TRACE(("wlabel - %S\r\n", 1, name));

	if (check_dd(dir) < 0)
		return(check_dd(dir));

	if (*name == '\xe5')
		strcpy (volume_label, "");
	else
	{
		volume_label[32] = 0;
		strncpy (volume_label, name, 32L);
	}
	return (E_OK);
}

LONG bnet_rlabel (void *dir, char *name, char *buf, WORD len)
{
	TRACE(("rlabel - %S %L\r\n", 2, name, (LONG)len));

	if (check_dd(dir) < 0)
		return(check_dd(dir));
	if (! *volume_label )
		return (EFILNF);
	if ((WORD)strlen (volume_label) >= len)
		return (ERANGE);

	strcpy (buf, volume_label);
	return (E_OK);
}

/*
LONG bnet_symlink (void *dir, char *name, char *to)
{
	BNETX_FD	*dd;
	DIR_SLOT	*entry;
	char		*link;
	LONG		len;

	/* ===== NOT IMPLEMENTED ===== */
	return (E_OK);
}
*/

LONG bnet_readlink (void *dir, char *name, char *buf, WORD size)
{
	BNETX_FD	*dd;
	DIR_SLOT	*local;

	TRACE(("readlink - %L\\%S\r\n", 2, dir, name));
	dd = (BNETX_FD *)dir;
	if (check_dd(dd) < 0)
		return (check_dd(dd));

	if ((local = find_obj (dd, name, 2, FF_SEARCH, 0)) == NULL)
		return (EFILNF);

	if (!is_link (local->de_xattr.mode))
		return(EACCDN);
	if (size < (strlen (&local->de_faddr[2]) + 1L))
		return (ERANGE);

	local->de_xattr.atime = Tgettime();
	local->de_xattr.adate = Tgetdate();
	strcpy (buf, &local->de_faddr[2]);
	return (E_OK);
}

LONG bnet_dcntl (void *dir, char *name, WORD cmd, LONG arg, char **symlink)
{
	BNETX_FD	*dd;

	TRACE(("dcntl - %L\\%S, %L, %L\r\n", 4, dir, name, (LONG)cmd, arg));

	dd = (BNETX_FD *)dir;
	return (work_entry (dd, name, symlink, 1, cmd, arg, dcntl_action));
}

LONG dcntl_action (DIR_SLOT *entry, LONG cmd, LONG arg)
{
	WORD	*timebuf;

	switch ((WORD)cmd)
	{
		case FUTIME:
			if (!waccess(entry))
				return(EACCDN);
			timebuf = (WORD *)arg;
			entry->de_xattr.ctime = Tgettime();
			entry->de_xattr.cdate = Tgetdate();
			if (timebuf != NULL)
			{
				entry->de_xattr.atime = timebuf[0];
				entry->de_xattr.adate = timebuf[1];
				entry->de_xattr.mtime = timebuf[2];
				entry->de_xattr.mdate = timebuf[3];
			}
			else
			{
				entry->de_xattr.atime = entry->de_xattr.mtime =
					entry->de_xattr.ctime;
				entry->de_xattr.adate = entry->de_xattr.mdate =
					entry->de_xattr.cdate;
			}
			return(E_OK);
		default:
			return(EINVFN);
	}
}

/* === From here follows the functions of the Device-driver (XDD) === */

LONG bnet_close (void *file)
{
	BNETX_FD	*fd;
	CM_PACKET	*packet = bnet_f->packet;
	DIR_SLOT	*local;
	LONG		res;

	TRACE(("close - %L\r\n", 1, file));
	fd = (BNETX_FD *)file;

	if (check_fd(fd) < 0)
	{
		return(check_fd(fd));
	}
/*	Cconws (fd->fd_file->de_fname); */

	local = fd->fd_file;
	if (fd->fd_refcnt)
		fd->fd_refcnt--;
	if (!fd->fd_refcnt)
	{
		local->de_faddr = NULL;
		local = NULL;
	}
/*	Cconws (" Closing ...\r\n");	*/
	packet->cmd = 0x3E;
	packet->dummy = fd->fd_han;
	*bnet_f->cur_id = fd->fd_file->de_host;
	if (! req_tx (PACSIZ))
		get_ransw((char *)&res);

	return (E_OK);
}

LONG bnet_read (void *file, LONG count, char *buffer)
{
	BNETX_FD	*fd;
	LONG		read, res;
	CM_PACKET	*packet;
/*	char		test[4];	*/

	TRACE(("read - %L, %L\r\n", 2, file, count));
/*	Cconws ("Reading ...\r\n");	*/
	fd = (BNETX_FD *)file;
	if (check_fd(fd) < 0)
		return(check_fd(fd));
/*	Cconws (fd->fd_file->de_fname);	*/

	if ((fd->fd_mode & (OM_RPERM | OM_EXEC)) == 0)
		return(EACCDN);
	packet = bnet_f->packet;
	packet->cmd = 0x3F;
	packet->path_yn = fd->fd_han;
	*bnet_f->cur_id = fd->fd_file->de_host;
/*
	test[0] = fd->fd_han + '0';
	test[1] = ' ';	test[2] = *bnet_f->cur_id + '0'; test[3] = '\0';
	Cconws (test); Cconws (" Han + host \r\n");
*/
	read = 0L;
	while (count > 0L)
	{
		packet->dummy = (count > SECSIZ) ? (int)SECSIZ : (int)count;
		if (! req_tx (PACSIZ))
		{
			get_ransw((char *)&res);
			if (res > 0)
			{
				memcpy(buffer, bnet_f->t_hdr->buf, res);
				count -= res;
				read += res;
				buffer = &buffer[res];
			}
			else if (res == 0L)
				count = 0L;
			else
				return (EREADF);
		}
		else
			return (ERROR);
	}
	fd->fd_fpos += read;

	return (read);
}

LONG bnet_write (void *file, LONG count, char *buffer)
{
	BNETX_FD	*fd;
	CM_PACKET	*packet;
	LONG		written, res;

	TRACE(("write - %L, %L\r\n", 2, file, count));
	fd = (BNETX_FD *)file;
	if (check_fd(fd) < 0)
		return(check_fd(fd));
	if ((fd->fd_mode & OM_WPERM) == 0)
		return(EACCDN);

	packet = bnet_f->packet;
	packet->cmd = 0x40;
	packet->path_yn = fd->fd_han;
	*bnet_f->cur_id = fd->fd_file->de_host;

	written = 0L;
	while (count > 0L)
	{
		packet->dummy = (count > SECSIZ) ? (int)SECSIZ : (int)count;
		memcpy (packet->buf, buffer, packet->dummy);
		if (! req_tx (packet->dummy + PACSIZ))
		{
			get_ransw ((char *)&res);
			if (res > 0)
			{
				count -= res;
				written += res;
				buffer = &buffer[res];
			}
			else if (res == 0L)
				count = 0L;
			else
				return (EWRITF);
		}
		else
			return (ERROR);
	}

	fd->fd_fpos += written;		/* just in case... */
	return (written);
}

LONG bnet_stat (void *file, MAGX_UNSEL *unselect, WORD rwflag, LONG apcode)
{
	BNETX_FD	*fd;
	LONG		retcode;

	TRACE(("stat - %L, %L, %L, %L\r\n", 4, file, unselect,
		(LONG)rwflag, apcode));
	fd = (BNETX_FD *)file;

	if (check_fd(fd) < 0)
	{
		retcode = check_fd(fd);
		goto rs_exit;
	}

	if (!rwflag && ((fd->fd_mode & (OM_RPERM | OM_EXEC)) == 0))
	{
		retcode = EACCDN;
		goto rs_exit;
	}

	if (rwflag && ((fd->fd_mode & OM_WPERM) == 0))
	{
		retcode = EACCDN;
		goto rs_exit;
	}
	retcode = 1L;
rs_exit:
	if (unselect != NULL)
		unselect->unsel.status = retcode;
	return(retcode);
}

LONG bnet_seek (void *file, LONG where, WORD mode)
{
	BNETX_FD	*fd;
	LONG		new_pos;
	CM_RCSEEK	*packet = (CM_RCSEEK *)bnet_f->packet;
/*	char		test[4]; */

	TRACE(("seek - %L, %L, %L\r\n", 3, file, where, (LONG)mode));
	fd = (BNETX_FD *)file;
	if (check_fd(fd) < 0)
		return (check_fd(fd));
	*bnet_f->cur_id = fd->fd_file->de_host;
	packet->cmd = 0x42;
	packet->path_yn = fd->fd_han;
/*
	packet->timeout = (int)where;
	packet->dummy = (where >> 16);	/* Doesn't swap, uses ASR, too bad...	*/
*/
	packet->dummy = where;
	*(WORD *)packet->buf = mode;
/*
	test[0] = fd->fd_han + '0';
	test[1] = ' ';	test[2] = *bnet_f->cur_id + '0'; test[3] = '\0';
	Cconws (test); Cconws (" Han + host \r\n");
	if (where == -8L)
		Cconws ("Seek negative ! \r\n");
*/
	if (! req_tx (PACSIZ + 2))
		get_ransw ((char *)&new_pos);

	if (new_pos < 0L)
	{
	/*	Cconws (" Seek error \r\n"); */
		return (ERANGE);
	}

	fd->fd_fpos = new_pos;
	return (new_pos);
}


LONG bnet_datime (void *file, WORD *d, WORD setflag)
{
	BNETX_FD	*fd;
	CM_PACKET	*packet;
	WORD		i;
	LONG		res;
	char		*ptr;

	TRACE(("datime - %L, %L\r\n", 2, file, (LONG)setflag));
	fd = (BNETX_FD *)file;
	if (check_fd(fd) < 0)
		return(check_fd(fd));
	packet = bnet_f->packet;
	packet->cmd = 0x57;
	packet->path_yn = fd->fd_han;
	packet->dummy = setflag;
	*bnet_f->cur_id = fd->fd_file->de_host;
	ptr = (char *)d;
	if (setflag)
	{
		for (i = 0; i < 4; i++)
			packet->buf[i] = *(ptr + i);
		if (! req_tx (i + PACSIZ))
			get_ransw ((char *)&res);
	}
	else
	{
		if (! req_tx ( PACSIZ))
			get_ransw ((char *)&res);
		for (i = 0; i < 4; i++)
			*(ptr + i) = bnet_f->t_hdr->buf[i];
	}
	return (res);
}

/*
 * ioctl for open files. Since the file is already closed at the remote
 * it's safe to call bnet_xattr to obtain the (real) xattr.
 */
LONG bnet_ioctl (void *file, WORD cmd, void *buf)
{
	BNETX_FD	*fd;
	CM_PACKET	*packet;
	WORD		*timebuf;
	LONG		*avail, res;
	XATTR		*xattr, *rattr;

	TRACE(("ioctl - %L, %L, %L\r\n", 3, file, (LONG)cmd,  buf));
	fd = (BNETX_FD *)file;
	if (check_fd(fd) < 0)
		return(check_fd(fd));

	avail = (LONG *)buf;
	switch (cmd)
	{
		case FUTIME:
			if (!waccess (fd->fd_file))
				return (EACCDN);
			timebuf = (WORD *)buf;
			fd->fd_file->de_xattr.ctime = Tgettime();
			fd->fd_file->de_xattr.cdate = Tgetdate();
			if (timebuf != NULL)
			{
				fd->fd_file->de_xattr.atime = timebuf[0];
				fd->fd_file->de_xattr.adate = timebuf[1];
				fd->fd_file->de_xattr.mtime = timebuf[2];
				fd->fd_file->de_xattr.mdate = timebuf[3];
			}
			else
			{
				fd->fd_file->de_xattr.atime =
					fd->fd_file->de_xattr.mtime =
					fd->fd_file->de_xattr.ctime;
				fd->fd_file->de_xattr.adate =
					fd->fd_file->de_xattr.mdate =
					fd->fd_file->de_xattr.cdate;
			}
			return (E_OK);
		case FIONREAD:
			if ((fd->fd_mode & (OM_RPERM | OM_EXEC)) == 0)
				return (EACCDN);	/* Thomas	*/
			*avail = fd->fd_file->de_xattr.size - fd->fd_fpos;
			return (E_OK);
		case FIONWRITE:
			if ((fd->fd_mode & OM_WPERM) == 0)
				return (EACCDN);
			*avail = SECSIZ;
			return (E_OK);
		case FSTAT:
			packet = bnet_f->packet;
			packet->cmd = FIL_CNTR;
			packet->path_yn = fd->fd_han;
			packet->dummy = cmd;
			*bnet_f->cur_id = fd->fd_file->de_host;
			if (! req_tx (PACSIZ) )
			{
				get_ransw ((char *)&res);
				if (res >= 0L)
				{
					xattr = (XATTR *)buf;
					rattr = (XATTR *)bnet_f->t_hdr->buf;
					xattr->mode = rattr->mode;
					xattr->size = rattr->size;
				/*	*xattr = fd->fd_file->de_xattr;	*/
					return (E_OK);
				}
			}
			return (EACCDN);
		default:
			return (EINVFN);	/* No more supported */
	}
}

/*
 * Well, this is slow and probably not needed, but...
 */

LONG bnet_getc (void *file, WORD mode)
{
	BNETX_FD	*fd;
	UBYTE		dummy;

	TRACE(("getchar - %L, %L\r\n", 2, file, (LONG)mode));
	fd = (BNETX_FD *)file;
	if (check_fd(fd) < 0)
		return (check_fd(fd));
	if (bnet_read (file, 1L, (char *)&dummy) != 1L)
		return (0xff1aL);

	return ((ULONG)dummy);
}

LONG bnet_getline (void *file, char *buf, WORD mode, LONG size)
{
	BNETX_FD	*fd;
	LONG		dummy,
				count;

	TRACE(("getline - %L, %L, %L\r\n", 3, file, size, (LONG)mode));
	fd = (BNETX_FD *)file;
	if (check_fd(fd) < 0)
		return(0L);

	for (count = 0L;; count++)
	{
/* Break, if the max num of characters have been read */
		if (count == size)
			return (count);

		dummy = bnet_getc (file, 0);
		TRACE(("getline: count = %L, gelesenes Byte: %L\r\n", 2,
			count, dummy));

		if (dummy == 0xff1aL)
		{
			buf[count] = 0;
			return (count);
		}

		if ((dummy == 0xdL) || (dummy == 0xaL))
		{
			if (dummy == 0xdL)
				bnet_getc (file, 0);
			buf[count] = 0;
			return(count);
		}
		buf[count] = dummy;
	}
}

LONG bnet_putc (void *file, WORD mode, LONG value)
{
	BNETX_FD	*fd;
	char		dummy;

	TRACE(("putc - %L, %L, %L\r\n", 3, file, (LONG)mode, value));
	fd = (BNETX_FD *)file;
	if (check_fd(fd) < 0)
		return (check_fd(fd));
	dummy = (char)value;
	return (bnet_write (file, 1L, &dummy));
}
#pragma warn .par

/* End of BNET_XFS Functions */
