/*-
 * $Id: pack.c,v 1.1 1993/12/02 20:45:46 Rhialto Exp $
 * $Log: pack.c,v $
 * Revision 1.1  1993/12/02  20:45:46  Rhialto
 * Initial revision
 *
 *
 *  Originally:
 *
 *	DOSDEVICE.C	    V1.10   2 November 1987
 *
 *	EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C	PUBLIC DOMAIN.
 *
 *	By Matthew Dillon.
 *
 *  This has been stripped and refilled with network filesystem code
 *  by Olaf Seibert.
 *
 *  This code is (C) Copyright 1993 by Olaf Seibert. All rights reserved.
 *  May not be used or copied without a licence.
-*/

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

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


#define NETFL(something)     ((NetFileLock *)(something))
#define NETFH(something)     ((NetFileHandle *)(something))

Prototype struct ExecBase *SysBase;
Prototype struct MsgPort *DosPort;
Prototype struct DeviceNode *DevNode;
Prototype struct DeviceList *VolNode;
Prototype long	    UnitNr;
Prototype char	   *DevName;
Prototype ULONG     DevFlags;
Prototype struct DosPacket *DosPacket;
Prototype ULONG     error;

Local BPTR MakeFileLock(NetFileLock *netfl, struct FileLock *fl, long mode);

/*
 * Since this code might be called several times in a row without being
 * unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!!  This also goes
 * for any global/static assignments that might be changed by running the
 * code.
 */

struct MsgPort	*DosPort;	/* Our DOS port... */
struct DeviceNode *DevNode;	/* Our DOS node.. created by DOS for us */
struct DeviceList *VolNode;	/* Device List structure for our volume
				 * node */

struct DosLibrary *DOSBase;	/* DOS library base */
long		WaitMask;	/* The signal mask to wait for */
long		UnitNr; 	/* From */
char	       *DevName;	/*   the */
ULONG		DevFlags;	/*     mountlist */
struct DosPacket *DosPacket;	/* For the SystemRequest pr_WindowPtr */
long		OpenCount;	/* How many open files/locks there are */
ULONG		error;


/*
 * Don't call the entry point main().  This way, if you make a mistake
 * with the compile options you'll get a link error.
 */

Prototype void netfilesyshandler(void);

