/*
 * $Id: netfs.c,v 1.1 1993/12/02 20:45:46 Rhialto Exp $
 * $Log: netfs.c,v $
 * Revision 1.1  1993/12/02  20:45:46  Rhialto
 * Initial revision
 *
 * Actually convert file system actions into network packets.
 * All functions assume that the packet type has been set already,
 * so that the code for some packets can be shared.
 * Read and Write are an exception because they may split the
 * operation in multiple packets.
 */

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

#ifdef DEBUG
#   include "syslog.h"
#else
#   define	debug(x)
#endif

Prototype Packet	  ReqPacket;
Prototype ULONG 	  Validation;
Prototype ULONG 	  MyValidation;

Packet		ReqPacket;
ULONG		Validation;
ULONG		MyValidation;
NetFileLock    *RootLock;
struct List	NameList;

struct Entry {
    struct Node     e_Node;
};

/*
 * Remember device parts of names DH0:xx/yy,
 * without the colons.
 */

void
AddRemoteName(char *bstr)
{
    struct Entry   *e;
    int 	    len;
    char	   *n;
    static int	    nestcount;

    debug(("AddRemoteName %d '%.*s'\n", nestcount, bstr[0], bstr+1));
    if (nestcount > 0)
	return;

    nestcount++;

    n = memchr(bstr + 1, ':', (unsigned char)bstr[0]);
    if (n == NULL) {
	/* Can't happen */
	len = bstr[0];
    } else
	len = n - (bstr + 1);

    e = (void *)&NameList;
    while (e = NextNode((struct MinNode *)e)) {
	n = e->e_Node.ln_Name;

	if (n[0] == len && strnicmp(n+1, bstr+1, len) == 0) {
	    debug(("AddName: Found it\n"));
	    goto end;		/* Found it */
	}
    }

    debug(("AddName: A new name\n"));

    e = dosalloc(sizeof(struct Entry) + 1 + len);
    if (e == NULL)
	goto end;
    n = e + 1;
    e->e_Node.ln_Name = n;
    n[0] = len;
    memcpy(n + 1, bstr + 1, len);

    AddTail(&NameList, &e->e_Node);

    /* Now check if it was a reasonable thing to do... */
    {
	NetFileLock    *nfl;
	static Packet	save;

	save = ReqPacket;
	ReqPacket.p_Action = ACTION_LOCATE_OBJECT;
	if (nfl = NetLock(RootLock, n, SHARED_LOCK)) {
	    ReqPacket.p_Action = ACTION_FREE_LOCK;
	    NetUnLock(nfl);
	} else {
	    Remove(e);
	    dosfree((void *)e);
	}
	ReqPacket = save;
    }

end:
    nestcount--;
}

char *
CopyString(char *to, char *from)
{
    int 	    len = from[0];

    debug(("CopyString '%.*s'\n", from[0], from+1));
    memcpy(to, from, 1 + len);
    to = to + 1 + len;
    *to++ = '\0';
    /* Align to longword for conversion to BPTR by the server */
    to = (char *)(((ULONG)to + 3) & ~3);

    return to;
}

/*
 * Some packets use C style 0-terminated strings.
 * Convert them to counted strings, for consisteny.
 */
char *
CopyCString(char *to, char *from)
{
    int 	    len = strlen(from);

    to[0] = len;
    memcpy(to + 1, from, len);
    to = to + 1 + len;
    *to++ = '\0';
    /* Align to longword for conversion to BPTR by the server */
    to = (char *)(((ULONG)to + 3) & ~3);

    return to;
}

void *
memchr(const void *cs, int c, size_t n)
{
    while (n > 0) {
	if (*(unsigned char *)cs == (unsigned char)c)
	    return cs;
	cs = (char *)cs + 1;
	n--;
    }
    return NULL;
}

void
bstrcpy(unsigned char *d, unsigned char *s)
{
    memcpy(d, s, 1 + s[0]);
}

int NameIsNetRoot;
int NameIsRemoteDevice;

