Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator) (07/16/90)
Submitted-by: Olaf 'Rhialto' Seibert <U211344%HNYKUN11.BITNET@CUNYVM.CUNY.EDU> Posting-number: Volume 90, Issue 215 Archive-name: devices/msh-1.30/part03 #!/bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 3 (of 6)." # Contents: src/device.c src/hanfile.c # Wrapped by tadguy@xanth on Sun Jul 15 19:59:07 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'src/device.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/device.c'\" else echo shar: Extracting \"'src/device.c'\" \(18541 characters\) sed "s/^X//" >'src/device.c' <<'END_OF_FILE' X/*- X * $Id: device.c,v 1.30 90/06/04 23:18:39 Rhialto Rel $ X * $Log: device.c,v $ X * Revision 1.30 90/06/04 23:18:39 Rhialto X * Release 1 Patch 3 X * X * DEVICE.C X * X * The messydisk.device code that makes it a real Exec .device. X * Mostly based on the 1.1 RKM example and Matt Dillon's library code. X * X * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May X * not be used or copied without a licence. X-*/ X X#include "dev.h" X#include "device.h" X X/*#undef DEBUG /**/ X#ifdef DEBUG X# define debug(x) dbprintf x X#else X# define debug(x) X#endif X X/* X * The first executable location. This should return an error in case X * someone tried to run you as a program (instead of loading you as a X * device) X */ X/* INDENT OFF */ X#asm X moveq.l #20,d0 X rts X#endasm X/* INDENT ON */ X X/* X * A romtag structure. Both "exec" and "ramlib" look for this structure to X * discover magic constants about you (such as where to start running you X * from...). X */ X/* INDENT OFF */ X#asm X public __H0_end X_EndCode equ __H0_end X public _RomTag X_RomTag: X dc.w $4AFC ; RTC_MATCHWORD X dc.l _RomTag ; rt_MatchTag X dc.l __H0_end ; rt_EndSkip X dc.b 0 ; rt_Flags (no RTF_AUTOINIT) X dc.b VERSION ; rt_Version X dc.b 3 ; rt_Type NT_DEVICE X dc.b RTPRI ; rt_Pri X dc.l _DevName ; rt_Name X dc.l _idString ; rt_IdString X dc.l _Init ; rt_Init X#endasm X/* INDENT ON */ X Xchar DevName[] = "messydisk.device"; Xchar idString[] = "messydisk.device $Revision: 1.30 $ $Date: 90/06/04 23:18:39 $\r\n"; X X/* X * -30-6*X Library vectors: X */ X Xvoid (*LibVectors[]) () = X{ X _DevOpen, _DevClose, _DevExpunge, _LibNull, X X _DevBeginIO, X _DevAbortIO, X (void (*) ()) -1 X}; X X/* X * Device commands: X */ X Xvoid (*funcTable[]) () = { X CMD_Invalid, CMD_Reset, CMD_Read, CMD_Write, CMD_Update, CMD_Clear, X CMD_Stop, CMD_Start, CMD_Flush, TD_Motor, TD_Seek, TD_Format, X TD_Remove, TD_Changenum, TD_Changestate, TD_Protstatus, TD_Rawread, X TD_Rawwrite, TD_Getdrivetype, TD_Getnumtracks, TD_Addchangeint, X TD_Remchangeint, X}; X X/* X * Here begin the system interface commands. When the user calls X * OpenDevice/CloseDevice/RemDevice, this eventually gets trahslated into X * a call to the following routines (Open/Close/Expunge). Exec has X * already put our device pointer in A6 for us. Exec has turned off task X * switching while in these routines (via Forbid/Permit), so we should not X * take too long in them. X */ X/* INDENT OFF */ X#asm X public _Init X_Init: ;a0=segment list X movem.l D2-D3/A0/A6,-(sp) X jsr _CInit X movem.l (sp)+,D2-D3/A0/A6 X rts X X public __DevOpen X__DevOpen: ;d0=unitnum,d1=flags,a1=ioreq,a6=device X movem.l D0-D3/A1/A6,-(sp) X jsr _DevOpen X movem.l (sp)+,D0-D3/A1/A6 X rts X X public __DevClose X__DevClose: ;a1=ioreq,a6=device X movem.l D2-D3/A1/A6,-(sp) X jsr _DevClose X movem.l (sp)+,D2-D3/A1/A6 X rts X X public __DevExpunge X__DevExpunge: ;a6=device X movem.l D2-D3/A6,-(sp) X jsr _DevExpunge X movem.l (sp)+,D2-D3/A6 X rts X X public __LibNull X__LibNull: X clr.l d0 X rts X X public __DevBeginIO X__DevBeginIO: ;a1=ioreq,a6=device X movem.l D2-D3/A1/A6,-(sp) X jsr _DevBeginIO X movem.l (sp)+,D2-D3/A1/A6 X rts X X public __DevAbortIO X__DevAbortIO: ;a1=ioreq,a6=device X movem.l D2-D3/A1/A6,-(sp) X jsr _DevAbortIO X movem.l (sp)+,D2-D3/A1/A6 X rts X#endasm X X#ifdef HANDLE_IO_QUICK X#asm X;;;; X; X; C interface to the atomic set bit and test old value instruction. X; X; Called as BSET_ACTIVE(byte *address). X; X; Old value of the bit returned all over d0.w X X_BSET_ACTIVE: X move.l 4(sp),a0 X bset #0,(a0) ; UNITB_ACTIVE X sne d0 X rts X X#endasm X#endif X/* INDENT ON */ X Xlong SysBase; /* Argh! A global variable! */ X X/* X * The Initialization routine is given only a seglist pointer. Since we X * are NOT AUTOINIT we must construct and add the device ourselves and X * return either NULL or the device pointer. Exec has Forbid() for us X * during the call. X * X * If you have an extended device structure you must specify the size of the X * extended structure in MakeLibrary(). X */ X XDEV * XCInit(D2, D3, segment) Xulong D2, X D3; Xlong segment; X{ X DEV *dev; X X SysBase = *(long *) 4; X#ifdef DEBUG X dbinit(); X#endif X dev = MakeLibrary(LibVectors, NULL, NULL, (long) sizeof (DEV), NULL); X if (DevInit(dev)) { X dev->dev_Node.ln_Type = NT_DEVICE; X dev->dev_Node.ln_Name = DevName; X dev->dev_Flags = LIBF_CHANGED | LIBF_SUMUSED; X dev->dev_Version = VERSION; X dev->dev_Revision = REVISION; X dev->dev_IdString = (APTR) idString; X dev->md_Seglist = segment; X AddDevice(dev); X return (dev); X } X FreeMem((char *) dev - dev->dev_NegSize, dev->dev_NegSize + dev->dev_PosSize); X return NULL; X} X X/* X * Open is given the device pointer, unitno and flags. Either return the X * device pointer or NULL. Remove the DELAYED-EXPUNGE flag. Exec has X * Forbid() for us during the call. X */ X Xvoid XDevOpen(unitno, flags, D2, D3, ioreq, dev) Xulong unitno; Xulong flags; Xulong D2, X D3; Xstruct IOStdReq *ioreq; XDEV *dev; X{ X UNIT *unit; X X debug(("OpenDevice unit %ld, flags %lx\n", unitno, flags)); X if (unitno >= MD_NUMUNITS) X goto error; X X if ((unit = dev->md_Unit[unitno]) == NULL) { X if ((unit = UnitInit(dev, unitno)) == NULL) X goto error; X dev->md_Unit[unitno] = unit; X } X ioreq->io_Unit = (struct Unit *) unit; X X ++unit->mu_OpenCnt; X ++dev->dev_OpenCnt; X dev->dev_Flags &= ~LIBF_DELEXP; X X return; X Xerror: X ioreq->io_Error = IOERR_OPENFAIL; X} X X/* X * Close is given the device pointer and the io request. Be sure not to X * decrement the open count if already zero. If the open count is or X * becomes zero AND there is a LIBF_DELEXP, we expunge the device and X * return the seglist. Otherwise we return NULL. X * X * Note that this routine never sets LIBF_DELEXP on its own. X * X * Exec has Forbid() for us during the call. X */ X Xlong XDevClose(D2, D3, ioreq, dev) Xulong D2, X D3; Xstruct IOStdReq *ioreq; XDEV *dev; X{ X UNIT *unit; X X unit = (UNIT *) ioreq->io_Unit; X debug(("CloseDevice io %08lx unit %08lx\n", ioreq, unit)); X X /* X * See if the unit is still in use. If not, close it down. This may X * need to do an update, which requires the ioreq. X */ X X if (unit->mu_OpenCnt && --unit->mu_OpenCnt == 0) { X dev->md_Unit[unit->mu_UnitNr] = NULL; X UnitCloseDown(ioreq, dev, unit); X } X /* X * Make sure the ioreq is not used again. X */ X ioreq->io_Unit = (void *) -1; X ioreq->io_Device = (void *) -1; X X if (dev->dev_OpenCnt && --dev->dev_OpenCnt) X return (NULL); X if (dev->dev_Flags & LIBF_DELEXP) X return (DevExpunge(D2, D3, dev)); X return (NULL); X} X X/* X * We expunge the device and return the Seglist ONLY if the open count is X * zero. If the open count is not zero we set the DELAYED-EXPUNGE X * flag and return NULL. X * X * Exec has Forbid() for us during the call. NOTE ALSO that Expunge might be X * called from the memory allocator and thus we CANNOT DO A Wait() or X * otherwise take a long time to complete (straight from RKM). X * X * Apparently RemLibrary(lib) calls our expunge routine and would therefore X * freeze if we called it ourselves. As far as I can tell from RKM, X * DevExpunge(lib) must remove the device itself as shown below. X */ X Xlong XDevExpunge(D2, D3, dev) Xulong D2, X D3; XDEV *dev; X{ X long Seglist; X X if (dev->dev_OpenCnt) { X dev->dev_Flags |= LIBF_DELEXP; X return (NULL); X } X Remove(dev); X DevCloseDown(dev); /* Should be quick! */ X#ifdef DEBUG X dbuninit(); X#endif X Seglist = dev->md_Seglist; X FreeMem((char *) dev - dev->dev_NegSize, X (long) dev->dev_NegSize + dev->dev_PosSize); X return (Seglist); X} X X/* X * BeginIO entry point. We don't handle any QUICK requests, we just send X * the request to the proper unit to handle. X */ X Xvoid XDevBeginIO(D2, D3, ioreq, dev) Xulong D2, X D3; Xregister struct IOStdReq *ioreq; XDEV *dev; X{ X UNIT *unit; X X /* X * Bookkeeping. X */ X unit = (UNIT *) ioreq->io_Unit; X debug(("BeginIO: io %08lx dev %08lx u %08lx\n", ioreq, dev, unit)); X X /* X * See if the io command is within range. X */ X if (STRIP(ioreq->io_Command) > TD_LASTCOMM) X goto NoCmd; X X#ifdef HANDLE_IO_QUICK X Forbid(); /* Disable(); is a bit too strong for us. */ X#endif X X /* X * Process all immediate commands no matter what. Don't even require X * an exclusive lock on the unit. X */ X if (IMMEDIATE & (1L << STRIP(ioreq->io_Command))) X goto Immediate; X X /* X * We don't handle any QUICK I/O since that only gives trouble with X * message ports and so. Other devices normally would include the code X * below. X */ X#ifdef HANDLE_IO_QUICK X /* X * See if the user does not request QUICK IO. If not, it is likely to X * be async and therefore we don't do it sync. X */ X if (!(ioreq->io_Flags & IOF_QUICK)) X goto NoQuickRequested; X X /* X * See if the unit is STOPPED. If so, queue the msg. X */ X if (unit->mu_Flags & UNITF_STOPPED) X goto QueueMsg; X X /* X * This is not an immediate command. See if the device is busy. If X * not, process the action in this (the caller's) context. X */ X if (!BSET_ACTIVE(&unit->mu_Flags)) X goto Immediate; X#endif X X /* X * We need to queue the device. Clear the QUICK flag. X */ XQueueMsg: X ioreq->io_Flags &= ~IOF_QUICK; XNoQuickRequested: X#ifdef HANDLE_IO_QUICK X Permit(); /* Enable(); is a bit too strong for us. */ X#endif X PutMsg(&unit->mu_Port, ioreq); X X return; X XImmediate: X#ifdef HANDLE_IO_QUICK X Permit(); /* Enable(); is a bit too strong for us. */ X#endif X debug(("BeginIO: Immediate\n")); X ioreq->io_Error = TDERR_NoError; X PerformIO(ioreq, unit); X return; X XNoCmd: X ioreq->io_Error = IOERR_NOCMD; X TermIO(ioreq); X return; X X} X X/* X * Terminate an io request. Called (normally) for every BeginIO. 'Funny' X * commands that don't call TermIO, or call it multiple times, may not be X * properly handled unless you are careful. TD_ADDCHANGEINT and X * TD_REMCHANGEINT are obvious examples. X */ X Xvoid XTermIO(ioreq) Xregister struct IOStdReq *ioreq; X{ X register UNIT *unit; X X unit = (UNIT *) ioreq->io_Unit; X debug(("TermIO: io %08lx u %08lx %ld %d\n", ioreq, unit, X ioreq->io_Actual, ioreq->io_Error)); X X#ifdef HANDLE_IO_QUICK X /* X * Since immediate commands don't even require an exclusive lock on X * the unit, don't unlock it. X */ X if (IMMEDIATE & (1L << STRIP(ioreq->io_Command))) X goto Immediate; X X /* X * We may need to turn the active (lock) bit off, but not if we are X * within the task. X */ X if (unit->mu_Flags & UNITF_INTASK) X goto Immediate; X X unit->mu_Flags &= ~UNITF_ACTIVE; X X /* X * The task may have work to do that came in while we were processing X * in the caller's context. X */ X if (unit->mu_Flags & UNITF_WAKETASK) { X unit->mu_Flags &= ~UNITF_WAKETASK; X WakePort(&unit->mu_Port); X } X#endif X XImmediate: X /* X * If the quick bit is still set then wen don't need to reply the msg X * -- just return to the user. X */ X X if (!(ioreq->io_Flags & IOF_QUICK)) X ReplyMsg(&ioreq->io_Message); X X return; X} X X/* X * AbortIO entry point. We don't abort IO here. X */ X Xlong XDevAbortIO(D2, D3, ioreq, dev) Xulong D2, X D3; XDEV *dev; Xstruct IOStdReq *ioreq; X{ X return 1; X} X Xvoid XWakePort(port) Xregister struct MsgPort *port; X{ X Signal(port->mp_SigTask, 1L << port->mp_SigBit); X} X X/* X * This is the main loop of the Unit tasks. It must be very careful with X * global data. X */ X Xvoid XUnitTask() X{ X /* DEV *dev; */ X UNIT *unit; X long waitmask; X struct IOExtTD *ioreq; X X { X struct Task *task, X *FindTask(); X X task = FindTask(NULL); X unit = (UNIT *) task->tc_UserData; X /* dev = unit->mu_Dev; */ X task->tc_UserData = NULL; X } X X /* X * Now finish initializing the message ports and other signal things X */ X X { X byte sigbit; X X unit->mu_DiskReplyPort.mp_SigBit = AllocSignal(-1L); X unit->mu_DiskReplyPort.mp_Flags = PA_SIGNAL; X X sigbit = AllocSignal(-1L); X unit->mu_Port.mp_SigBit = sigbit; X unit->mu_Port.mp_Flags = PA_SIGNAL; X waitmask = 1L << sigbit; X X unit->mu_DmaSignal = AllocSignal(-1L); X } X X for (;;) { X debug(("Task: Waiting... ")); X Wait(waitmask); X X /* X * See if we are stopped. X */ X if (unit->mu_Flags & UNITF_STOPPED) X continue; X X#ifdef HANDLE_IO_QUICK X /* X * Lock the device. If it fails, we have set a flag such that the X * TermIO wakes us again. X */ X unit->mu_Flags |= UNITF_WAKETASK; X if (BSET_ACTIVE(&unit->mu_Flags)) X continue; X X unit->mu_Flags |= UNITF_INTASK; X#endif X X while (ioreq = (struct IOExtTD *) GetMsg(&unit->mu_Port)) { X debug(("Task: io %08lx %x\n", ioreq, ioreq->iotd_Req.io_Command)); X ioreq->iotd_Req.io_Error = 0; X PerformIO((&ioreq->iotd_Req), unit); X } X X#ifdef HANDLE_IO_QUICK X unit->mu_Flags &= ~(UNITF_ACTIVE | UNITF_INTASK | UNITF_WAKETASK); X#endif X } X} X Xvoid XCMD_Invalid(ioreq, unit) Xstruct IOStdReq *ioreq; XUNIT *unit; X{ X ioreq->io_Error = IOERR_NOCMD; X TermIO(ioreq); X} X Xvoid XCMD_Stop(ioreq, unit) Xstruct IOExtTD *ioreq; XUNIT *unit; X{ X unit->mu_Flags |= UNITF_STOPPED; X TermIO(ioreq); X} X Xvoid XCMD_Start(ioreq, unit) Xstruct IOExtTD *ioreq; XUNIT *unit; X{ X unit->mu_Flags &= ~UNITF_STOPPED; X WakePort(&unit->mu_Port); X TermIO(ioreq); X} X Xvoid XCMD_Flush(ioreq, unit) Xstruct IOExtTD *ioreq; XUNIT *unit; X{ X register struct IOStdReq *req; X X /* Flush our own command queue */ X Forbid(); X while (req = (struct IOStdReq *) GetMsg(unit->mu_Port)) { X req->io_Error = IOERR_ABORTED; X ReplyMsg(&req->io_Message); X } X Permit(); X X WakePort(&unit->mu_Port); X TermIO(ioreq); X} X Xvoid XTrackdiskGateway(ioreq, unit) Xregister struct IOExtTD *ioreq; XUNIT *unit; X{ X register struct IOExtTD *tdioreq; X X debug(("Trackdisk: %x ", ioreq->iotd_Req.io_Command)); X tdioreq = unit->mu_DiskIOReq; X X /* X * Clone almost the entire io request to relay to the X * trackdisk.device. X */ X X tdioreq->iotd_Req.io_Command = ioreq->iotd_Req.io_Command; X tdioreq->iotd_Req.io_Flags = ioreq->iotd_Req.io_Flags | IOF_QUICK; X tdioreq->iotd_Req.io_Length = ioreq->iotd_Req.io_Length; X tdioreq->iotd_Req.io_Data = ioreq->iotd_Req.io_Data; X tdioreq->iotd_Req.io_Offset = ioreq->iotd_Req.io_Offset; X if (ioreq->iotd_Req.io_Command & TDF_EXTCOM) { X tdioreq->iotd_Count = ioreq->iotd_Count; X tdioreq->iotd_SecLabel = ioreq->iotd_SecLabel; X } X BeginIO(tdioreq); X WaitIO(tdioreq); X X ioreq->iotd_Req.io_Error = tdioreq->iotd_Req.io_Error; X ioreq->iotd_Req.io_Actual = tdioreq->iotd_Req.io_Actual; X X TermIO(ioreq); X} X X#ifdef DEBUG X/* DEBUGGING */ Xstruct MsgPort *Dbport; /* owned by the debug process */ Xstruct MsgPort *Dback; /* owned by the DOS device driver */ Xshort DBEnable; Xstruct SignalSemaphore PortUse; X X#define CTOB(x) (void *)(((long)(x))>>2) /* BCPL conversion */ X X/* X * DEBUGGING CODE. You cannot make DOS library calls that access X * other devices from within a device driver because the caller may not be X * a process. If you need to make such calls you must create a port and X * construct the DOS messages yourself. I do not do this. To get X * debugging info out another PROCESS is created to which debugging X * messages can be sent. The replyport gets a new SigTask for every X * dbprintf call, therefore the semaphore. X */ X Xextern void debugproc(); Xstruct Library *DOSBase, X *OpenLibrary(); X Xdbinit() X{ X struct Task *task = FindTask(NULL); X X DOSBase = OpenLibrary("dos.library", 0L); X Dback = CreatePort("Dback", -1L); X FreeSignal((long) Dback->mp_SigBit); X Dback->mp_SigBit = 2; X InitSemaphore(&PortUse); X CreateProc("messydisk_DB", (long) TASKPRI + 1, CTOB(debugproc), 2000L); X WaitPort(Dback); /* handshake startup */ X GetMsg(Dback); /* remove dummy msg */ X DBEnable = 1; X dbprintf("Debugger running V1.11\n"); X} X Xdbuninit() X{ X struct Message killmsg; X X if (Dbport) { X killmsg.mn_Length = 0; /* 0 means die */ X ObtainSemaphore(&PortUse); X Dback->mp_SigTask = FindTask(NULL); X PutMsg(Dbport, &killmsg); X WaitPort(Dback); /* He's dead jim! */ X GetMsg(Dback); X ReleaseSemaphore(&PortUse); X Dback->mp_SigBit = -1; X DeletePort(Dback); X X /* X * Since the debug process is running at a greater priority, I am X * pretty sure that it is guarenteed to be completely removed X * before this task gets control again. X */ X } X CloseLibrary(DOSBase); X} X Xdbprintf(a, b, c, d, e, f, g, h, i, j) Xlong a, b, c, d, e, f, g, h, i, j; X{ X struct { X struct Message msg; X char buf[256]; X } msgbuf; X register struct Message *msg = &msgbuf.msg; X register long len; X X if (Dbport && DBEnable) { X ObtainSemaphore(&PortUse); /* sprintf is not re-entrant */ X sprintf(msgbuf.buf, a, b, c, d, e, f, g, h, i, j); X len = strlen(msgbuf.buf) + 1; X msg->mn_Length = len; /* Length NEVER 0 */ X Dback->mp_SigTask = FindTask(NULL); X PutMsg(Dbport, msg); X WaitPort(Dback); X GetMsg(Dback); X ReleaseSemaphore(&PortUse); X } X} X X/* X * BTW, the DOS library used by debugmain() was actually opened by the X * opener of the device driver. X */ X Xdebugmain() X{ X register struct Message *msg; X register long len; X register void *fh, X *Open(); X void *fh2; X struct Message DummyMsg; X X Dbport = CreatePort("Dbport", -1L); X fh = Open("CON:0/20/640/101/Device debug", MODE_NEWFILE); X fh2 = Open("PAR:", MODE_OLDFILE); X PutMsg(Dback, &DummyMsg); X for (;;) { X WaitPort(Dbport); X msg = GetMsg(Dbport); X len = msg->mn_Length; X if (len == 0) X break; X --len; /* Fix length up */ X if (DBEnable & 1) X Write(fh, msg + 1, len); X if (DBEnable & 2) X Write(fh2, msg + 1, len); X PutMsg(Dback, msg); X } X Close(fh); X Close(fh2); X DeletePort(Dbport); X PutMsg(Dback, msg); /* Kill handshake */ X} X X/* X * The assembly tag for the DOS process: CNOP causes alignment problems X * with the Aztec assembler for some reason. I assume then, that the X * alignment is unknown. Since the BCPL conversion basically zero's the X * lower two bits of the address the actual code may start anywhere within X * 8 bytes of address (remember the first longword is a segment pointer X * and skipped). Sigh.... (see CreateProc() above). X */ X/* INDENT OFF */ X#asm X public _debugproc X public _debugmain X X cseg X_debugproc: X nop X nop X nop X nop X nop X movem.l D2-D7/A2-A6,-(sp) X jsr _debugmain X movem.l (sp)+,D2-D7/A2-A6 X rts X#endasm X X#endif /* DEBUG */ END_OF_FILE if test 18541 -ne `wc -c <'src/device.c'`; then echo shar: \"'src/device.c'\" unpacked with wrong size! fi # end of 'src/device.c' fi if test -f 'src/hanfile.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/hanfile.c'\" else echo shar: Extracting \"'src/hanfile.c'\" \(19853 characters\) sed "s/^X//" >'src/hanfile.c' <<'END_OF_FILE' X/*- X * $Id: hanfile.c,v 1.30 90/06/04 23:17:33 Rhialto Rel $ X * $Log: hanfile.c,v $ X * Revision 1.30 90/06/04 23:17:33 Rhialto X * Release 1 Patch 3 X * X * HANFILE.C X * X * The code for the messydos file system handler. X * X * This parts handles files and the File Allocation Table. X * X * This code is (C) Copyright 1989,1990 by Olaf Seibert. All rights reserved. X * May not be used or copied without a licence. X-*/ X X#include "dos.h" X#include "han.h" X X#ifdef HDEBUG X# define debug(x) dbprintf x X#else X# define debug(x) X#endif X Xextern char DotDot[1 + 8 + 3]; X X/* X * Read the FAT from the disk, and count the free clusters. X */ X Xint XGetFat() X{ X int i; X byte *secptr; X X if (!Fat && !(Fat = AllocMem((long) Disk.bps * Disk.spf, BufMemType))) { X debug(("No memory for FAT\n")); X return 0; X } X FatDirty = FALSE; X for (i = 0; i < Disk.spf; i++) { X if (secptr = GetSec(Disk.res + i)) { X CopyMem(secptr, Fat + i * Disk.bps, (long) Disk.bps); X FreeSec(secptr); X } else { X /* q&d way to set the entire FAT to FAT_EOF */ X setmem(Fat + i * Disk.bps, (int) Disk.bps, FAT_EOF); /* 0xFF */ X } X } X X debug(("counting free clusters\n")); X X Disk.nsectsfree = 0; X for (i = MS_FIRSTCLUST; i <= Disk.maxclst; i++) { X if (GetFatEntry((word) i) == FAT_UNUSED) X Disk.nsectsfree += Disk.spc; X } X X return 1; X} X Xvoid XFreeFat() X{ X if (Fat) { X FreeMem(Fat, (long) Disk.bps * Disk.spf); X Fat = NULL; X FatDirty = FALSE; X } X} X X/*- X * The FAT consists of 12-bits entries for each cluster, X * indicating the next cluster in the chain, or FFF for EOF. X * X * Every two entries are packed in three bytes, like this: X * X * Two entries abc 123 (for one cluster and the next) X * are packed as bc 3a 12 X-*/ X Xword XGetFatEntry(cluster) Xword cluster; X{ X if (!Fat && !GetFat()) X return FAT_EOF; X X if (Disk.fat16bits) { X return OtherEndianWord(((word *)Fat)[cluster]); X } else { X register int offset = 3 * (cluster / 2); X register word twoentries; X X if (cluster & 1) { X twoentries = Fat[offset + 1] >> 4; X twoentries |= Fat[offset + 2] << 4; X } else { X twoentries = Fat[offset]; X twoentries |= (Fat[offset + 1] & 0x0F) << 8; X } X X /* X * Convert the special values 0xFF7 .. 0xFFF to 16 bits so they X * can be checked consistently. X */ X if (twoentries >= 0xFF7) X twoentries |= 0xF000; X X return twoentries; X } X} X X#ifndef READONLY X Xvoid XSetFatEntry(cluster, value) Xword cluster; Xword value; X{ X if (!Fat && !GetFat()) X return; X X if (Disk.fat16bits) { X ((word *)Fat)[cluster] = OtherEndianWord(value); X } else { X register int offset = 3 * (cluster / 2); X X if (cluster & 1) { /* 123 kind of entry */ X Fat[offset + 2] = value >> 4; X Fat[offset + 1] &= 0x0F; X Fat[offset + 1] |= (value & 0x0F) << 4; X } else { /* abc kind of entry */ X Fat[offset + 0] = value; X Fat[offset + 1] &= 0xF0; X Fat[offset + 1] |= (value >> 8) & 0x0F; X } X } X X FatDirty = TRUE; X} X X/* X * Find a free cluster to install as the one following this one. Start X * looking for it right after the given one, so we allocate the cluster X * chain as contiguous as possible. If we run off the end of the disk, we X * start again at the beginning. The termination test should not be X * necessary (and won't work if we are given MSFIRSTCLUST - 1) but won't X * harm either. X */ X Xword XFindFreeCluster(prev) Xword prev; X{ X register word i; X X if (prev == 0 || prev == FAT_EOF) X prev = MS_FIRSTCLUST - 1; X X if (Disk.nsectsfree >= Disk.spc) { X for (i = prev + 1; i != prev; i++) { X if (i > Disk.maxclst) /* Wrap around */ X i = MS_FIRSTCLUST; X if (GetFatEntry(i) == FAT_UNUSED) { X SetFatEntry(i, FAT_EOF); X if (prev >= MS_FIRSTCLUST) X SetFatEntry(prev, i); X Disk.nsectsfree -= Disk.spc; X return i; X } X } X } X return FAT_EOF; X} X X/* X * Add a cluster to a cluster chain. For input, we get some cluster we X * know that is on the chain, even if it is the first one. X */ X Xword XExtendClusterChain(cluster) Xregister word cluster; X{ X register word nextcluster; X X /* X * Find the end of the cluster chain to tack the new cluster on to. X * Then FindFreeCluster will (or won't) extend the chain for us. X */ X if (cluster != 0) X while ((nextcluster = NextCluster(cluster)) != FAT_EOF) { X cluster = nextcluster; X } X X return FindFreeCluster(cluster); X} X X/* X * Free a chain of clusters by setting their FAT entries to FAT_UNUSED. X */ X Xvoid XFreeClusterChain(cluster) Xregister word cluster; X{ X register word nextcluster; X X while (cluster != FAT_EOF) { X nextcluster = NextCluster(cluster); X SetFatEntry(cluster, FAT_UNUSED); X Disk.nsectsfree += Disk.spc; X cluster = nextcluster; X } X} X X#endif /* READONLY */ X X/* X * This routine opens a file. X */ X Xstruct MSFileHandle * XMSOpen(parentdir, name, mode) Xstruct MSFileLock *parentdir; Xchar *name; Xlong mode; X{ X struct MSFileLock *fl; X struct MSFileHandle *fh = NULL; X long lockmode; X X switch (mode) { X case MODE_NEWFILE: X case MODE_READWRITE: X lockmode = EXCLUSIVE_LOCK ^ MODE_CREATEFILE; X break; X default: X mode = MODE_OLDFILE; X case MODE_OLDFILE: X lockmode = SHARED_LOCK; X } X X if (fl = MSLock(parentdir, name, lockmode)) { Xmakefh: X if (fl->msfl_Msd.msd_Attributes & ATTR_DIR) { X error = ERROR_OBJECT_WRONG_TYPE; X MSUnLock(fl); X } else if (fh = AllocMem((long) sizeof (*fh), MEMF_PUBLIC)) { X#ifndef READONLY X /* Do we need to truncate the file? */ X if (mode == MODE_NEWFILE && fl->msfl_Msd.msd_Cluster) { X FreeClusterChain(fl->msfl_Msd.msd_Cluster); X fl->msfl_Msd.msd_Cluster = 0; X fl->msfl_Msd.msd_Filesize = 0; X UpdateFileLock(fl); X } X#endif X fh->msfh_Cluster = fl->msfl_Msd.msd_Cluster; X fh->msfh_SeekPos = 0; X fh->msfh_FileLock = fl; X } else { X error = ERROR_NO_FREE_STORE; X MSUnLock(fl); X } X return fh; X } X#ifndef READONLY X /* X * If the file was not found, see if we can make a new one. Therefore X * we need to have an empty spot in the desired directory, and create X * an MSFileLock for it. X */ X X if (!(lockmode & MODE_CREATEFILE) && (fl = EmptyFileLock)) { X debug(("Creating new file\n")); X EmptyFileLock = NULL; X fl->msfl_Msd.msd_Attributes = ATTR_ARCHIVED; X UpdateFileLock(fl); X X goto makefh; X } X if (EmptyFileLock) { X MSUnLock(EmptyFileLock); X EmptyFileLock = NULL; X } X#endif X X return NULL; X} X Xvoid XMSClose(fh) Xregister struct MSFileHandle *fh; X{ X if (fh) { X MSUnLock(fh->msfh_FileLock); X FreeMem(fh, (long) sizeof (*fh)); X } X} X Xlong XMSSeek(fh, position, mode) Xstruct MSFileHandle *fh; Xlong position; Xlong mode; X{ X long oldpos = fh->msfh_SeekPos; X long newpos = oldpos; X long filesize = fh->msfh_FileLock->msfl_Msd.msd_Filesize; X word cluster = fh->msfh_Cluster; X word oldcluster; X word newcluster; X X switch (mode) { X case OFFSET_BEGINNING: X newpos = position; X break; X case OFFSET_CURRENT: X newpos += position; X break; X case OFFSET_END: X newpos = filesize - position; X break; X } X X if (newpos < 0 || newpos > filesize) { X error = ERROR_SEEK_ERROR; X return -1; X } X newcluster = newpos / Disk.bpc; X oldcluster = oldpos / Disk.bpc; X X if (oldcluster > newcluster) { /* Seek backwards */ X cluster = fh->msfh_FileLock->msfl_Msd.msd_Cluster; X oldcluster = 0; X } X if (oldcluster < newcluster) { X if (CheckLock(fh->msfh_FileLock)) X return -1L; X while (oldcluster < newcluster) { X cluster = NextCluster(cluster); X oldcluster++; X } X } X fh->msfh_Cluster = cluster; X fh->msfh_SeekPos = newpos; X X return oldpos; X} X Xlong XMSRead(fh, userbuffer, size) Xregister struct MSFileHandle *fh; Xregister byte *userbuffer; Xregister long size; X{ X long oldsize; X X if (CheckLock(fh->msfh_FileLock)) X return -1L; X X if (fh->msfh_SeekPos + size > fh->msfh_FileLock->msfl_Msd.msd_Filesize) X size = fh->msfh_FileLock->msfl_Msd.msd_Filesize - fh->msfh_SeekPos; X X oldsize = size; X X while (size > 0) { X word offset; X word sector; X byte *diskbuffer; X long insector; X long tocopy; X X offset = fh->msfh_SeekPos % Disk.bpc; X sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset); X if (diskbuffer = GetSec(sector)) { X offset %= Disk.bps; X insector = Disk.bps - offset; X tocopy = lmin(size, insector); X X CopyMem(diskbuffer + offset, userbuffer, tocopy); X userbuffer += tocopy; X size -= tocopy; X FreeSec(diskbuffer); X /* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */ X if ((fh->msfh_SeekPos += tocopy) % Disk.bpc == 0) X fh->msfh_Cluster = NextCluster(fh->msfh_Cluster); X } else { /* Read error. Return amount successfully X * read, if any. Else return -1 for error. */ X if (size == oldsize) { X return -1L; X } X return oldsize - size; X } X } X X return oldsize; X} X Xlong XMSWrite(fh, userbuffer, size) Xregister struct MSFileHandle *fh; Xregister byte *userbuffer; Xregister long size; X{ X#ifdef READONLY X return -1; X#else X long oldsize; X struct MSFileLock *fl = fh->msfh_FileLock; X word prevclust = fl->msfl_Msd.msd_Cluster; X word update = 0; X X if (CheckLock(fl)) X return -1; X X if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) { X error = ERROR_WRITE_PROTECTED; X return -1; X } X X oldsize = size; X X while (size > 0) { X /* X * Do we need to extend the file? X */ X X if (fh->msfh_Cluster == 0 || fh->msfh_Cluster == FAT_EOF) { X word newclust; X X newclust = ExtendClusterChain(prevclust); X debug(("Extend with %d\n", newclust)); X if (newclust != FAT_EOF) { X if (prevclust == 0) { /* Record first cluster in dir */ X fl->msfl_Msd.msd_Cluster = newclust; X } X fh->msfh_Cluster = newclust; X prevclust = newclust; X } else { X error = ERROR_DISK_FULL; X goto error; X } X } X { X word offset; X word sector; X byte *diskbuffer; X long insector; X long tocopy; X X offset = fh->msfh_SeekPos % Disk.bpc; X sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset); X offset %= Disk.bps; X insector = Disk.bps - offset; X tocopy = lmin(size, insector); X X if (tocopy == Disk.bps) X diskbuffer = EmptySec(sector); X else X diskbuffer = GetSec(sector); X X if (diskbuffer != NULL) { X CopyMem(userbuffer, diskbuffer + offset, tocopy); X userbuffer += tocopy; X size -= tocopy; X MarkSecDirty(diskbuffer); X FreeSec(diskbuffer); X /* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */ X if ((fh->msfh_SeekPos += tocopy) % Disk.bpc == 0) X fh->msfh_Cluster = NextCluster(fh->msfh_Cluster); X if (fh->msfh_SeekPos > fl->msfl_Msd.msd_Filesize) X fl->msfl_Msd.msd_Filesize = fh->msfh_SeekPos; X fl->msfl_Msd.msd_Attributes |= ATTR_ARCHIVED; X update = 1; X } else { /* Write error. */ X error: X if (update) X UpdateFileLock(fl); X#if 1 X return -1; /* We loose the information about how much X * data we wrote, but the standard file system X * seems to do it this way. */ X#else X if (size == oldsize) { X return -1; X } X return oldsize - size; /* Amount successfully written */ X#endif X } X } X } X X if (update) X UpdateFileLock(fl); X X return oldsize; X#endif X} X Xlong XMSDeleteFile(parentdir, name) Xstruct MSFileLock *parentdir; Xbyte *name; X{ X#ifdef READONLY X return DOSFALSE; X#else X register struct MSFileLock *fl; X X fl = MSLock(parentdir, name, EXCLUSIVE_LOCK); X if (fl) { X if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) { X error = ERROR_DELETE_PROTECTED; X goto error; X } X if (fl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY) { X struct FileInfoBlock fib; X X /* X * We normally can't get REAL exclusive locks on directories, X * so we check here just to be sure. We don't want to delete X * anyone's current directory, do we? X */ X X if (fl->msfl_Refcount != 1 || fl == RootLock) { X error = ERROR_OBJECT_IN_USE; X goto error; X } X if (MSExamine(fl, &fib) && /* directory itself */ X MSExNext(fl, &fib)) { /* should fail */ X if (error == 0) { X not_empty: X error = ERROR_DIRECTORY_NOT_EMPTY; X error: X MSUnLock(fl); X return DOSFALSE; X } X } X if (error != ERROR_NO_MORE_ENTRIES) X goto error; X X error = 0; X } X if (fl->msfl_Msd.msd_Cluster) X FreeClusterChain(fl->msfl_Msd.msd_Cluster); X fl->msfl_Msd.msd_Name[0] = DIR_DELETED; X WriteFileLock(fl); X MSUnLock(fl); X X return DOSTRUE; X } X return DOSFALSE; X#endif X} X Xlong XMSSetDate(parentdir, name, datestamp) Xstruct MSFileLock *parentdir; Xbyte *name; Xstruct DateStamp *datestamp; X{ X#ifdef READONLY X return DOSFALSE; X#else X register struct MSFileLock *fl; X X fl = MSLock(parentdir, name, EXCLUSIVE_LOCK); X if (fl) { X ToMSDate(&fl->msfl_Msd.msd_Date, &fl->msfl_Msd.msd_Time, datestamp); X WriteFileLock(fl); X MSUnLock(fl); X X return DOSTRUE; X } X return DOSFALSE; X#endif X} X X/* X * Create a new directory, with its own initial "." and ".." entries. X */ X Xstruct MSFileLock * XMSCreateDir(parentdir, name) Xstruct MSFileLock *parentdir; Xbyte *name; X{ X#ifdef READONLY X return DOSFALSE; X#else X register struct MSFileLock *fl; X X /* X * Go create a new file. If we fail later, we have an empty file that X * we delete again. X */ X X fl = MSLock(parentdir, name, EXCLUSIVE_LOCK ^ MODE_CREATEFILE); X if (fl || error == ERROR_OBJECT_IN_USE) { X error = ERROR_OBJECT_EXISTS; X goto error; X } X if (error != 0) { X goto error; X } X if (fl = EmptyFileLock) { X debug(("Creating new dir\n")); X EmptyFileLock = NULL; X if ((fl->msfl_Msd.msd_Cluster = FindFreeCluster(FAT_EOF)) != FAT_EOF) { X struct MsDirEntry direntry; X byte *sec; X word sector; X X sector = ClusterToSector(fl->msfl_Msd.msd_Cluster); X sec = EmptySec(sector); X if (sec == NULL) X goto error_no_free_store; X setmem(sec, (int) Disk.bps, 0); X X /* X * Turn the file into a directory. X */ X fl->msfl_Msd.msd_Attributes = ATTR_DIRECTORY | ATTR_ARCHIVED; X UpdateFileLock(fl); X X /* X * Create the "." entry. X */ X direntry = fl->msfl_Msd; X strncpy(direntry.msd_Name, DotDot + 1, 8 + 3); X OtherEndianMsd(&direntry); X ((struct MsDirEntry *) sec)[0] = direntry; X X /* X * Get the real parent directory because we will duplicate the X * directory entry in the subdirectory. X */ X X parentdir = MSParentDir(fl); X if (parentdir == NULL) /* Cannot happen */ X parentdir = MSDupLock(RootLock); X X /* X * Create the ".." entry. X */ X direntry = parentdir->msfl_Msd; X strncpy(direntry.msd_Name, DotDot, 8 + 3); X direntry.msd_Attributes = ATTR_DIRECTORY | ATTR_ARCHIVED; X OtherEndianMsd(&direntry); X ((struct MsDirEntry *) sec)[1] = direntry; X X MSUnLock(parentdir); X X MarkSecDirty(sec); X FreeSec(sec); X X /* X * Clear out the rest of the newly created directory. X */ X X while ((sector = NextClusteredSector(sector)) != SEC_EOF) { X sec = EmptySec(sector); X if (sec == NULL) X goto error_no_free_store; X setmem(sec, (int) Disk.bps, 0); X MarkSecDirty(sec); X FreeSec(sec); X } X } else { X MSUnLock(fl); X fl = NULL; X MSDeleteFile(parentdir, name); X error = ERROR_DISK_FULL; X } X } X if (EmptyFileLock) { X MSUnLock(EmptyFileLock); X EmptyFileLock = NULL; X } X return fl; X Xerror_no_free_store: X error = ERROR_NO_FREE_STORE; Xerror: X if (fl) X MSUnLock(fl); X return DOSFALSE; X#endif X} X X/* X * Rename a file or directory, possibly moving it to a different X * directory. X * X * "Tuned" to also work in full directories by first deleting the source X * name, then look for a slot to put the destination name. If that fails, X * we undo the deletion. By playing with the cache, we even avoid a write X * of the sector with the undeleted entry. X */ X Xlong XMSRename(slock, sname, dlock, dname) Xstruct MSFileLock *slock; Xbyte *sname; Xstruct MSFileLock *dlock; Xbyte *dname; X{ X#ifdef READONLY X return DOSFALSE; X#else X struct MSFileLock *sfl; X struct MSFileLock *dfl; X long success; X struct CacheSec *scache; X ulong oldstatus; X X success = DOSFALSE; X scache = NULL; X dfl = NULL; X X sfl = MSLock(slock, sname, SHARED_LOCK); X if (sfl == NULL || sfl == RootLock) X goto error; X X /* X * Now we are going to pull a dirty trick with the cache. We are going X * to temporarily delete the source file, in the chache only, and X * undelete it again if we cannot create the new name. And above all X * we want to avoid unnecessary writes if we decide not to do the X * deletion after all. X */ X { X byte *sec; X byte old; X X if ((sec = GetSec(sfl->msfl_DirSector)) == NULL) X goto error; X scache = FindSecByBuffer(sec); X oldstatus = scache->sec_Refcount; X X old = sfl->msfl_Msd.msd_Name[0]; X sfl->msfl_Msd.msd_Name[0] = DIR_DELETED; X WriteFileLock(sfl); X sfl->msfl_Msd.msd_Name[0] = old; X X /* X * Don't FreeSec it yet; we don't want it written out to disk. X */ X } X X /* X * Now we have freed the directory entry of the source name, we might X * be able to use it for the destination name. But only if we also X * temporarily hide the MSFileLock on that spot. Gross hack ahead! X */ X X sfl->msfl_DirOffset = ~sfl->msfl_DirOffset; X dfl = MSLock(dlock, dname, EXCLUSIVE_LOCK ^ MODE_CREATEFILE); X sfl->msfl_DirOffset = ~sfl->msfl_DirOffset; X X if (dfl != NULL || error == ERROR_OBJECT_IN_USE) { X error = ERROR_OBJECT_EXISTS; X goto undelete; X } X dfl = EmptyFileLock; X EmptyFileLock = NULL; X if (dfl == NULL) { X /* X * Sigh, we could not create the new name. But because of that, we X * are sure that we need to write nothing to the disk at all. So X * we can safely reset the sector-dirty flag to what it was X * before, if we also restore the cached sector. X */ Xundelete: X WriteFileLock(sfl); X scache->sec_Refcount = oldstatus; X goto error; X } X /* X * Now, if the moved entry was a directory, and it was moved to a X * different directory, we need to adapt its "..", which is the second X * entry. X */ X X if (sfl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY && X sfl->msfl_Parent != dfl->msfl_Parent) { X struct MSFileLock *parentdir; X struct MsDirEntry *dir; X X if (dir = (struct MsDirEntry *) X GetSec(DirClusterToSector(sfl->msfl_Msd.msd_Cluster))) { X parentdir = MSParentDir(dfl); X /* X * Copy everything except the name which must remain "..". But X * first a quick consistency check... X */ X debug(("Creating new \"..\" ")); X if (dir[1].msd_Name[1] == '.') { X CopyMem(&parentdir->msfl_Msd.msd_Attributes, X &dir[1].msd_Attributes, X (long) sizeof (struct MsDirEntry) - X OFFSETOF(MsDirEntry, msd_Attributes)); X dir[1].msd_Attributes = ATTR_DIRECTORY; X OtherEndianMsd(&dir[1]); X MarkSecDirty(dir); X } X#ifdef HDEBUG X else X debug(("!!! No \"..\" found ??\n")); X#endif X MSUnLock(parentdir); X FreeSec(dir); X } X } X /* X * Move the name from the new entry to the old filelock. We do this X * for the case that somebody else has a lock on the (possibly moved) X * file/directory. Also move the other administration. X */ X X strncpy(sfl->msfl_Msd.msd_Name, dfl->msfl_Msd.msd_Name, 8 + 3); X sfl->msfl_DirSector = dfl->msfl_DirSector; X sfl->msfl_DirOffset = dfl->msfl_DirOffset; X /* X * Free the old, and get the new parent directory. They might be the X * same, of course... X */ X MSUnLock(sfl->msfl_Parent); X sfl->msfl_Parent = dfl->msfl_Parent; X dfl->msfl_Parent = NULL; X sfl->msfl_Msd.msd_Attributes |= ATTR_ARCHIVED; X WriteFileLock(sfl); /* Write the new name; the old name X * already has been deleted. */ X success = DOSTRUE; X Xerror: X if (sfl) X MSUnLock(sfl); X if (dfl) X MSUnLock(dfl); X if (scache) X FreeSec(scache->sec_Data); X X return success; X#endif X} END_OF_FILE if test 19853 -ne `wc -c <'src/hanfile.c'`; then echo shar: \"'src/hanfile.c'\" unpacked with wrong size! fi # end of 'src/hanfile.c' fi echo shar: End of archive 3 \(of 6\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 4 5 6 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 6 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Mail submissions (sources or binaries) to <amiga@cs.odu.edu>. Mail comments to the moderator at <amiga-request@cs.odu.edu>. Post requests for sources, and general discussion to comp.sys.amiga.