void
handlermain(void)
{
    register struct DosPacket *packet;
    struct Message *msg;
    short	    done;

    /*
     * Initialize all global variables.  SysBase MUST be initialized
     * before we can make Exec calls.  AbsExecBase is a library symbol
     * referencing absolute memory location 4.
     */

    DOSBase = OpenLibrary("dos.library", 0L);

#ifdef DEBUG
    /*
     * Initialize debugging code as soon as possible. Only SysBase required.
     */

    initsyslog();
#endif				/* DEBUG */

    DosPort = &((struct Process *)FindTask(NULL))->pr_MsgPort;

    debug(("WaitPort DosPort for startup...\n"));
    WaitPort(DosPort);      /* Get Startup Packet  */
    debug(("WaitPort DosPort done\n"));
    msg = GetMsg(DosPort);
    packet = (struct DosPacket *) msg->mn_Node.ln_Name;

    DevNode = BTOC(PArg3);
    {
	struct FileSysStartupMsg *fssm;
	struct DosEnvec *de;

	DevName = "parnet.device";
	UnitNr = 0;
	DevFlags = 0;

	if (fssm = (struct FileSysStartupMsg *)BTOC(DevNode->dn_Startup)) {
				    /* Same as BTOC(packet->dp_Arg2) */
	    UnitNr = fssm->fssm_Unit;
	    if (fssm->fssm_Device)
		DevName = (char *)BTOC(fssm->fssm_Device)+1;
	    DevFlags = fssm->fssm_Flags;
	    de = BTOC(fssm->fssm_Environ);

	    ClientDefaults(de->de_BlocksPerTrack, de->de_Interleave);
	}
    }

    if (DOSBase && OpenNetFS() == 0) {
	/*
	 * Loading DevNode->dn_Task causes DOS *NOT* to startup a new
	 * instance of the device driver for every reference.	E.G. if
	 * you were writing a CON: device you would want this field to be
	 * NULL.
	 */

	DevNode->dn_Task = DosPort;

	PRes1 = DOSTRUE;
	PRes2 = 0;
    } else {		    /* couldn't open dos.library  */
	PRes1 = DOSFALSE;
	PRes2 = ERROR_DEVICE_NOT_MOUNTED;   /* no better message available */
	returnpacket(packet);
	goto exit;		/* exit process    */
    }
    returnpacket(packet);

    /*
     * Get the first real packet.
     * This seems to be necessary because adding our Volume node at
     * this point causes some kind of deadlock. Which is kind
     * of silly since we returned the startup packet. Even calling
     * AttemptLockDosList does not seem to give any indication...
     */
    debug(("WaitPort DosPort for 1st real packet...\n"));
    WaitPort(DosPort);
    debug(("WaitPort DosPort done\n"));
    msg = GetMsg(DosPort);

    {
	struct DateStamp ds;

	DateStamp(&ds);
	VolNode = NewVolNode("Network", &ds);
	debug(("VolNode done\n"));
    }
    /* Initialize some more global variables	*/

    OpenCount = 0;
    WaitMask = (1L << DosPort->mp_SigBit) |
	       (1L << RdPort->mp_SigBit);

    done = -1;

    goto entry;

    /*
     * Here begins the endless loop, waiting for requests over our message
     * port and executing them.  Since requests are sent over the message
     * port in our device and volume nodes, we must not use our Process
     * message port for this: this precludes being able to call DOS
     * functions ourselves.
     */

top:
    for (done = -1; done < 0;) {
	Wait(WaitMask);
    wakeup:
	while (msg = GetMsg(RdPort)) {
	    /* Handle unsollicited network input */
	    DoProtocol(msg, ((struct IOStdReq *)msg)->io_Data);
	}
	while (msg = GetMsg(DosPort)) {
    entry:

	    packet = (PACKET *) msg->mn_Node.ln_Name;
	    PRes1 = DOSFALSE;
	    PRes2 = 0;
	    error = 0;
	    debug(("Packet: %4ld %08lx %08lx %08lx %s\n",
		     PType, PArg1, PArg2, PArg3, typetostr(PType)));

	    DosPacket = packet; 	/* For the System Requesters */
	    ReqPacket.p_Action = PType;

	    switch (PType) {
	    case ACTION_DIE:		/* attempt to die?  */
		done = (PArg1 == MSH_MAGIC) ? PArg2 : 0;   /* Argh! Hack! */
		break;
	    case ACTION_CURRENT_VOLUME: /* -		       VolNode,UnitNr*/
		PRes1 = (long) CTOB(VolNode);
		PRes2 = UnitNr;
		break;
	    case ACTION_LOCATE_OBJECT:	/* Lock,Name,Mode	Lock	     */
		{
		    struct FileLock *lock;
		    NetFileLock     *netfl;
		    long	    lockmode;

		    lock = BTOC(PArg1);
		    if ((lockmode = PArg3) != EXCLUSIVE_LOCK)
			lockmode = SHARED_LOCK;
		    netfl = NetLock(NETFL(lock ? lock->fl_Key : 0),
				  BTOC(PArg2),
				  lockmode);
		    PRes1 = MakeFileLock(netfl, lock, lockmode);
		}
		break;
	    case ACTION_RENAME_DISK:	/* BSTR:NewName 	   Bool      */
		NewVolNodeName(BTOC(PArg1));
		PRes1 = DOSTRUE;
		break;
	    case ACTION_FREE_LOCK:	/* Lock 		   Bool      */
		{
		    struct FileLock *lock;
		    NetFileLock    *netfl;

		    PRes1 = DOSTRUE;
		    lock = BTOC(PArg1);
		    if (lock == NULL)
			break;

		    netfl = NETFL(lock->fl_Key);
		    FreeFileLock(lock);
		    NetUnLock(netfl);
		}
		break;
	    case ACTION_DELETE_OBJECT:	/* Lock,Name		Bool	     */
		{
		    struct FileLock *lock;

		    lock = BTOC(PArg1);
		    PRes1 = NetDeleteFile(NETFL(lock ? lock->fl_Key : 0),
					 BTOC(PArg2));
		}
		break;
	    case ACTION_RENAME_OBJECT:	/* SLock,SName,DLock,DName   Bool    */
		{
		    struct FileLock *slock, *dlock;

		    slock = BTOC(PArg1);
		    dlock = BTOC(PArg3);
		    PRes1 = NetRename(NETFL(slock ? slock->fl_Key : 0),
				     BTOC(PArg2),
				     NETFL(dlock ? dlock->fl_Key : 0),
				     BTOC(PArg4));
		}
		break;
	    case ACTION_MORECACHE:	/* #BufsToAdd		bool,numbufs */
		PRes1 = 0;	    /* observed behaviour in std filesystem */
		PRes2 = 0;	    /* documented behaviour in manual */
		break;
	    case ACTION_COPY_DIR:	/* Lock 		   Lock      */
		{
		    struct FileLock *lock;
		    NetFileLock *netfl;

		    lock = BTOC(PArg1);

		    netfl = NetDupLock(NETFL(lock ? lock->fl_Key : 0));

		    PRes1 = MakeFileLock(netfl, lock,
					 lock ? lock->fl_Access : SHARED_LOCK);
		}
		break;
	    case ACTION_SET_PROTECT:	/* -,Lock,Name,Mask	   Bool      */
		{
		    struct FileLock *lock;

		    lock = BTOC(PArg2);
		    PRes1 = NetSetProtect(NETFL(lock ? lock->fl_Key : 0),
					BTOC(PArg3), PArg4);
		}
		break;
	    case ACTION_CREATE_DIR:	/* Lock,Name		Lock	     */
		{
		    struct FileLock *lock;
		    NetFileLock *netfl;

		    lock = BTOC(PArg1);

		    netfl = NetCreateDir(NETFL(lock ? lock->fl_Key : 0),
				       BTOC(PArg2));

		    PRes1 = MakeFileLock(netfl, lock, SHARED_LOCK);
		}
		break;
	    case ACTION_EXAMINE_OBJECT: /* Lock,Fib	       Bool	     */
		{
		    struct FileLock *lock;

		    lock = BTOC(PArg1);
		    PRes1 = NetExamine(NETFL(lock ? lock->fl_Key : 0),
				      BTOC(PArg2));
		}
		break;
	    case ACTION_EXAMINE_NEXT:	/* Lock,Fib	       Bool	     */
		{
		    struct FileLock *lock;

		    lock = BTOC(PArg1);
		    PRes1 = NetExNext(NETFL(lock ? lock->fl_Key : 0),
				     BTOC(PArg2));
		}
		break;
	    case ACTION_DISK_INFO:	/* InfoData	       Bool:TRUE     */
		PRes1 = NetDiskInfo(BTOC(PArg1));
		break;
	    case ACTION_INFO:	/* Lock,InfoData	       Bool:TRUE     */
		{
		    struct FileLock *lock;

		    lock = BTOC(PArg1);
		    PRes1 = NetInfo(NETFL(lock ? lock->fl_Key : 0),
				     BTOC(PArg2));
		}
		break;
/*	    case ACTION_FLUSH:		/ * writeout bufs, disk motor off     */
	    case ACTION_SET_COMMENT:	/* -,Lock,Name,Comment	   Bool      */
		{
		    struct FileLock *lock;

		    lock = BTOC(PArg2);
		    PRes1 = NetSetComment(NETFL(lock ? lock->fl_Key : 0),
					  BTOC(PArg3), BTOC(PArg4));
		}
		break;
	    case ACTION_PARENT: /* Lock 		       ParentLock    */
		{
		    struct FileLock *lock;
		    NetFileLock *netfl;

		    lock = BTOC(PArg1);

		    netfl = NetParentDir(NETFL(lock ? lock->fl_Key : 0));

		    PRes1 = MakeFileLock(netfl, lock, SHARED_LOCK);
		}
		break;
	    case ACTION_INHIBIT:	/* Bool 		   Bool      */
		PRes1 = DOSFALSE;
		break;
	    case ACTION_SET_DATE: /* -,Lock,Name,CPTRDateStamp	   Bool      */
		{
		    struct FileLock *lock;

		    lock = BTOC(PArg2);
		    PRes1 = NetSetDate(NETFL(lock ? lock->fl_Key : 0),
				      BTOC(PArg3),
				      (struct DateStamp *)PArg4);
		}
		break;
	    case ACTION_SAME_LOCK:  /* Lock1,Lock2		   Result    */
		{
		    struct FileLock *fl1, *fl2;

		    fl1 = BTOC(PArg1);
		    fl2 = BTOC(PArg2);
		    if (fl1->fl_Volume == fl2->fl_Volume &&
			BTOC(fl1->fl_Volume) == VolNode) {
			PRes1 = NetSameLock(NETFL(fl1->fl_Key),
					   NETFL(fl2->fl_Key));
		    } else {
			PRes1 = DOSFALSE;
			error = ERROR_INVALID_LOCK;
		    }
		}
		break;
	    case ACTION_READ:	/* FHArg1,CPTRBuffer,Length	  ActLength  */
		PRes1 = NetRead(NETFH(PArg1), (UBYTE *)PArg2, PArg3);
		break;
	    case ACTION_WRITE:	/* FHArg1,CPTRBuffer,Length	  ActLength  */
		PRes1 = NetWrite(NETFH(PArg1), (UBYTE *)PArg2, PArg3);
		break;
	    case ACTION_OPENRW: 	/* FileHandle,Lock,Name    Bool      */
	    case ACTION_OPENOLD:	/* FileHandle,Lock,Name    Bool      */
	    case ACTION_OPENNEW:	/* FileHandle,Lock,Name    Bool      */
		{
		    NetFileHandle *netfh;
		    struct FileHandle *fh;
		    struct FileLock *lock;

		    fh = BTOC(PArg1);
		    lock = BTOC(PArg2);
		    netfh = NetOpen(NETFL(lock ? lock->fl_Key : 0),
				  BTOC(PArg3),
				  PType);
		    if (netfh) {
			fh->fh_Arg1 = (long) netfh;
			PRes1 = DOSTRUE;
			OpenCount++;
		    }
		}
		break;
	    case ACTION_CLOSE:	/* FHArg1			Bool:Success */
		PRes1 = NetClose(NETFH(PArg1));
		OpenCount--;
		break;
	    case ACTION_SEEK:	/* FHArg1,Position,Mode 	 OldPosition */
		PRes1 = NetSeek(NETFH(PArg1), PArg2, PArg3);
		break;
#ifdef notdef
	    case ACTION_FORMAT: /* vol,type			Bool:success */
		PRes1 = NetFormat(BTOC(PArg1), PArg2);
		break;
#endif
	    case ACTION_MAKE_LINK: /* parent,name,target,soft	   Bool      */
		{
		    struct FileLock *lock;
		    void	    *dest;

		    lock = BTOC(PArg1);
		    dest = PArg4 ? (char *)PArg3            /* C string! */
				 : (void *)((struct FileLock *)BTOC(PArg3))->fl_Key;
		    PRes1 = NetMakeLink(NETFL(lock ? lock->fl_Key : 0),
					BTOC(PArg2),
					dest,
					PArg4);
		}
		break;
	    case ACTION_READ_LINK: /* parent,name,target,size	   length    */
		{
		    struct FileLock *lock;
		    lock = BTOC(PArg1);
		    PRes1 = NetReadLink(NETFL(lock ? lock->fl_Key : 0),
					(char *)PArg2,      /* C string! */
					(char *)PArg3,
					PArg4);
		}
		break;
	    case ACTION_SET_FILE_SIZE:
		PRes1 = NetSetFileSize(NETFH(PArg1), PArg2, PArg3);
		break;
/*	    case ACTION_WRITE_PROTECT: */
				/* FH,Lock			    BOOL     */
	    case ACTION_FH_FROM_LOCK:
		{
		    NetFileHandle *netfh;
		    struct FileHandle *fh;
		    struct FileLock *lock;

		    fh = BTOC(PArg1);
		    lock = BTOC(PArg2);
		    netfh = NetOpenFromLock(NETFL(lock ? lock->fl_Key : 0));
		    if (netfh) {
			fh->fh_Arg1 = (long) netfh;
			PRes1 = DOSTRUE;
			OpenCount++;
			/* Discard the lock */
			FreeFileLock(lock);
		    }
		}
		break;
	    case ACTION_IS_FILESYSTEM:
		PRes1 = DOSTRUE;
		break;
	    case ACTION_CHANGE_MODE:
		{
		    void *object;

		    switch (PArg1) {
		    case CHANGE_FH:
			object = NETFH(((struct FileHandle *)BTOC(PArg2))->fh_Arg1);
			break;
		    case CHANGE_LOCK:
			object = NETFL(((struct FileLock *)BTOC(PArg2))->fl_Key);
			break;
		    }
		    PRes1 = NetChangeMode(PArg1, object, PArg3);
		}
		break;
	    case ACTION_COPY_DIR_FH:	/* fh_Arg1		   Lock      */
	    case ACTION_PARENT_FH:
		{
		    NetFileLock *netfl;

		    if (PType == ACTION_PARENT_FH)
			netfl = NetParentOfFH(NETFH(PArg1));
		    else
			netfl = NetDupLockFromFH(NETFH(PArg1));

		    /* User has inserted disk by now, so we can use VolNode */
		    PRes1 = MakeFileLock(netfl, NULL, SHARED_LOCK);
		}
		break;
	    case ACTION_EXAMINE_FH:	 /* fh_Arg1,Fib 		Bool	  */
		PRes1 = NetExamineFH(NETFH(PArg1), BTOC(PArg2));
		break;
		/*
		 * A few other packet types which we do not support
		 */
	    case ACTION_LOCK_RECORD:	/* fh,pos,len,mode,time    Bool      */
		{
		    debug(("fh(arg1) %x\n", PArg1));

		    NetLockRecord(packet, NETFH(PArg1), PArg2, PArg3, PArg4, PArg5);
		    packet = NULL;
		}
		break;
	    case ACTION_FREE_RECORD:	/* fh,pos,len		   Bool      */
		{
		    debug(("fh(arg1) %x\n", PArg1));
/*		    NetFileHandle *netfh = NETFH(((struct FileHandle *)
						  PArg1)->fh_Arg1); */

		    PRes1 = NetFreeRecord(NETFH(PArg1), PArg2, PArg3);
		}
		break;
/*	    case ACTION_WAIT_CHAR:     / * Timeout, ticks	   Bool      */
/*	    case ACTION_RAWMODE:       / * Bool(-1:RAW 0:CON)      OldState  */
	    default:
		PRes1 = DOSFALSE;
		error = ERROR_ACTION_NOT_KNOWN;
		break;
	    } /* end switch */
	    if (packet) {
		if (error)
		    PRes2 = error;
		debug(("RES=%06lx, ERR=%ld\n", PRes1, error));
		returnpacket(packet);
		DosPacket = NULL;
	    }
#ifdef DEBUG
	    else {
		debug(("NOREP\n"));
	    }
#endif
	} /* end while (GetMsg()) */

    } /* end for (;done) */

#ifdef DEBUG
    debug(("Can we remove ourselves? "));
#endif				/* DEBUG */
    Forbid();
    if (OpenCount || packetsqueued()) {
	Permit();
	debug((" ..  not yet!\n"));
	goto top;		/* sorry... can't exit     */
    }
    debug((" .. yes!\n"));

    /*
     * Causes a new process to be created on next reference.
     */

    DevNode->dn_Task = NULL;
    CloseNetFS();
    FreeVolNode(VolNode);
    debug(("CloseNetFS() returned.\n"));

    /*
     * Remove debug, closedown, fall of the end of the world.
     */
exit:
#ifdef DEBUG
    uninitsyslog();
#endif				/* DEBUG */

    if (done & 2)
	UnLoadSeg(DevNode->dn_SegList); /* This is real fun. We are still */
    if (done & (2 | 1))
	DevNode->dn_SegList = NULL;	/* Forbid()den, fortunately */

    CloseLibrary((struct Library *)DOSBase);

    /* Fall off the end of the world. Implicit Permit(). */
}