char *
CopyName(char *to, char *from, NetFileLock *parent)
{
    int 	    len = from[0];
    char	   *p;

    debug(("CopyName '%.*s'\n", from[0], from+1));
    NameIsNetRoot = 0;
    NameIsRemoteDevice = 0;
    if (p = memchr(from + 1, ':', len)) {
	/* NET:something -> something */
	p++;
	len -= (p - (from + 1));
	to[0] = len;
	if (len)
	    memcpy(to + 1, p, len);
    } else
	memcpy(to, from, 1 + len);
    if (parent == RootLock) {
	NameIsNetRoot = (len == 0);
	p = memchr(to + 1, '/', len);
	if (p) {
	    /* dh0/something -> dh0:something */
	    p[0] = ':';
	} else {
	    if (len > 5 && strncmp(&to[1+len-5], ".info", 5) == 0) {
		/* NET:DH0.info -> DH0:Node.rinfo */
		strcpy(&to[1+len-5], ":Node.rinfo");
		len += 6;
		to[0] = len;
	    } else {
		/* dh0 -> dh0: */
		to[0] = ++len;
		to[len] = ':';
		NameIsRemoteDevice = 1;
	    }
	}
	if (!NameIsNetRoot) {
	    AddRemoteName(to);
	}
    }
    to = to + 1 + len;
    *to++ = '\0';
    /* Align to longword for conversion to BPTR by the server */
    to = (char *)(((ULONG)to + 3) & ~3);

    return to;
}

NetFileLock *
ValidLock(NetFileLock *netfl)
{
    if (netfl == NULL)
	return RootLock;

    if (netfl->nfl_Validation == Validation)
	return netfl;

    if (netfl->nfl_Validation == 0)
	return netfl;

    error = ERROR_DEVICE_NOT_MOUNTED;
    return NULL;
}

int
ValidFile(NetFileHandle *netfh)
{
    return netfh && netfh->nfh_Validation == Validation;
}

NetFileLock *
MakeNetLock(ULONG fl)
{
    NetFileLock *netfl;

    if (netfl = AllocMem(sizeof(*netfl), MEMF_PUBLIC|MEMF_CLEAR)) {
	netfl->nfl_FileLock = fl;
	netfl->nfl_Validation = Validation;
    }
#ifdef DEBUG
    debug(("MakeNetLock %x from %x\n", netfl, fl));
    if (fl == 0)
	debug(("!!! 0 remote lock !!!\n"));
#endif
    return netfl;
}

void
FreeNetLock(NetFileLock *netfl)
{
    if (netfl && netfl != RootLock)
	FreeMem(netfl, sizeof(*netfl));
}

NetFileHandle *
MakeNetFile(ULONG fh)
{
    NetFileHandle *netfh;

    if (netfh = AllocMem(sizeof(*netfh), MEMF_PUBLIC|MEMF_CLEAR)) {
	netfh->nfh_FileHandle = fh;
	netfh->nfh_Validation = Validation;
    }
    debug(("MakeNetFile %x\n", netfh));
    return netfh;
}

void
FreeNetFile(NetFileHandle *netfh)
{
    if (netfh)
	FreeMem(netfh, sizeof(*netfh));
}

/* ---------------------------------------------------------------------- */

Prototype NetFileLock *NetLock(NetFileLock *parent, char *name, ULONG mode);

