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 216 Archive-name: devices/msh-1.30/part04 #!/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 4 (of 6)." # Contents: src/hansec.c src/pack.c # Wrapped by tadguy@xanth on Sun Jul 15 19:59:09 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'src/hansec.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/hansec.c'\" else echo shar: Extracting \"'src/hansec.c'\" \(20964 characters\) sed "s/^X//" >'src/hansec.c' <<'END_OF_FILE' X/*- X * $Id: hansec.c,v 1.30 90/06/04 23:17:02 Rhialto Rel $ X * $Log: hansec.c,v $ X * Revision 1.30 90/06/04 23:17:02 Rhialto X * Release 1 Patch 3 X * X * HANSEC.C X * X * The code for the messydos file system handler. X * X * Sector-level stuff: read, write, cache, unit conversion. X * Other interactions (via MyDoIO) with messydisk.device. 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/*#undef HDEBUG /**/ X#ifdef HDEBUG X# define debug(x) dbprintf x X#else X# define debug(x) X#endif X Xstruct MsgPort *DiskReplyPort; Xstruct IOExtTD *DiskIOReq; Xstruct IOStdReq *DiskChangeReq; X Xstruct DiskParam Disk; Xbyte *Fat; Xshort FatDirty; /* Fat must be written to disk */ X Xshort error; /* To put the error value; for Result2 */ Xlong IDDiskState; /* InfoData.id_DiskState */ Xlong IDDiskType; /* InfoData.id_DiskType */ Xstruct timerequest *TimeIOReq; /* For motor-off delay */ Xstruct MinList CacheList; /* Sector cache */ Xint CurrentCache; /* How many cached buffers do we have */ Xint MaxCache; /* Maximum amount of cached buffers */ Xlong CacheBlockSize; /* Size of disk block + overhead */ Xulong BufMemType; Xint DelayState; Xshort CheckBootBlock; /* Do we need to check the bootblock? */ X Xword XGet8086Word(Word8086) Xregister byte *Word8086; X{ X return Word8086[0] | Word8086[1] << 8; X} X Xword XOtherEndianWord(oew) Xword oew; X{ X/* INDENT OFF */ X#asm X move.w 8(a5),d0 X rol.w #8,d0 X#endasm X /* INDENT ON */ X /* X * return (oew << 8) | ((oew >> 8) & 0xff); X */ X} X Xulong XOtherEndianLong(oel) Xulong oel; X{ X/* INDENT OFF */ X#asm X move.l 8(a5),d0 X rol.w #8,d0 X swap d0 X rol.w #8,d0 X#endasm X /* INDENT ON */ X /* X * return ((oel & 0xff) << 24) | ((oel & 0xff00) << 8) | X * ((oel & 0xff0000) >> 8) | ((oel & 0xff000000) >> 24); X */ X} X Xvoid XOtherEndianMsd(msd) Xregister struct MsDirEntry *msd; X{ X msd->msd_Date = OtherEndianWord(msd->msd_Date); X msd->msd_Time = OtherEndianWord(msd->msd_Time); X msd->msd_Cluster = OtherEndianWord(msd->msd_Cluster); X msd->msd_Filesize = OtherEndianLong(msd->msd_Filesize); X} X Xword XClusterToSector(cluster) Xregister word cluster; X{ X return cluster ? Disk.start + cluster * Disk.spc X : 0; X} X Xword XClusterOffsetToSector(cluster, offset) Xregister word cluster; Xregister word offset; X{ X return cluster ? Disk.start + cluster * Disk.spc + offset / Disk.bps X : 0; X} X Xword XDirClusterToSector(cluster) Xregister word cluster; X{ X return cluster ? Disk.start + cluster * Disk.spc X : Disk.rootdir; X} X Xword XSectorToCluster(sector) Xregister word sector; X{ X return sector ? (sector - Disk.start) / Disk.spc X : 0; X} X X/* X * Get the next cluster in a chain. Sort-of checks for special entries. X */ X Xword XNextCluster(cluster) Xword cluster; X{ X register word entry; X X return (entry = GetFatEntry(cluster)) >= 0xFFF7 ? FAT_EOF : entry; X} X Xword XNextClusteredSector(sector) Xword sector; X{ X word next = (sector + 1 - Disk.start) % Disk.spc; X X if (next == 0) { X next = NextCluster(SectorToCluster(sector)); X return next != FAT_EOF ? ClusterToSector(next) X : SEC_EOF; X } else X return sector + 1; X} X X#ifndef READONLY X Xword XFindFreeSector(prev) Xword prev; X{ X word freecluster = FindFreeCluster(SectorToCluster(prev)); X X return freecluster == FAT_EOF ? SEC_EOF : ClusterToSector(freecluster); X} X X#endif X X/* X * Find a specific sector. The cache list is a Least Recently Used stack: X * Put it on the head of the cache list. So if it is not used anymore in a X * long time, it bubbles to the end of the list, getting a higher chance X * of being trashed for re-use. X */ X Xstruct CacheSec * XFindSecByNumber(number) Xregister int number; X{ X register struct CacheSec *sec; X register struct CacheSec *nextsec; X X debug(("FindSecByNumber %d", number)); X X for (sec = (void *) CacheList.mlh_Head; X nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) { X if (sec->sec_Number == number) { X debug((" (%x) %lx\n", sec->sec_Refcount, sec)); X Remove(sec); X AddHead(&CacheList, &sec->sec_Node); X return sec; X } X } X X debug(("; ")); X return NULL; X} X Xstruct CacheSec * XFindSecByBuffer(buffer) Xbyte *buffer; X{ X return (struct CacheSec *) (buffer - OFFSETOF(CacheSec, sec_Data)); X} X X/* X * Get a fresh cache buffer. If we are allowed more cache, we just X * allocate memory. Otherwise, we try to find a currently unused buffer. X * We start looking at the end of the list, which is the bottom of the LRU X * stack. If that fails, allocate more memory anyway. Not that is likely X * anyway, since we currently lock only one sector at a time. X */ X Xstruct CacheSec * XNewCacheSector() X{ X register struct CacheSec *sec; X register struct CacheSec *nextsec; X X debug(("NewCacheSector\n")); X X if (CurrentCache < MaxCache) { X if (sec = AllocMem(CacheBlockSize, BufMemType)) { X goto add; X } X } X for (sec = (void *) CacheList.mlh_TailPred; X nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) { X if ((CurrentCache >= MaxCache) && (sec->sec_Refcount == SEC_DIRTY)) { X FreeCacheSector(sec); /* Also writes it to disk */ X continue; X } X if (sec->sec_Refcount == 0) /* Implies not SEC_DIRTY */ X return sec; X } X X sec = AllocMem(CacheBlockSize, BufMemType); X X if (sec) { Xadd: X CurrentCache++; X AddHead(&CacheList, &sec->sec_Node); X } else X error = ERROR_NO_FREE_STORE; X X return sec; X} X X/* X * Dispose a cached sector, even if it has a non-zero refcount. If it is X * dirty, write it out. X */ X Xvoid XFreeCacheSector(sec) Xregister struct CacheSec *sec; X{ X debug(("FreeCacheSector %d\n", sec->sec_Number)); X Remove(sec); X#ifndef READONLY X if (sec->sec_Refcount & SEC_DIRTY) { X PutSec(sec->sec_Number, sec->sec_Data); X } X#endif X FreeMem(sec, CacheBlockSize); X CurrentCache--; X} X X/* X * Create an empty cache list X */ X Xvoid XInitCacheList() X{ X extern struct CacheSec *sec; /* Of course this does not exist... */ X X NewList(&CacheList); X CurrentCache = 0; X CacheBlockSize = Disk.bps + sizeof (*sec) - sizeof (sec->sec_Data); X} X X/* X * Dispose all cached sectors, possibly writing them to disk. X */ X Xvoid XFreeCacheList() X{ X register struct CacheSec *sec; X X debug(("FreeCacheList, %d\n", CurrentCache)); X while (sec = GetHead(&CacheList)) { X FreeCacheSector(sec); X } X} X X/* X * Do an insertion sort on tosort in the CacheList. Since it changes the X * location in the list, you must fetch it before calling this routine. X * The list will become ascending. X */ X Xvoid XSortSec(tosort) Xregister struct CacheSec *tosort; X{ X register struct CacheSec *sec; X struct CacheSec *nextsec; X register word secno; X X secno = tosort->sec_Number; X debug(("SortSec %d: ", secno)); X X for (sec = (void *) CacheList.mlh_Head; X nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) { X debug(("%d, ", sec->sec_Number)); X if (sec == tosort) { X debug(("\n")); X return; /* No need to move it away */ X } X if (sec->sec_Number > secno) X break; X } X /* Insert before sec */ X Remove(tosort); X Insert(&CacheList, tosort, sec->sec_Node.mln_Pred); X debug(("\n")); X} X X/* X * Write all dirty cache buffers to disk. They are written from highest to X * lowest, and then the FAT is written out. X */ X Xvoid XMSUpdate(immediate) Xint immediate; X{ X register struct CacheSec *sec; X register struct CacheSec *nextsec; X X debug(("MSUpdate\n")); X X#ifndef READONLY X if (DelayState & DELAY_DIRTY) { X /* X * First sort all dirty sectors on block number X */ X for (sec = (void *) CacheList.mlh_Head; X nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) { X if (sec->sec_Refcount & SEC_DIRTY) { X SortSec(sec); X } X } X /* X * Then do a second (backward) scan to write them out. X */ X for (sec = (void *) CacheList.mlh_TailPred; X nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) { X if (sec->sec_Refcount & SEC_DIRTY) { X PutSec(sec->sec_Number, sec->sec_Data); X sec->sec_Refcount &= ~SEC_DIRTY; X } X } X DelayState &= ~DELAY_DIRTY; X } X if (FatDirty) { X WriteFat(); X } X#endif X X if (immediate) X DelayState = DELAY_RUNNING1; X X if (DelayState & DELAY_RUNNING2) { X StartTimer(); X DelayState &= ~DELAY_RUNNING2; X } else { /* DELAY_RUNNING1 */ X#ifndef READONLY X while (TDUpdate() != 0 && RetryRwError(DiskIOReq)) X ; X#endif X TDMotorOff(); X DelayState = DELAY_OFF; X } X} X X/* X * Start the timer which triggers cache writing and stopping the disk X * motor. X */ X Xvoid XStartTimer() X{ X DelayState |= DELAY_RUNNING1 | DELAY_RUNNING2; X X if (CheckIO(TimeIOReq)) { X WaitIO(TimeIOReq); X TimeIOReq->tr_node.io_Command = TR_ADDREQUEST; X TimeIOReq->tr_time.tv_secs = 3; X TimeIOReq->tr_time.tv_micro = 0; X SendIO(TimeIOReq); X } X} X X/* X * Get a pointer to a logical sector { 0, ..., MS_SECTCNT - 1}. We X * allocate a buffer and copy the data in, and lock the buffer until X * FreeSec() is called. X */ X Xbyte * XGetSec(sector) Xint sector; X{ X struct CacheSec *sec; X X#ifdef HDEBUG X if (sector == 0) { X debug(("************ GetSec(0) ***************\n")); X } X#endif X X if (sec = FindSecByNumber(sector)) { X sec->sec_Refcount++; X X return sec->sec_Data; X } X if (sec = NewCacheSector()) { X register struct IOExtTD *req; X X sec->sec_Number = sector; X sec->sec_Refcount = 1; X X debug(("GetSec %d\n", sector)); X X req = DiskIOReq; X do { X req->iotd_Req.io_Command = ETD_READ; X req->iotd_Req.io_Data = (APTR)sec->sec_Data; X req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps; X req->iotd_Req.io_Length = Disk.bps; X MyDoIO(req); X } while (req->iotd_Req.io_Error != 0 && RetryRwError(req)); X X StartTimer(); X X if (req->iotd_Req.io_Error == 0) { X return sec->sec_Data; X } X error = ERROR_NOT_A_DOS_DISK; X FreeCacheSector(sec); X } X return NULL; X} X X#ifndef READONLY X Xbyte * XEmptySec(sector) Xint sector; X{ X byte *buffer; X register struct CacheSec *sec; X X#ifdef HDEBUG X if (sector == 0) { X debug(("************ EmptySec(0) ***************\n")); X } X#endif X if (sec = FindSecByNumber(sector)) { X sec->sec_Refcount++; X X return sec->sec_Data; X } X if (sec = NewCacheSector()) { X sec->sec_Number = sector; X sec->sec_Refcount = 1; X X return sec->sec_Data; X } X X return NULL; X} X Xvoid XPutSec(sector, data) Xint sector; Xbyte *data; X{ X register struct IOExtTD *req; X X debug(("PutSec %d\n", sector)); X X req = DiskIOReq; X do { X req->iotd_Req.io_Command = ETD_WRITE; X req->iotd_Req.io_Data = (APTR) data; X req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps; X req->iotd_Req.io_Length = Disk.bps; X MyDoIO(req); X } while (req->iotd_Req.io_Error != 0 && RetryRwError(req)); X X StartTimer(); X} X X#endif X X/* X * Unlock a cached sector. When the usage count drops to zero, which X * implies it is not dirty, and we are over our cache quota, the sector is X * freed. Otherwise we keep it for re-use. X */ X Xvoid XFreeSec(buffer) Xbyte *buffer; X{ X register struct CacheSec *sec; X X if (sec = FindSecByBuffer(buffer)) { X#ifdef HDEBUG X if (sec->sec_Number == 0) { X debug(("************ FreeSec(0) ***************\n")); X } X#endif X if (--sec->sec_Refcount == 0) { /* Implies not SEC_DIRTY */ X if (CurrentCache > MaxCache) { X FreeCacheSector(sec); X } X } X } X} X X#ifndef READONLY X Xvoid XMarkSecDirty(buffer) Xbyte *buffer; X{ X register struct CacheSec *sec; X X if (sec = FindSecByBuffer(buffer)) { X sec->sec_Refcount |= SEC_DIRTY; X DelayState |= DELAY_DIRTY; X StartTimer(); X } X} X X/* X * Write out the FAT. Called from MSUpdate(), so don't call it again from X * here. Don't use precious cache space for it; you could say it has its X * own private cache already. X */ X Xvoid XWriteFat() X{ X register int fat, X sec; X int disksec = Disk.res; /* First FAT, first sector */ X X /* Write all FATs */ X for (fat = 0; fat < Disk.nfats; fat++) { X for (sec = 0; sec < Disk.spf; sec++) { X PutSec(disksec++, Fat + sec * Disk.bps); X /* return; /* Fat STILL dirty! */ X } X } X FatDirty = FALSE; X} X X#endif X Xint XAwaitDFx() X{ X debug(("AwaitDFx\n")); X if (DosType) { X static char dfx[] = "DFx:"; X void *dfxProc, X *DeviceProc(); X char xinfodata[sizeof(struct InfoData) + 3]; X struct InfoData *infoData; X int triesleft; X X dfx[2] = '0' + UnitNr; X infoData = (struct InfoData *)(((long)&xinfodata[3]) & ~3L); X X for (triesleft = 10; triesleft; triesleft--) { X debug(("AwaitDFx %d\n", triesleft)); X if ((dfxProc = DeviceProc(dfx)) == NULL) X break; X X dos_packet(dfxProc, ACTION_DISK_INFO, CTOB(infoData)); X debug(("AwaitDFx %lx\n", infoData->id_DiskType)); X if (infoData->id_DiskType == ID_NO_DISK_PRESENT) { X /* DFx has not noticed yet. Wait a bit. */ X WaitIO(TimeIOReq); X TimeIOReq->tr_node.io_Command = TR_ADDREQUEST; X TimeIOReq->tr_time.tv_secs = 0; X TimeIOReq->tr_time.tv_micro = 750000L; /* .75 s */ X SendIO(TimeIOReq); X continue; X } X if (infoData->id_DiskType == ID_DOS_DISK) { X /* DFx: understands it, so it is not for us. */ X return 1; X } X /* X * All (well, most) other values mean that DFx: does not X * understand it, so we can give it a try. X */ X break; X } X } X return 0; X} X Xint XReadBootBlock() X{ X int protstatus; X X debug(("ReadBootBlock\n")); X FreeFat(); /* before disk parameters change */ X TDClear(); X X if (TDProtStatus() >= 0) { X register byte *bootblock; X short oldCancel; X X oldCancel = Cancel; X X if (AwaitDFx()) X goto bad_disk; X if ((protstatus = TDProtStatus()) < 0) X goto no_disk; X X TDChangeNum(); X debug(("Changenumber = %ld\n", DiskIOReq->iotd_Count)); X X Cancel = 1; X if (bootblock = GetSec(0)) { X word bps; X X if (CheckBootBlock && X /* Atari: empty or 68000 JMP */ X /*bootblock[0] != 0x00 && bootblock[0] != 0x4E &&*/ X /* 8086 ml for a jump */ X bootblock[0] != 0xE9 && bootblock[0] != 0xEB) { X goto bad_disk; X } X bps = Get8086Word(bootblock + 0x0b); X Disk.spc = bootblock[0x0d]; X Disk.res = Get8086Word(bootblock + 0x0e); X Disk.nfats = bootblock[0x10]; X Disk.ndirs = Get8086Word(bootblock + 0x11); X Disk.nsects = Get8086Word(bootblock + 0x13); X Disk.media = bootblock[0x15]; X Disk.spf = Get8086Word(bootblock + 0x16); X Disk.spt = Get8086Word(bootblock + 0x18); X Disk.nsides = Get8086Word(bootblock + 0x1a); X Disk.nhid = Get8086Word(bootblock + 0x1c); X FreeSec(bootblock); X X /* X * Maybe the sector size just changed. Who knows? X */ X if (Disk.bps != bps) { X FreeCacheList(); X Disk.bps = bps; X InitCacheList(); X } X X Disk.ndirsects = (Disk.ndirs * MS_DIRENTSIZE) / Disk.bps; X Disk.rootdir = Disk.res + Disk.spf * Disk.nfats; X Disk.datablock = Disk.rootdir + Disk.ndirsects; X Disk.start = Disk.datablock - MS_FIRSTCLUST * Disk.spc; X /* Available clusters are 2..maxclust in secs start..nsects-1 */ X Disk.maxclst = (Disk.nsects - Disk.start) / Disk.spc - 1; X Disk.bpc = Disk.bps * Disk.spc; X Disk.vollabel = FakeRootDirEntry; X/* Disk.fat16bits = Disk.nsects > 20740; /* DOS3.2 magic value */ X Disk.fat16bits = Disk.maxclst >= 0xFF7; /* DOS3.0 magic value */ X X debug(("%x\tbytes per sector\n", Disk.bps)); X debug(("%x\tsectors per cluster\n", Disk.spc)); X debug(("%x\treserved blocks\n", Disk.res)); X debug(("%x\tfats\n", Disk.nfats)); X debug(("%x\tdirectory entries\n", Disk.ndirs)); X debug(("%x\tsectors\n", Disk.nsects)); X debug(("%x\tmedia byte\n", Disk.media)); X debug(("%x\tsectors per FAT\n", Disk.spf)); X debug(("%x\tsectors per track\n", Disk.spt)); X debug(("%x\tsides\n", Disk.nsides)); X debug(("%x\thidden sectors\n", Disk.nhid)); X X debug(("%x\tdirectory sectors\n", Disk.ndirsects)); X debug(("%x\troot dir block\n", Disk.rootdir)); X debug(("%x\tblock for (imaginary) cluster 0\n", Disk.start)); X debug(("%x\tfirst data block\n", Disk.datablock)); X debug(("%x\tclusters total\n", Disk.maxclst)); X debug(("%x\tbytes per cluster\n", Disk.bpc)); X debug(("%x\t16-bits FAT?\n", Disk.fat16bits)); X X IDDiskType = ID_DOS_DISK; X#ifdef READONLY X IDDiskState = ID_WRITE_PROTECTED; X#else X if (protstatus > 0) X IDDiskState = ID_WRITE_PROTECTED; X else X IDDiskState = ID_VALIDATED; X#endif X X if (Disk.nsects / (MS_SPT * Disk.nsides) <= 40) X DiskIOReq->iotd_Req.io_Flags |= IOMDF_40TRACKS; X else X DiskIOReq->iotd_Req.io_Flags &= ~IOMDF_40TRACKS; X X GetFat(); X } else { X debug(("Can't read %d.\n", DiskIOReq->iotd_Req.io_Error)); X bad_disk: X FreeCacheList(); X error = ERROR_NO_DISK; X IDDiskType = ID_UNREADABLE_DISK; X IDDiskState = ID_WRITE_PROTECTED; X } X Cancel = oldCancel; X } X#ifdef HDEBUG X else debug(("No disk inserted %d.\n", DiskIOReq->iotd_Req.io_Error)); X#endif Xno_disk: X return 1; X} X X/* X * We try to identify the disk currently in the drive, trying to find the X * volume label in the first directory block. X */ X Xint XIdentifyDisk(name, date) Xchar *name; /* Should be at least 32 characters */ Xstruct DateStamp *date; X{ X debug(("IdentifyDisk\n")); X ReadBootBlock(); /* Also sets default vollabel */ X X if (IDDiskType == ID_DOS_DISK) { X byte *dirblock; X register struct MsDirEntry *dirent; X X if (dirblock = GetSec(Disk.rootdir)) { X dirent = (struct MsDirEntry *) dirblock; X X while ((byte *) dirent < &dirblock[Disk.bps]) { X if (dirent->msd_Attributes & ATTR_VOLUMELABEL) { X Disk.vollabel.de_Msd = *dirent; X Disk.vollabel.de_Sector = Disk.rootdir; X Disk.vollabel.de_Offset = (byte *) dirent - dirblock; X OtherEndianMsd(&Disk.vollabel.de_Msd); X Disk.vollabel.de_Msd.msd_Cluster = 0; /* to be sure */ X break; X } X dirent++; X } X strncpy(name, Disk.vollabel.de_Msd.msd_Name, 8 + 3); X name[8 + 3] = '\0'; X ZapSpaces(name, name + 8 + 3); X ToDateStamp(date, Disk.vollabel.de_Msd.msd_Date, X Disk.vollabel.de_Msd.msd_Time); X debug(("Disk is called '%s'\n", name)); X X FreeSec(dirblock); X X return 0; X } X } X return 1; X} X X/* X * Remove the disk change SoftInt. The V1.2 / V1.3 version is broken, so X * we use a workaround. The correct thing to do is shown but not used. X */ X Xvoid XTDRemChangeInt() X{ X if (DiskChangeReq) { X register struct IOExtTD *req = DiskIOReq; X X#if 0 /* V1.2 and V1.3 have a broken X * TD_REMCHANGEINT */ X req->iotd_Req.io_Command = TD_REMCHANGEINT; X req->iotd_Req.io_Data = (void *) DiskChangeReq; X MyDoIO(req); X WaitIO(DiskChangeReq); X#else X Forbid(); X Remove(DiskChangeReq); X Permit(); X#endif X DeleteExtIO(DiskChangeReq); X DiskChangeReq = NULL; X } X} X X/* X * Set the disk change SoftInt. Return nonzero on failure. X */ X Xint XTDAddChangeInt(interrupt) Xstruct Interrupt *interrupt; X{ X register struct IOExtTD *req = DiskIOReq; X X if (DiskChangeReq) { X TDRemChangeInt(); X } X DiskChangeReq = (void *)CreateExtIO(DiskReplyPort, X (long) sizeof (*DiskChangeReq)); X if (DiskChangeReq) { X /* Clone IO request part */ X DiskChangeReq->io_Device = req->iotd_Req.io_Device; X DiskChangeReq->io_Unit = req->iotd_Req.io_Unit; X DiskChangeReq->io_Command = TD_ADDCHANGEINT; X DiskChangeReq->io_Data = (void *) interrupt; X SendIO(DiskChangeReq); X X return 0; X } X return 1; X} X X/* X * Get the current disk change number. Necessary for ETD_ commands. Makes X * absolutely sure nobody can change the disk without us noticing it. X */ X Xint XTDChangeNum() X{ X register struct IOExtTD *req = DiskIOReq; X X req->iotd_Req.io_Command = TD_CHANGENUM; X MyDoIO(req); X req->iotd_Count = req->iotd_Req.io_Actual; X X return req->iotd_Req.io_Actual; X} X X/* X * Get the current write protection state. X * X * Zero means writable, one means write protected, minus one means X * no disk in drive. X */ X Xint XTDProtStatus() X{ X register struct IOExtTD *req = DiskIOReq; X X req->iotd_Req.io_Command = TD_PROTSTATUS; X MyDoIO(req); X X if (req->iotd_Req.io_Error) X return -1; X X return req->iotd_Req.io_Actual != 0; X} X X/* X * Switch the drive motor off. Return previous state. Don't use this when X * you have allocated the disk via GetDrive(). X */ X Xint XTDMotorOff() X{ X register struct IOExtTD *req = DiskIOReq; X X req->iotd_Req.io_Command = TD_MOTOR; X req->iotd_Req.io_Length = 0; X MyDoIO(req); X X return req->iotd_Req.io_Actual; X} X X/* X * Clear all internal messydisk buffers. X */ X Xint XTDClear() X{ X register struct IOExtTD *req = DiskIOReq; X X req->iotd_Req.io_Command = CMD_CLEAR; X X return MyDoIO(req); X} X X#ifndef READONLY X/* X * Write out all internal messydisk buffers to the disk. X */ X Xint XTDUpdate() X{ X register struct IOExtTD *req = DiskIOReq; X X req->iotd_Req.io_Command = ETD_UPDATE; X X return MyDoIO(req); X} X#endif X Xint XMyDoIO(ioreq) Xregister struct IOStdReq *ioreq; X{ X ioreq->io_Flags |= IOF_QUICK; /* Preserve IOMDF_40TRACKS */ X BeginIO(ioreq); X return WaitIO(ioreq); X} END_OF_FILE if test 20964 -ne `wc -c <'src/hansec.c'`; then echo shar: \"'src/hansec.c'\" unpacked with wrong size! fi # end of 'src/hansec.c' fi if test -f 'src/pack.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/pack.c'\" else echo shar: Extracting \"'src/pack.c'\" \(26338 characters\) sed "s/^X//" >'src/pack.c' <<'END_OF_FILE' X/*- X * $Id: pack.c,v 1.30 90/06/04 23:15:58 Rhialto Rel $ X * $Log: pack.c,v $ X * Revision 1.30 90/06/04 23:15:58 Rhialto X * Release 1 Patch 3 X * X * Originally: X * X * DOSDEVICE.C V1.10 2 November 1987 X * X * EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C PUBLIC DOMAIN. X * X * By Matthew Dillon. X * X * This has been stripped and refilled with messydos code X * by Olaf Seibert. 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 * Please note that we are NOT pure, so if you wish to mount X * multiple MSDOS units, you must use different copies of this driver. X * X * This file forms the interface between the actual handler code and all X * AmigaDOS requirements. It shields it from ugly stuff like BPTRs, BSTRs, X * FileLocks, FileHandles and VolumeNodes (in the form of DeviceLists). X * Also, most protection against non-inserted disks is done here. 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 X/* X * Since this code might be called several times in a row without being X * unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!! This also goes X * for any global/static assignments that might be changed by running the X * code. X */ X XPORT *DosPort; /* Our DOS port... */ XDEVNODE *DevNode; /* Our DOS node.. created by DOS for us */ XDEVLIST *VolNode; /* Device List structure for our volume X * node */ X Xvoid *SysBase; /* EXEC library base */ XDOSLIB *DOSBase; /* DOS library base for debug process */ Xlong PortMask; /* The signal mask for our DosPort */ Xlong WaitMask; /* The signal mask to wait for */ Xshort DiskChanged; /* Set by disk change interrupt */ Xshort Inhibited; /* Are we inhibited (ACTION_INHIBIT)? */ Xlong UnitNr; /* From */ Xchar *DevName; /* the */ Xulong DevFlags; /* mountlist */ Xlong DosType; XPACKET *DosPacket; /* For the SystemRequest pr_WindowPtr */ X Xvoid ChangeIntHand(), DiskChange(); Xvoid NewVolNodeName(), NewVolNodeDate(); X Xstruct Interrupt ChangeInt = { X { 0 }, /* is_Node */ X 0, /* is_Data */ X ChangeIntHand, /* is_Code */ X}; X X/* X * Don't call the entry point main(). This way, if you make a mistake X * with the compile options you'll get a link error. X */ X Xvoid Xmessydoshandler() X{ X register PACKET *packet; X MSG *msg; X byte notdone; X long OpenCount; /* How many open files/locks there are */ X X /* X * Initialize all global variables. SysBase MUST be initialized X * before we can make Exec calls. AbsExecBase is a library symbol X * referencing absolute memory location 4. X */ X X SysBase = AbsExecBase; X DOSBase = OpenLibrary("dos.library", 0L); X X#ifdef HDEBUG X /* X * Initialize debugging code as soon as possible. Only SysBase and X * DOSBase are required. X */ X X dbinit(); X#endif /* HDEBUG */ X X DosPort = &((struct Process *)FindTask(NULL))->pr_MsgPort; X X WaitPort(DosPort); /* Get Startup Packet */ X msg = GetMsg(DosPort); X packet = (PACKET *) msg->mn_Node.ln_Name; X X X DevNode = BTOC(PArg3); X { X struct FileSysStartupMsg *fssm; X ulong *environ; X ulong Reserved; X X DevName = "messydisk.device"; X UnitNr = 0; X DevFlags = 0; X X MaxCache = 5; X BufMemType = MEMF_PUBLIC; X Disk.nsides = MS_NSIDES; X Disk.spt = MS_SPT; X Disk.bps = MS_BPS; X Disk.lowcyl = 0; X Reserved = 0; X X if (fssm = (struct FileSysStartupMsg *)BTOC(DevNode->dn_Startup)) { X /* Same as BTOC(packet->dp_Arg2) */ X UnitNr = fssm->fssm_Unit; X if (fssm->fssm_Device) X DevName = (char *)BTOC(fssm->fssm_Device)+1; X DevFlags = fssm->fssm_Flags; X X if (environ = BTOC(fssm->fssm_Environ)) { X debug(("environ size %ld\n", environ[0])); X#define get(xx,yy) if (environ[DE_TABLESIZE] >= yy) xx = environ[yy]; X X get(MaxCache, DE_NUMBUFFERS); X get(BufMemType, DE_MEMBUFTYPE); X get(Disk.nsides, DE_NUMHEADS); X get(Disk.spt, DE_BLKSPERTRACK); X get(Disk.bps, DE_SIZEBLOCK); X Disk.bps *= 4; X debug(("Disk.bps %d\n", Disk.bps)); X get(Disk.lowcyl, DE_LOWCYL); X get(Reserved, DE_RESERVEDBLKS); X get(DosType, DE_DOSTYPE); X#undef get X } X } X Disk.lowcyl *= (long)MS_BPS * Disk.spt * Disk.nsides; X Disk.lowcyl += (long)MS_BPS * Reserved; X } X X if (DOSBase && HanOpenUp()) { X /* X * Loading DevNode->dn_Task causes DOS *NOT* to startup a new X * instance of the device driver for every reference. E.G. if X * you were writing a CON: device you would want this field to be X * NULL. X */ X X DevNode->dn_Task = DosPort; X X PRes1 = DOSTRUE; X PRes2 = 0; X } else { /* couldn't open dos.library */ X PRes1 = DOSFALSE; X PRes2 = ERROR_DEVICE_NOT_MOUNTED; /* no better message available */ X returnpacket(packet); X goto exit; /* exit process */ X } X returnpacket(packet); X X /* Initialize some more global variables */ X X PortMask = 1L << DosPort->mp_SigBit; X VolNode = NULL; X OpenCount = 0; X Inhibited = 0; X X /* Get the first real packet */ X WaitPort(DosPort); X msg = GetMsg(DosPort); X notdone = 1; X WaitMask = PortMask | (1L << DiskReplyPort->mp_SigBit); X TDAddChangeInt(&ChangeInt); X DiskInserted(WhichDiskInserted()); X X goto entry; X X /* X * Here begins the endless loop, waiting for requests over our message X * port and executing them. Since requests are sent over the message X * port in our device and volume nodes, we must not use our Process X * message port for this: this precludes being able to call DOS X * functions ourselves. X */ X Xtop: X for (notdone = 1; notdone;) { X Wait(WaitMask); X if (DiskChanged) X DiskChange(); X while (msg = GetMsg(DosPort)) { X byte buf[256]; /* Max length of BCPL strings is X * 255 + 1 for \0. */ X X entry: X if (DiskChanged) X DiskChange(); X packet = (PACKET *) msg->mn_Node.ln_Name; X PRes1 = DOSFALSE; X PRes2 = 0; X error = 0; X debug(("Packet: %3ld %08lx %08lx %08lx %10s\n", X PType, PArg1, PArg2, PArg3, typetostr(PType))); X X DosPacket = packet; /* For the System Requesters */ X switch (PType) { X case ACTION_DIE: /* attempt to die? */ X notdone = 0; /* try to die */ X break; X case ACTION_CURRENT_VOLUME: /* - VolNode,UnitNr */ X PRes1 = (long) CTOB(VolNode); X PRes2 = UnitNr; X break; X case ACTION_LOCATE_OBJECT: /* Lock,Name,Mode Lock */ X { X register struct FileLock *newlock; X struct FileLock *lock; X struct MSFileLock *msfl; X long lockmode; X X lock = BTOC(PArg1); X if (CheckRead(lock)) X break; X btos(PArg2, buf); X if ((lockmode = PArg3) != EXCLUSIVE_LOCK) X lockmode = SHARED_LOCK; X msfl = MSLock(lock ? lock->fl_Key : NULL, X buf, X lockmode); X if (msfl) { X if (newlock = NewFileLock(msfl, lock)) { X newlock->fl_Access = lockmode; X PRes1 = (long) CTOB(newlock); X OpenCount++; X } else X MSUnLock(msfl); X } X } X break; X case ACTION_RENAME_DISK: /* BSTR:NewName Bool */ X if (CheckWrite(NULL)) X break; X btos(PArg1, buf); X buf[31] = '\0'; X if (PRes1 = MSRelabel(buf)) X NewVolNodeName(); X break; X case ACTION_FREE_LOCK: /* Lock Bool */ X { X struct FileLock *lock; X struct MSFileLock *msfl; X X PRes1 = DOSTRUE; X lock = BTOC(PArg1); X if (lock == NULL) X break; X X msfl = (struct MSFileLock *)lock->fl_Key; X FreeFileLock(lock); /* may remove last lock on volume */ X MSUnLock(msfl); /* may call MayFreeVolNode */ X OpenCount--; X } X break; X case ACTION_DELETE_OBJECT: /* Lock,Name Bool */ X { X struct FileLock *lock; X X lock = BTOC(PArg1); X if (CheckWrite(lock)) X break; X btos(PArg2, buf); X PRes1 = MSDeleteFile(lock ? lock->fl_Key : NULL, X buf); X } X break; X case ACTION_RENAME_OBJECT: /* SLock,SName,DLock,DName Bool */ X { X struct FileLock *slock, *dlock; X char buf2[256]; X X slock = BTOC(PArg1); X dlock = BTOC(PArg3); X if (CheckWrite(slock) || CheckWrite(dlock)) X break; X btos(PArg2, buf); X btos(PArg4, buf2); X PRes1 = MSRename(slock ? slock->fl_Key : NULL, X buf, X dlock ? dlock->fl_Key : NULL, X buf2); X } X break; X case ACTION_MORECACHE: /* #BufsToAdd Bool */ X if ((MaxCache += (short) PArg1) <= 0) { X MaxCache = 1; X } else X PRes1 = DOSTRUE; X debug(("Now %d cache sectors\n", MaxCache)); X break; X case ACTION_COPY_DIR: /* Lock Lock */ X { X register struct FileLock *newlock; X struct FileLock *lock; X struct MSFileLock *msfl; X X lock = BTOC(PArg1); X X msfl = MSDupLock(lock ? lock->fl_Key : NULL); X X if (msfl) { X if (newlock = NewFileLock(msfl, lock)) { X newlock->fl_Access = X lock ? lock->fl_Access : SHARED_LOCK; X PRes1 = (long) CTOB(newlock); X OpenCount++; X } else X MSUnLock(msfl); X } X } X break; X case ACTION_SET_PROTECT: /* -,Lock,Name,Mask Bool */ X { X struct FileLock *lock; X X lock = BTOC(PArg2); X if (CheckWrite(lock)) X break; X btos(PArg3, buf); X PRes1 = MSSetProtect(lock ? lock->fl_Key : NULL, buf, PArg4); X } X break; X case ACTION_CREATE_DIR: /* Lock,Name Lock */ X { X register struct FileLock *newlock; X struct FileLock *lock; X struct MSFileLock *msfl; X X lock = BTOC(PArg1); X if (CheckWrite(lock)) X break; X btos(PArg2, buf); X X msfl = MSCreateDir(lock ? lock->fl_Key : NULL, buf); X X if (msfl) { X if (newlock = NewFileLock(msfl, lock)) { X newlock->fl_Access = SHARED_LOCK; X PRes1 = (long) CTOB(newlock); X OpenCount++; X } else X MSUnLock(msfl); X } X } X break; X case ACTION_EXAMINE_OBJECT: /* Lock,Fib Bool */ X { X struct FileLock *lock; X X lock = BTOC(PArg1); X if (CheckRead(lock)) X break; X PRes1 = MSExamine(lock ? lock->fl_Key : NULL, BTOC(PArg2)); X } X break; X case ACTION_EXAMINE_NEXT: /* Lock,Fib Bool */ X { X struct FileLock *lock; X X lock = BTOC(PArg1); X if (CheckRead(lock)) X break; X PRes1 = MSExNext(lock ? lock->fl_Key : NULL, BTOC(PArg2)); X } X break; X case ACTION_DISK_INFO: /* InfoData Bool:TRUE */ X PRes1 = MSDiskInfo(BTOC(PArg1)); X break; X case ACTION_INFO: /* Lock,InfoData Bool:TRUE */ X if (CheckRead(BTOC(PArg1))) X break; X PRes1 = MSDiskInfo(BTOC(PArg2)); X break; X case ACTION_FLUSH: /* writeout bufs, disk motor off */ X MSUpdate(1); X break; X/* case ACTION_SET_COMMENT: /* -,Lock,Name,Comment Bool */ X case ACTION_PARENT: /* Lock ParentLock */ X { X register struct FileLock *newlock; X struct FileLock *lock; X struct MSFileLock *msfl; X long mode; X X lock = BTOC(PArg1); X X msfl = MSParentDir(lock ? lock->fl_Key : NULL); X X if (msfl) { X if (newlock = NewFileLock(msfl, lock)) { X newlock->fl_Access = SHARED_LOCK; X PRes1 = (long) CTOB(newlock); X OpenCount++; X } else X MSUnLock(msfl); X } X } X break; X case ACTION_INHIBIT: /* Bool Bool */ X if (Inhibited = PArg1) { X DiskRemoved(); X } else { /* Fall through to ACTION_DISK_CHANGE: */ X case ACTION_DISK_CHANGE: /* ? ? */ X DiskChange(); X } X PRes1 = DOSTRUE; X break; X case ACTION_SET_DATE: /* -,Lock,Name,CPTRDateStamp Bool */ X { X struct FileLock *lock; X X lock = BTOC(PArg2); X if (CheckWrite(lock)) X break; X btos(PArg3, buf); X PRes1 = MSSetDate(lock ? lock->fl_Key : NULL, X buf, X PArg4); X } X break; X case ACTION_READ: /* FHArg1,CPTRBuffer,Length ActLength */ X if (CheckRead(NULL)) { X PRes1 = -1; X } else X PRes1 = MSRead(PArg1, PArg2, PArg3); X break; X case ACTION_WRITE: /* FHArg1,CPTRBuffer,Length ActLength */ X if (CheckWrite(NULL)) { X PRes1 = -1; X } else X PRes1 = MSWrite(PArg1, PArg2, PArg3); X break; X case ACTION_OPENNEW: /* FileHandle,Lock,Name Bool */ X { X struct MSFileHandle *msfh; X struct FileHandle *fh; X struct FileLock *lock; X X if (CheckWrite(BTOC(PArg2))) X break; X case ACTION_OPENRW: /* FileHandle,Lock,Name Bool */ X case ACTION_OPENOLD: /* FileHandle,Lock,Name Bool */ X X fh = BTOC(PArg1); X lock = BTOC(PArg2); X if (CheckRead(lock)) X break; X btos(PArg3, buf); X debug(("'%s' ", buf)); X msfh = MSOpen(lock ? lock->fl_Key : NULL, X buf, X PType); X if (msfh) { X fh->fh_Arg1 = (long) msfh; X PRes1 = DOSTRUE; X OpenCount++; X } X } X break; X case ACTION_CLOSE: /* FHArg1 Bool:TRUE */ X MSClose(PArg1); X PRes1 = DOSTRUE; X OpenCount--; X break; X case ACTION_SEEK: /* FHArg1,Position,Mode OldPosition */ X if (CheckRead(NULL)) { X PRes1 = -1; X } else X PRes1 = MSSeek(PArg1, PArg2, PArg3); X break; X /* X * A few other packet types which we do not support X */ X/* case ACTION_WAIT_CHAR: /* Timeout, ticks Bool */ X/* case ACTION_RAWMODE: /* Bool(-1:RAW 0:CON) OldState */ X default: X error = ERROR_ACTION_NOT_KNOWN; X break; X } /* end switch */ X if (packet) { X if (error) { X debug(("ERR=%d\n", error)); X PRes2 = error; X } X#ifdef HDEBUG X else { X debug(("RES=%06lx\n", PRes1)); X } X#endif X returnpacket(packet); X DosPacket = NULL; X } X#ifdef HDEBUG X else { X debug(("NOREP\n")); X } X#endif X } /* end while (GetMsg()) */ X X /* X * Now check for an other cause of events: timer IO. X * Unfortunately we cannot be sure that we always get a signal X * when the timeout has elapsed, since the same message port is X * used for other IO. X */ X if (CheckIO(TimeIOReq)) { /* Timer finished? */ X debug(("TimeIOReq is finished\n")); X if (DelayState != DELAY_OFF) { X MSUpdate(0); /* Also may switch off motor */ X } X } X } /* end for (;notdone) */ X X#ifdef HDEBUG X debug(("Can we remove ourselves? ")); X Delay(50L); /* I wanna even see the debug message! */ X#endif /* HDEBUG */ X Forbid(); X if (OpenCount || packetsqueued()) { X Permit(); X debug((" .. not yet!\n")); X goto top; /* sorry... can't exit */ X } X debug((" .. yes!\n")); X X /* X * Causes a new process to be created on next reference. X */ X X DevNode->dn_Task = NULL; X TDRemChangeInt(); X DiskRemoved(); X HanCloseDown(); X debug(("HanCloseDown returned. dbuninit in 2 seconds:\n")); X X /* X * Remove debug window, closedown, fall of the end of the world. X */ Xexit: X#ifdef HDEBUG X Delay(100L); /* This is dangerous! */ X dbuninit(); X#endif /* HDEBUG */ X X#if 1 X UnLoadSeg(DevNode->dn_SegList); /* This is real fun. We are still */ X DevNode->dn_SegList = NULL; /* Forbid()den, fortunately */ X#endif X X CloseLibrary(DOSBase); X X /* Fall off the end of the world. Implicit Permit(). */ X} X Xvoid XChangeIntHand() X{ X/* INDENT OFF */ X#asm X move.l a6,-(sp) X#endasm X DiskChanged = 1; X Signal(DosPort->mp_SigTask, PortMask); X#asm X move.l (sp)+,a6 X#endasm X/* INDENT ON */ X} X X/* X * Make a new struct FileLock, for DOS use. It is put on a singly linked X * list, which is attached to the same VolumeNode the old lock was on. X * X * Also note that we must ALWAYS be prepared to UnLock() or DupLock() X * any FileLocks we ever made, even if the volume in question has been X * removed and/or inserted into another drive with another FileSystem X * handler! X * X * DOS makes certain assumptions about LOCKS. A lock must minimally be a X * FileLock structure, with additional private information after the X * FileLock structure. The longword before the beginning of the structure X * must contain the length of structure + 4. X * X * NOTE!!!!! The workbench does not follow the rules and assumes it can copy X * lock structures. This means that if you want to be workbench X * compatible, your lock structures must be EXACTLY sizeof(struct X * FileLock). Also, it sometimes uses uninitialized values for the lock mode... X */ X Xstruct FileLock * XNewFileLock(msfl, fl) Xstruct MSFileLock *msfl; Xstruct FileLock *fl; X{ X struct FileLock *newlock; X DEVLIST *volnode; X X if (fl) { X volnode = BTOC(fl->fl_Volume); X } else { X volnode = VolNode; X } X X if (newlock = dosalloc((ulong)sizeof (*newlock))) { X newlock->fl_Key = (ulong) msfl; X newlock->fl_Task = DosPort; X newlock->fl_Volume = (BPTR) CTOB(volnode); X Forbid(); X newlock->fl_Link = volnode->dl_LockList; X volnode->dl_LockList = (BPTR) CTOB(newlock); X Permit(); X } else X error = ERROR_NO_FREE_STORE; X X return newlock; X} X X/* X * This should be called before MSUnLock(), so that it may call X * MayFreeVolNode() which then calls FreeVolNode(). A bit tricky, X * I'm sorry for that. X */ X Xlong XFreeFileLock(lock) Xstruct FileLock *lock; X{ X register struct FileLock *fl; X register struct FileLock **flp; X DEVLIST *volnode; X X volnode = (DEVLIST *)BTOC(lock->fl_Volume); X flp = (struct FileLock **) &volnode->dl_LockList; X for (fl = BTOC(*flp); fl && fl != lock; fl = BTOC(fl->fl_Link)) X flp = (struct FileLock **)&fl->fl_Link; X X if (fl == lock) { X *(BPTR *)flp = fl->fl_Link; X dosfree(fl); X return DOSTRUE; X } else { X debug(("Huh?? Could not find filelock!\n")); X return DOSFALSE; X } X} X X/* X * Create Volume node and add to the device list. This will X * cause the WORKBENCH to recognize us as a disk. If we X * don't create a Volume node, Wb will not recognize us. X * However, we are a MESSYDOS: disk, Volume node or not. X */ X XDEVLIST * XNewVolNode(name, date) Xstruct DateStamp *date; Xchar *name; X{ X DOSINFO *di; X register DEVLIST *volnode; X char *volname; /* This is my volume name */ X X di = BTOC(((ROOTNODE *) DOSBase->dl_Root)->rn_Info); X X if (volnode = dosalloc((ulong)sizeof (DEVLIST))) { X if (volname = dosalloc(32L)) { X volname[0] = strlen(name); X strcpy(volname + 1, name); /* Make sure \0 terminated */ X X volnode->dl_Type = DLT_VOLUME; X volnode->dl_Task = DosPort; X volnode->dl_DiskType = IDDiskType; X volnode->dl_Name = CTOB(volname); X volnode->dl_VolumeDate = *date; X volnode->dl_MSFileLockList = NULL; X X Forbid(); X volnode->dl_Next = di->di_DevInfo; X di->di_DevInfo = (long) CTOB(volnode); X Permit(); X } else { X dosfree(volnode); X volnode = NULL; X } X } else { X error = ERROR_NO_FREE_STORE; X } X X return volnode; X} X X/* X * Get the current VolNode a new name from the volume label. X */ X Xvoid XNewVolNodeName() X{ X if (VolNode) { X register char *volname = BTOC(VolNode->dl_Name); X X strncpy(volname + 1, Disk.vollabel.de_Msd.msd_Name, 8+3); X volname[1+8+3] = '\0'; /* Make sure \0 terminated */ X ZapSpaces(volname + 1, volname + 1 + 8+3); X volname[0] = strlen(volname+1); X } X} X X/* X * Get the current VolNode a new date, from the last root directory. X */ X Xvoid XNewVolNodeDate() X{ X if (VolNode) { X ToDateStamp(&VolNode->dl_VolumeDate, X Disk.vollabel.de_Msd.msd_Date, X Disk.vollabel.de_Msd.msd_Time); X } X} X X/* X * Remove Volume entry. Since DOS uses singly linked lists, we must X * (ugg) search it manually to find the link before our Volume entry. X */ X Xvoid XFreeVolNode(volnode) XDEVLIST *volnode; X{ X DOSINFO *di = BTOC(((ROOTNODE *) DOSBase->dl_Root)->rn_Info); X register DEVLIST *dl; X register void *dlp; X X debug(("FreeVolNode %08lx\n", volnode)); X X if (volnode == NULL) X return; X X dlp = &di->di_DevInfo; X Forbid(); X for (dl = BTOC(di->di_DevInfo); dl && dl != volnode; dl = BTOC(dl->dl_Next)) X dlp = &dl->dl_Next; X if (dl == volnode) { X *(BPTR *) dlp = dl->dl_Next; X dosfree(BTOC(dl->dl_Name)); X dosfree(dl); X } X#ifdef HDEBUG X else { X debug(("****PANIC: Unable to find volume node\n")); X } X#endif /* HDEBUG */ X Permit(); X X if (volnode == VolNode) X VolNode = NULL; X} X X/* X * This is also called from the real handler when the last lock on a X * volume is UnLock()ed, or the last file has been Close()d. X */ X Xint XMayFreeVolNode(volnode) XDEVLIST *volnode; X{ X if (volnode->dl_LockList == NULL) { X FreeVolNode(volnode); X return 1; X } X X return 0; X} X X/* X * Our disk has been removed. Save the FileLocks in the dl_LockList, X * and let the handler save its MSFileLocks in the dl_MSFileLockList field. X * If it has nothing to save, forget about the volume, and return X * DOSTRUE. X * There is one subtlety that MSDiskRemoved must know about: X * If it MSUnLock()s the last lock on the volume, the VolNode is X * deleted via FreeLockList().. MayFreeVolNode().. FreeVolNode(). X * But then there is no place anymore to put NULL in, so that needs X * to be done first. X */ X Xint XDiskRemoved() X{ X debug(("DiskRemoved %08lx\n", VolNode)); X X if (VolNode == NULL) { X IDDiskType = ID_NO_DISK_PRESENT;/* really business of MSDiskRemoved */ X return DOSTRUE; X } X X VolNode->dl_Task = NULL; X MSDiskRemoved(&VolNode->dl_MSFileLockList); X if (VolNode == NULL) { /* Could happen via MSDiskRemoved() */ X return DOSTRUE; X } X NewVolNodeDate(); /* Fetch new date of root directory */ X VolNode = NULL; X return DOSFALSE; X} X X/* X * Reconstruct everything from a Volume node X */ X Xvoid XDiskInserted(volnode) Xregister DEVLIST *volnode; X{ X debug(("DiskInserted %08lx\n", volnode)); X X VolNode = volnode; X X if (volnode) { X volnode->dl_Task = DosPort; X MSDiskInserted(&volnode->dl_MSFileLockList, volnode); X volnode->dl_MSFileLockList = NULL; X } X} X XDEVLIST * XWhichDiskInserted() X{ X char name[34]; X struct DateStamp date; X register DEVLIST *dl = NULL; X X if (!Inhibited && IdentifyDisk(name, &date) == 0) { X DOSINFO *di = BTOC(((ROOTNODE *) DOSBase->dl_Root)->rn_Info); X byte *nodename; X int namelen = strlen(name); X X for (dl = BTOC(di->di_DevInfo); dl; dl = BTOC(dl->dl_Next)) { X nodename = BTOC(dl->dl_Name); X if (nodename[0] != namelen || strncmp(nodename+1,name,namelen)) X continue; X if (dl->dl_VolumeDate == date) /* Non-standard! Structure compare! */ X break; X } X X name[31] = '\0'; X if (dl == NULL) X dl = NewVolNode(name, &date); X } X X return dl; X} X Xvoid XDiskChange() X{ X debug(("DiskChange\n")); X DiskChanged = 0; X DiskRemoved(); X DiskInserted(WhichDiskInserted()); X} X Xint XCheckRead(lock) Xstruct FileLock *lock; X{ X if (lock && BTOC(lock->fl_Volume) != VolNode) X error = ERROR_DEVICE_NOT_MOUNTED; X else if (IDDiskType == ID_NO_DISK_PRESENT) X error = ERROR_NO_DISK; X else if (IDDiskType != ID_DOS_DISK) X error = ERROR_NOT_A_DOS_DISK; X X return error; X} X Xint XCheckWrite(lock) Xstruct FileLock *lock; X{ X if (lock && BTOC(lock->fl_Volume) != VolNode) X error = ERROR_DEVICE_NOT_MOUNTED; X else if (IDDiskType == ID_NO_DISK_PRESENT) X error = ERROR_NO_DISK; X else if (IDDiskType != ID_DOS_DISK) X error = ERROR_NOT_A_DOS_DISK; X else if (IDDiskState == ID_VALIDATING) X error = ERROR_DISK_NOT_VALIDATED; X else if (IDDiskState != ID_VALIDATED) X error = ERROR_DISK_WRITE_PROTECTED; X X return error; X} X X#ifdef HDEBUG X /* DEBUGGING */ XPORT *Dbport; /* owned by the debug process */ XPORT *Dback; /* owned by the DOS device driver */ Xshort DBEnable; X X/* X * DEBUGGING CODE. You cannot make DOS library calls that access other X * devices from within a DOS device driver because they use the same X * message port as the driver. If you need to make such calls you must X * create a port and construct the DOS messages yourself. I do not X * do this. To get debugging info out another PROCESS is created to which X * debugging messages can be sent. X */ X Xextern void debugproc(); X Xdbinit() X{ X TASK *task = FindTask(NULL); X X Dback = CreatePort("MSH:Dback", -1L); X CreateProc("MSH_DB", (long)task->tc_Node.ln_Pri+1, CTOB(debugproc), 4096L); X WaitPort(Dback); /* handshake startup */ X GetMsg(Dback); /* remove dummy msg */ X DBEnable = 1; X dbprintf("Debugger running V1.10\n"); X} X Xdbuninit() X{ X MSG killmsg; X X if (Dbport) { X killmsg.mn_Length = 0; /* 0 means die */ X PutMsg(Dbport, &killmsg); X WaitPort(Dback); /* He's dead jim! */ X GetMsg(Dback); X DeletePort(Dback); X X /* X * Since the debug process is running at a greater priority, I X * am pretty sure that it is guarenteed to be completely removed X * before this task gets control again. Still, it doesn't hurt... X */ X X Delay(50L); /* ensure he's dead */ X } 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 MSG msg; X char buf[256]; X } msgbuf; X register MSG *msg = &msgbuf.msg; X register long len; X X if (Dbport && DBEnable) { 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 PutMsg(Dbport, msg); X WaitPort(Dback); X GetMsg(Dback); X } X} X X/* X * BTW, the DOS library used by debugmain() was actually opened by X * the device driver. X */ X Xdebugmain() X{ X register MSG *msg; X register long len; X register void *fh; X void *fh2; X MSG DummyMsg; X X Dbport = CreatePort("MSH:Dbport", -1L); X fh = Open("CON:0/10/640/190/FileSystem 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 X * within 8 bytes of address (remember the first longword is a segment X * pointer and skipped). Sigh.... (see CreateProc() above). X */ X 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 /* HDEBUG */ END_OF_FILE if test 26338 -ne `wc -c <'src/pack.c'`; then echo shar: \"'src/pack.c'\" unpacked with wrong size! fi # end of 'src/pack.c' fi echo shar: End of archive 4 \(of 6\). cp /dev/null ark4isdone 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.