/*
 *  Make a new struct FileLock, for DOS use. It is put on a singly linked
 *  list, which is attached to the same VolumeNode the old lock was on.
 *
 *  Also note that we must ALWAYS be prepared to UnLock() or DupLock()
 *  any FileLocks we ever made, even if the volume in question has been
 *  removed and/or inserted into another drive with another FileSystem
 *  handler!
 *
 * DOS makes certain assumptions about LOCKS.	A lock must minimally be a
 * FileLock structure, with additional private information after the
 * FileLock structure.	The longword before the beginning of the structure
 * must contain the length of structure + 4.
 *
 * NOTE!!!!! The workbench does not follow the rules and assumes it can copy
 * lock structures.  This means that if you want to be workbench
 * compatible, your lock structures must be EXACTLY sizeof(struct
 * FileLock). Also, it sometimes uses uninitialized values for the lock mode...
 */

Prototype struct FileLock *NewFileLock(NetFileLock *netfl, struct FileLock *fl);

struct FileLock *
NewFileLock(NetFileLock *netfl, struct FileLock *fl)
{
    struct FileLock *newlock;
    struct DeviceList *volnode = NULL;

    if (fl) {
	volnode = BTOC(fl->fl_Volume);
    }
    if (volnode == NULL) {
	volnode = VolNode;
	debug(("volnode 0->%lx\n", volnode));
    }
#ifdef DEBUG
    if (volnode != VolNode) {
	debug(("volnode != VolNode %lx != %lx\n",
	    volnode, VolNode));
    }
    if (volnode->dl_Task != DosPort) {
	debug(("volnode->dl_Task != DosPort %lx != %lx\n",
	    volnode->dl_Task, DosPort));
    }
#endif

    if (newlock = dosalloc((ULONG)sizeof (*newlock))) {
	newlock->fl_Key = (ULONG) netfl;
	newlock->fl_Task = DosPort;
	newlock->fl_Volume = (BPTR) CTOB(volnode);
	Forbid();
	newlock->fl_Link = volnode->dl_LockList;
	volnode->dl_LockList = (BPTR) CTOB(newlock);
	Permit();
    } else
	error = ERROR_NO_FREE_STORE;

    return newlock;
}