NetFileLock *
NetLock(NetFileLock *parent, char *name, ULONG mode)
{
    char	   *end;

    if (parent = ValidLock(parent)) {
	ReqPacket.p_Arg[0] = parent->nfl_FileLock;
	ReqPacket.p_Arg[1] = mode;
	end = CopyName((char *)&ReqPacket.p_Arg[2], name, parent);

	debug(("NetLock: %x %s %d\n", parent, (char *)&ReqPacket.p_Arg[2]+1,
		mode));

	if (NameIsNetRoot)
	    return RootLock;

	if (SendRecv(&ReqPacket, end - &ReqPacket, &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    if (ReqPacket.p_Res1) {
		return MakeNetLock(ReqPacket.p_Res1);
	    }
	}
    }
    return NULL;
}

Prototype void NetUnLock(NetFileLock *netfl);

void
NetUnLock(NetFileLock *netfl)
{
    if (netfl) {
	if ((netfl = ValidLock(netfl))) {
	    if (netfl == RootLock)
		return;

	    ReqPacket.p_Arg[0] = netfl->nfl_FileLock;

	    (void)SendRecv(&ReqPacket, REQSIZE(1), &ReqPacket, STDREPLY);
	}
	FreeNetLock(netfl);
    }
}

Prototype NetFileLock *NetDupLock(NetFileLock *netfl);

NetFileLock *
NetDupLock(NetFileLock *netfl)
{
    if (netfl = ValidLock(netfl)) {
	if (netfl == RootLock)
	    return RootLock;

	ReqPacket.p_Arg[0] = netfl->nfl_FileLock;

	if (SendRecv(&ReqPacket, REQSIZE(1), &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    if (ReqPacket.p_Res1) {
		return MakeNetLock(ReqPacket.p_Res1);
	    }
	}
    }
    return NULL;
}

Prototype NetFileLock *NetCreateDir(NetFileLock *parent, char *name);

NetFileLock *
NetCreateDir(NetFileLock *parent, char *name)
{
    char	   *end;

    if (parent = ValidLock(parent)) {
	ReqPacket.p_Arg[0] = parent->nfl_FileLock;
	end = CopyName((char *)&ReqPacket.p_Arg[1], name, parent);

	if (SendRecv(&ReqPacket, end - &ReqPacket, &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    if (ReqPacket.p_Res1) {
		return MakeNetLock(ReqPacket.p_Res1);
	    }
	}
    }
    return NULL;
}

Prototype ULONG NetDeleteFile(NetFileLock *parent, char *name);

ULONG
NetDeleteFile(NetFileLock *parent, char *name)
{
    char	   *end;

    if (parent = ValidLock(parent)) {
	ReqPacket.p_Arg[0] = parent->nfl_FileLock;
	end = CopyName((char *)&ReqPacket.p_Arg[1], name, parent);

	if (SendRecv(&ReqPacket, end - &ReqPacket, &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

Prototype NetFileLock *NetParentDir(NetFileLock *netfl);

NetFileLock *
NetParentDir(NetFileLock *netfl)
{
    if (netfl == NULL) {
	error = ERROR_OBJECT_NOT_FOUND;
    } else if (netfl = ValidLock(netfl)) {
	if (netfl == RootLock)
	    return NULL;

	ReqPacket.p_Arg[0] = netfl->nfl_FileLock;

	if (SendRecv(&ReqPacket, REQSIZE(1), &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    if (ReqPacket.p_Res1)
		return MakeNetLock(ReqPacket.p_Res1);
	    else if (error == 0)
		return RootLock;
	}
    }
    return NULL;
}

Prototype ULONG NetSameLock(NetFileLock *lock1, NetFileLock *lock2);

ULONG
NetSameLock(NetFileLock *lock1, NetFileLock *lock2)
{
    if ((lock1 = ValidLock(lock1)) && (lock2 = ValidLock(lock2))) {
	if (lock1 == lock2) {
	    debug(("NetSameLock: same local ptr %x\n", lock1));
	    return DOSTRUE;
	}

#if 1
	if (lock1 == RootLock || lock2 == RootLock) {
	    /*error = ERROR_DEVICE_NOT_MOUNTED;*/
	    error = ERROR_INVALID_LOCK;
	    return DOSFALSE;
	}
#endif

	if (lock1->nfl_FileLock == lock2->nfl_FileLock) {
	    debug(("NetSameLock: same remote ptr %x\n", lock1->nfl_FileLock));
	    return DOSTRUE;
	}

	debug(("NetSameLock: compare remote %x %x\n", lock1->nfl_FileLock, lock2->nfl_FileLock));
	ReqPacket.p_Arg[0] = lock1->nfl_FileLock;
	ReqPacket.p_Arg[1] = lock2->nfl_FileLock;

	if (SendRecv(&ReqPacket, REQSIZE(2), &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

ULONG
NetRelabel(NetFileLock *slock, char *sname, NetFileLock *dlock, char *dname)
{

    char	   *end;
    char	   *colon;

    ReqPacket.p_Action = ACTION_RENAME_DISK;
    end = CopyName((char *)&ReqPacket.p_Arg[0], sname, slock);
    colon = end;
    end = CopyName((char *)end,                 dname, dlock);

    /* Remove colon from new name */
    if (colon[colon[0]] == ':') {
	colon[colon[0]] = '\0';
	colon[0]--;
    }

    if (SendRecv(&ReqPacket, end - &ReqPacket, &ReqPacket, STDREPLY) == 0) {
	error = ReqPacket.p_Res2;
	return ReqPacket.p_Res1;
    }
}

Prototype ULONG NetRename(NetFileLock *slock, char *sname, NetFileLock *dlock, char *dname);

ULONG
NetRename(NetFileLock *slock, char *sname, NetFileLock *dlock, char *dname)
{
    char	   *end;
    char	    root;
    char	    dev;

    if ((slock = ValidLock(slock)) && (dlock = ValidLock(dlock))) {
	ReqPacket.p_Arg[0] = slock->nfl_FileLock;
	ReqPacket.p_Arg[1] = dlock->nfl_FileLock;
	end = CopyName((char *)&ReqPacket.p_Arg[2], sname, slock);
	root = NameIsNetRoot;
	dev  = NameIsRemoteDevice;
	end = CopyName((char *)end,                 dname, dlock);
	root += NameIsNetRoot;
	dev  += NameIsRemoteDevice;

	if (dev == 2)
	    return NetRelabel(slock, sname, dlock, dname);
	if (root || dev) {
	    error = ERROR_RENAME_ACROSS_DEVICES;
	    return DOSFALSE;
	}

	if (SendRecv(&ReqPacket, end - &ReqPacket, &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

Prototype ULONG NetSetProtect(NetFileLock *parent, char *name, ULONG protect);

ULONG
NetSetProtect(NetFileLock *parent, char *name, ULONG protect)
{
    char	   *end;

    if (parent = ValidLock(parent)) {
	ReqPacket.p_Arg[0] = parent->nfl_FileLock;
	ReqPacket.p_Arg[1] = protect;
	end = CopyName((char *)&ReqPacket.p_Arg[2], name, parent);

	if (SendRecv(&ReqPacket, end - &ReqPacket, &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

Prototype ULONG NetExamine(NetFileLock *netfl, struct FileInfoBlock *fib);

ULONG
NetExamine(NetFileLock *netfl, struct FileInfoBlock *fib)
{
    if (netfl = ValidLock(netfl)) {
	if (netfl == RootLock) {
	    char	   *p;

	    memset(fib, 0, sizeof(*fib));
	    /* Includes fib_DiskKey = 0 */
	    fib->fib_DirEntryType =
	    fib->fib_EntryType = ST_ROOT;
	    p = BTOC(VolNode->dl_Name);
	    bstrcpy(&fib->fib_FileName[0], p);
	    fib->fib_Date = VolNode->dl_VolumeDate;

	    return DOSTRUE;
	}

	ReqPacket.p_Arg[0] = netfl->nfl_FileLock;

	if (SendRecv2(&ReqPacket, REQSIZE(1), NULL, 0,
		      &ReqPacket, STDREPLY, fib, sizeof(*fib)) == 0) {
#ifdef DEBUG
	{
	    debug(("key %x size %d name '%.*s'\n", fib->fib_DiskKey, fib->fib_Size,
		    fib->fib_FileName[0], &fib->fib_FileName[1]));
	}
#endif
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

Prototype ULONG NetExNext(NetFileLock *netfl, struct FileInfoBlock *fib);

/*
 * For ExNext, (a part of) the FileInfoBlock must be sent
 * with the request. To be flexible, here is a define for
 * how much we actually transmit. In this case, everything
 * up to the end of the file name plus a bit of slop to
 * survive round-down errors.
 * See also notes in the server.
 */

#define EXNEXTFIBSIZE(f) (offsetof(struct FileInfoBlock, fib_FileName[0]) + \
			  f->fib_FileName[0] + 2)

ULONG
NetExNext(NetFileLock *netfl, struct FileInfoBlock *fib)
{
    if (netfl = ValidLock(netfl)) {
	if (netfl == RootLock) {
	    struct Entry   *e;
	    int 	    i;

	    if (fib->fib_DiskKey > 0 && fib->fib_DirEntryType == ST_USERDIR) {
		fib->fib_DirEntryType =
		fib->fib_EntryType = ST_FILE;
		i = fib->fib_FileName[0];
		strcpy(&fib->fib_FileName[1+i], ".info");
		fib->fib_FileName[0] = i + 5;

		return DOSTRUE;
	    }

	    e = NextNode((struct MinNode *)&NameList);
	    for (i = fib->fib_DiskKey; e && i > 0; i--)
		e = NextNode((struct MinNode *)e);

	    if (e) {
		fib->fib_DiskKey++;
		fib->fib_DirEntryType =
		fib->fib_EntryType = ST_USERDIR;
		bstrcpy(&fib->fib_FileName[0], e->e_Node.ln_Name);

		return DOSTRUE;
	    }

	    error = ERROR_NO_MORE_ENTRIES;
	    return DOSFALSE;
	}

	ReqPacket.p_Arg[0] = netfl->nfl_FileLock;

	if (SendRecv2(&ReqPacket, REQSIZE(1), fib, EXNEXTFIBSIZE(fib),
		      &ReqPacket, STDREPLY, fib, sizeof(*fib)) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}
Prototype ULONG NetDiskInfo(struct InfoData *id);

ULONG
NetDiskInfo(struct InfoData *id)
{
    memset(id, 0, sizeof(*id));
    id->id_DiskState = ID_VALIDATED;
    id->id_NumBlocks = 1;
    id->id_NumBlocksUsed = 1;
    id->id_BytesPerBlock = MAXDATA;
    id->id_DiskType = ID_DOS_DISK;
    id->id_VolumeNode = CTOB(VolNode);
    id->id_InUse = 1;

    return DOSTRUE;
}

Prototype ULONG NetInfo(NetFileLock *netfl, struct InfoData *id);

ULONG
NetInfo(NetFileLock *netfl, struct InfoData *id)
{
    if (netfl == RootLock)
	return NetDiskInfo(id);

    if (netfl = ValidLock(netfl)) {
	ReqPacket.p_Arg[0] = netfl->nfl_FileLock;

	if (SendRecv2(&ReqPacket, REQSIZE(1), NULL, 0,
		      &ReqPacket, STDREPLY, id, sizeof(*id)) == 0) {
	    id->id_VolumeNode = NULL;
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

Prototype ULONG NetSetComment(NetFileLock *parent, char *name, char *comment);

ULONG
NetSetComment(NetFileLock *parent, char *name, char *comment)
{
    char	   *end;

    if (parent = ValidLock(parent)) {
	ReqPacket.p_Arg[0] = parent->nfl_FileLock;
	end = CopyName((char *)&ReqPacket.p_Arg[1], name, parent);
	end = CopyString(end,               comment);

	if (SendRecv(&ReqPacket, end - &ReqPacket, &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

Prototype ULONG NetSetDate(NetFileLock *parent, char *name, struct DateStamp *ds);

ULONG
NetSetDate(NetFileLock *parent, char *name, struct DateStamp *ds)
{
    char	   *end;

    if (parent = ValidLock(parent)) {
	ReqPacket.p_Arg[0] = parent->nfl_FileLock;
	ReqPacket.p_Arg[1] = ds->ds_Days;
	ReqPacket.p_Arg[2] = ds->ds_Minute;
	ReqPacket.p_Arg[3] = ds->ds_Tick;
	end = CopyName((char *)&ReqPacket.p_Arg[4], name, parent);

	if (SendRecv(&ReqPacket, end - &ReqPacket, &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

Prototype NetFileHandle *NetOpen(NetFileLock *parent, char *name, ULONG mode);

NetFileHandle *
NetOpen(NetFileLock *parent, char *name, ULONG mode)
{
    char	   *end;

    if (parent = ValidLock(parent)) {
	ReqPacket.p_Arg[0] = parent->nfl_FileLock;
	end = CopyName((char *)&ReqPacket.p_Arg[1], name, parent);
	/* mode is actually the packet type */

	debug(("NetOpen: %x %s %d\n", parent, (char *)&ReqPacket.p_Arg[1]+1,
		mode));

	if (SendRecv(&ReqPacket, end - &ReqPacket, &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    if (ReqPacket.p_Res1) {
		return MakeNetFile(ReqPacket.p_Res1);
	    }
	}
    }
    return NULL;
}

Prototype LONG NetRead(NetFileHandle *netfh, char *buffer, LONG length);

LONG
NetRead(NetFileHandle *netfh, char *buffer, LONG length)
{
    LONG	    done = 0;

    if (ValidFile(netfh)) {
	while (length > 0) {
	    ReqPacket.p_Action = ACTION_READ;
	    ReqPacket.p_Arg[0] = netfh->nfh_FileHandle;
	    ReqPacket.p_Arg[1] = min(length, MAXDATA);

	    if (SendRecv2(&ReqPacket, REQSIZE(2), NULL, 0,
			  &ReqPacket, STDREPLY, buffer, ReqPacket.p_Arg[1]) != 0)
		break;

	    if ((long)ReqPacket.p_Res1 <= 0)
		break;

	    length -= ReqPacket.p_Res1;
	    buffer += ReqPacket.p_Res1;
	    done   += ReqPacket.p_Res1;
	}

	error = ReqPacket.p_Res2;
	return done;
    }

    return -1;
}

Prototype LONG NetWrite(NetFileHandle *netfh, char *buffer, LONG length);

LONG
NetWrite(NetFileHandle *netfh, char *buffer, LONG length)
{
    LONG	    done = 0;

    if (ValidFile(netfh)) {
	while (length > 0) {
	    LONG	    thislength = min(length, MAXDATA);

	    ReqPacket.p_Action = ACTION_WRITE;
	    ReqPacket.p_Arg[0] = netfh->nfh_FileHandle;
	    ReqPacket.p_Arg[1] = thislength;

	    if (SendRecv2(&ReqPacket, REQSIZE(2), buffer, thislength,
			  &ReqPacket, STDREPLY, NULL, 0) != 0)
		break;

	    if (ReqPacket.p_Res1 == -1)
		break;

	    done   += ReqPacket.p_Res1;

	    if (ReqPacket.p_Res1 != thislength)
		break;

	    length -= ReqPacket.p_Res1;
	    buffer += ReqPacket.p_Res1;
	}

	error = ReqPacket.p_Res2;
	return done;
    }

    return -1;
}

Prototype ULONG NetClose(NetFileHandle *netfh);

ULONG
NetClose(NetFileHandle *netfh)
{
    if (ValidFile(netfh)) {
	ReqPacket.p_Arg[0] = netfh->nfh_FileHandle;

	if (SendRecv(&ReqPacket, REQSIZE(1), &ReqPacket, STDREPLY) == 0) {
	    if (ReqPacket.p_Res1 && (error = ReqPacket.p_Res2) == 0) {
		FreeNetFile(netfh);
	    }
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

Prototype ULONG NetSeek(NetFileHandle *netfh, ULONG position, ULONG mode);

ULONG
NetSeek(NetFileHandle *netfh, ULONG position, ULONG mode)
{
    if (ValidFile(netfh)) {
	ReqPacket.p_Arg[0] = netfh->nfh_FileHandle;
	ReqPacket.p_Arg[1] = position;
	ReqPacket.p_Arg[2] = mode;

	if (SendRecv(&ReqPacket, REQSIZE(3), &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

/*
 * Target can be eith
 */

Prototype ULONG NetMakeLink(NetFileLock *parent, char *name, void *target, ULONG mode);

ULONG
NetMakeLink(NetFileLock *parent, char *name, void *target, ULONG soft)
{
    char	   *end;

    if (parent = ValidLock(parent)) {
	ReqPacket.p_Arg[0] = parent->nfl_FileLock;
	ReqPacket.p_Arg[1] = soft;
	if (soft) {
	    /* XXX Is this a BCPL or C string? */
	    end = CopyCString((char *)&ReqPacket.p_Arg[2], target);
	} else {
	    NetFileLock *tlock = ValidLock((NetFileLock *)target);

	    debug(("MakeLink hard %s -> %x\n", name, target));
	    if (tlock == 0) {
		error = ERROR_INVALID_LOCK;
		return DOSFALSE;
	    }
	    ReqPacket.p_Arg[2] = tlock->nfl_FileLock;
	    end = &ReqPacket.p_Arg[3];
	}
	end = CopyName(end, name, parent);

	if (SendRecv(&ReqPacket, end - &ReqPacket, &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return NULL;
}

Prototype ULONG NetReadLink(NetFileLock *parent, char *name, char *target, ULONG length);

ULONG
NetReadLink(NetFileLock *parent, char *name, char *target, ULONG length)
{
    char	   *end;

    if (parent = ValidLock(parent)) {
	ReqPacket.p_Arg[0] = parent->nfl_FileLock;
	ReqPacket.p_Arg[1] = length;
	end = CopyCString((char *)&ReqPacket.p_Arg[2], name);   /* XXX should be CopyCName */

	if (SendRecv2(&ReqPacket, end - &ReqPacket, NULL, 0,
		      &ReqPacket, STDREPLY, target, length) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return NULL;
}
Prototype ULONG NetSetFileSize(NetFileHandle *netfh, ULONG position, ULONG mode);

ULONG
NetSetFileSize(NetFileHandle *netfh, ULONG position, ULONG mode)
{
    /* Same arguments and result */
    return NetSeek(netfh, position, mode);
}

Prototype NetFileHandle *NetOpenFromLock(NetFileLock *parent);

NetFileHandle *
NetOpenFromLock(NetFileLock *parent)
{
    if (parent = ValidLock(parent)) {
	ReqPacket.p_Arg[0] = parent->nfl_FileLock;

	if (SendRecv(&ReqPacket, REQSIZE(1), &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    if (ReqPacket.p_Res1) {
		return MakeNetFile(ReqPacket.p_Res1);
	    }
	}
    }
    return NULL;
}

Prototype NetFileLock *NetParentOfFH(NetFileHandle *netfh);

NetFileLock *
NetParentOfFH(NetFileHandle *netfh)
{
    if (ValidFile(netfh)) {
	ReqPacket.p_Arg[0] = netfh->nfh_FileHandle;

	if (SendRecv(&ReqPacket, REQSIZE(1), &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    if (ReqPacket.p_Res1) {
		return MakeNetLock(ReqPacket.p_Res1);
	    }
	}
    }
    return NULL;
}

Prototype ULONG NetChangeMode(ULONG type, void *object, ULONG mode);

ULONG
NetChangeMode(ULONG type, void *object, ULONG mode)
{
    ULONG	    object2 = 0;

    switch (type) {
    case CHANGE_FH:
	if (object = ValidLock(object))
	    object2 = ((NetFileHandle *)object)->nfh_FileHandle;
	break;
    case CHANGE_LOCK:
	if (ValidFile(object))
	    object2 = ((NetFileLock *)object)->nfl_FileLock;
	break;
    }
    if (object2) {
	ReqPacket.p_Arg[0] = type;
	ReqPacket.p_Arg[1] = object2;
	ReqPacket.p_Arg[2] = mode;

	if (SendRecv(&ReqPacket, REQSIZE(3), &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }

    error = ERROR_OBJECT_WRONG_TYPE;
    return DOSFALSE;

}

Prototype NetFileLock *NetDupLockFromFH(NetFileHandle *netfh);

NetFileLock *
NetDupLockFromFH(NetFileHandle *netfh)
{
    return NetParentOfFH(netfh);
}

Prototype ULONG NetExamineFH(NetFileHandle *netfh, struct FileInfoBlock *fib);

ULONG
NetExamineFH(NetFileHandle *netfh, struct FileInfoBlock *fib)
{
    if (ValidFile(netfh)) {
	ReqPacket.p_Arg[0] = netfh->nfh_FileHandle;

	if (SendRecv2(&ReqPacket, REQSIZE(1), NULL, 0,
		      &ReqPacket, STDREPLY, fib, sizeof(*fib)) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return DOSFALSE;
}

void
NetLockRecordReply(struct DosPacket *dp, ULONG res1, ULONG res2)
{
    debug(("NetLockRecordReply %x %x\n", res1, res2));

    dp->dp_Res1 = res1;
    dp->dp_Res2 = res2;
    returnpacket(dp);
}

Prototype void NetLockRecord(struct DosPacket *dp, NetFileHandle *netfh, LONG start, LONG length, LONG mode, LONG timeout);

void
NetLockRecord(struct DosPacket *dp, NetFileHandle *netfh, LONG start,
	      LONG length, LONG mode, LONG timeout)
{
    debug(("NetLockRecord %x %x->%x\n", dp, netfh, netfh->nfh_FileHandle));

    if (ValidFile(netfh)) {
	ReqPacket.p_Arg[0] = netfh->nfh_FileHandle;
	ReqPacket.p_Arg[1] = start;
	ReqPacket.p_Arg[2] = length;
	ReqPacket.p_Arg[3] = mode;
	ReqPacket.p_Arg[4] = timeout;
	ReqPacket.p_Arg[5] = dp;	    /* extra */

	if (Send2(&ReqPacket, REQSIZE(6), NULL, 0) != 0) {
	    NetLockRecordReply(dp, DOSFALSE, ERROR_NO_FREE_STORE);
	}
    } else {
	debug(("Bad NetFH\n"));
	NetLockRecordReply(dp, DOSFALSE, ERROR_OBJECT_WRONG_TYPE);
    }
}

Prototype ULONG NetFreeRecord(NetFileHandle *netfh, LONG start, LONG length);

ULONG
NetFreeRecord(NetFileHandle *netfh, LONG start, LONG length)
{
    if (ValidFile(netfh)) {
	ReqPacket.p_Arg[0] = netfh->nfh_FileHandle;
	ReqPacket.p_Arg[1] = start;
	ReqPacket.p_Arg[2] = length;

	if (SendRecv(&ReqPacket, REQSIZE(3), &ReqPacket, STDREPLY) == 0) {
	    error = ReqPacket.p_Res2;
	    return ReqPacket.p_Res1;
	}
    }
    return NULL;
}

/* ---------------------------------------------------------------------- */

Prototype void DoAsyncReply(Packet *pkt);

void
DoAsyncReply(Packet *pkt)
{
    debug(("DoAsyncReply\n"));

    switch (pkt->p_Data[0]) {
    case ACTION_LOCK_RECORD:
	NetLockRecordReply((void *)pkt->p_Data[1], pkt->p_Res1, pkt->p_Res2);
	break;
    default:
	debug(("Weird AsyncReply, %d\n", pkt->p_Data[0]));
    }
}

/* ---------------------------------------------------------------------- */

Prototype ULONG OpenNetFS(void);

ULONG
OpenNetFS(void)
{
    ULONG	    error;

    debug(("OpenNetFS\n"));
    error = OpenNetwork();
    if (error == 0) {
	RootLock = MakeNetLock(0);
	RootLock->nfl_Validation = 0;
	NewList(&NameList);
    }
    debug(("OpenNetFS done\n"));
    return error;
}

Prototype ULONG CloseNetFS(void);

ULONG
CloseNetFS(void)
{
    NetFileLock *nfl;

    debug(("CloseNetFS\n"));
    CloseNetwork();

    nfl = RootLock;
    RootLock = NULL;
    FreeNetLock(nfl);

    return 0;
}