Prototype long FreeFileLock(struct FileLock *lock);

long
FreeFileLock(struct FileLock *lock)
{
    register struct FileLock *fl;
    register struct FileLock **flp;
    struct DeviceList	     *volnode;

    volnode = (struct DeviceList *)BTOC(lock->fl_Volume);
    flp = (struct FileLock **) &volnode->dl_LockList;
    for (fl = BTOC(*flp); fl && fl != lock; fl = BTOC(fl->fl_Link))
	flp = (struct FileLock **)&fl->fl_Link;

    if (fl == lock) {
	*(BPTR *)flp = fl->fl_Link;
	dosfree((ULONG *)fl);
	OpenCount--;
	return DOSTRUE;
    } else {
	debug(("Huh?? Could not find filelock!\n"));
	return DOSFALSE;
    }
}

/*
 * MakeFileLock allocates and initializes a new FileLock, using info
 * from an existing FileLock. It always consumes the NetFileLock, even
 * in case of failure.
 */

Prototype BPTR MakeFileLock(NetFileLock *netfl, struct FileLock *fl, long mode);

BPTR
MakeFileLock(NetFileLock *netfl, struct FileLock *fl, long mode)
{
    struct FileLock *newlock;

    newlock = NULL;
    if (netfl) {
	if (newlock = NewFileLock(netfl, fl)) {
	    newlock->fl_Access = mode;
	    OpenCount++;
	} else
	    NetUnLock(netfl);
    }

    return CTOB(newlock);
}

/*
 * Create Volume node and add to the device list.   This will
 * cause the WORKBENCH to recognize us as a disk.   If we
 * don't create a Volume node, Wb will not recognize us.
 * However, we are a NET: disk, Volume node or not.
 */

Prototype struct DeviceList *NewVolNode(char *name, struct DateStamp *date);

struct DeviceList	 *
NewVolNode(char *name, struct DateStamp *date)
{
    struct DeviceList *volnode;
    char	   *volname;	    /* This is my volume name */

    if (volnode = dosalloc((ULONG)sizeof (struct DeviceList))) {
	if (volname = dosalloc(33L)) {
	    volname[0] = strlen(name);
	    strcpy(volname + 1, name);      /* Make sure \0 terminated */

	    volnode->dl_Type = DLT_VOLUME;
	    volnode->dl_Task = DosPort;
	    volnode->dl_DiskType = ID_DOS_DISK;
	    volnode->dl_Name = (BSTR *)CTOB(volname);
	    volnode->dl_VolumeDate = *date;
	    volnode->dl_NetFileLockList = NULL;

	    if (DOSBase->dl_lib.lib_Version >= 37) {
		struct DosList *dl;
		debug(("LockDosList...\n"));
		dl = AttemptLockDosList(LDF_VOLUMES | LDF_DEVICES | LDF_WRITE);
		if ((ULONG)dl <= 1) {
		    debug(("LockDosList NOT ok\n"));
		    goto brute_force;
		}
		UnLockDosList(LDF_VOLUMES | LDF_DEVICES | LDF_WRITE);

		debug(("AddDosEntry...\n"));
		if (AddDosEntry((struct DosList *)volnode) == DOSFALSE) {
		    debug(("AddDosEntry for VolNode fails!\n"));
		    goto error;
		}
		debug(("AddDosEntry: done\n"));
	    } else {
		struct DosInfo *di;

	    brute_force:
		di = BTOC(((struct RootNode *) DOSBase->dl_Root)->rn_Info);

		Forbid();
		volnode->dl_Next = di->di_DevInfo;
		di->di_DevInfo = (long) CTOB(volnode);
		Permit();
	    }
	} else {
	error:
	    dosfree((ULONG *)volnode);
	    volnode = NULL;
	}
    } else {
	error = ERROR_NO_FREE_STORE;
    }

    return volnode;
}

/*
 *  Give the VolNode a new name (given as a BCPL string).
 */

Prototype void NewVolNodeName(char *newname);

void
NewVolNodeName(char *newname)
{
    if (VolNode) {
	register char *volname = BTOC(VolNode->dl_Name);

	memcpy(volname, newname, 1 + newname[0]);
    }
}

/*
 * Remove Volume entry.  Since DOS uses singly linked lists, we must
 * (ugg) search it manually to find the link before our Volume entry.
 */

Prototype void FreeVolNode(struct DeviceList *volnode);

void
FreeVolNode(struct DeviceList *volnode)
{
    debug(("FreeVolNode %08lx\n", volnode));

    if (volnode == NULL)
	return;

    if (DOSBase->dl_lib.lib_Version >= 37) {
	struct DosList *dl;

	dl = LockDosList(LDF_VOLUMES | LDF_WRITE);
	(void)RemDosEntry((struct DosList *)volnode);
	UnLockDosList(LDF_VOLUMES | LDF_WRITE);
    } else {
	struct DosInfo *di = BTOC(((struct RootNode *) DOSBase->dl_Root)->rn_Info);
	register struct DeviceList *dl;
	register void  *dlp;

	dlp = &di->di_DevInfo;
	Forbid();
	for (dl = BTOC(di->di_DevInfo); dl && dl != volnode; dl = BTOC(dl->dl_Next))
	    dlp = &dl->dl_Next;
	if (dl == volnode) {
	    *(BPTR *) dlp = dl->dl_Next;
	}
#ifdef DEBUG
	else {
	    debug(("****PANIC: Unable to find volume node\n"));
	}
#endif				/* DEBUG */
	Permit();
    }

    dosfree(BTOC(volnode->dl_Name));
    dosfree((ULONG *)volnode);

    if (volnode == VolNode)
	VolNode = NULL;
}
