/* * DOSDEVICE.C V1.10 2 November 1987 * * EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C PUBLIC DOMAIN. * * By Matthew Dillon. * * Debugging routines are disabled by simply attempting to open the * file "debugoff", turned on again with "debugon". No prefix may be * attached to these names (you must be CD'd to TEST:). * * See Documentation for a detailed discussion. * * BUGS: * Currently the only known bug is with the implementation of the * RAM disk itself. Specifically, if filehandle A is at the end of * the file, and somebody appends to the file with another filehandle, * B, filehandle A will get confused as to it's current position in * the file. * * I am probably not updating all the right timestamps. This is * easy to fix... All you have to do is fool with the floppy and * see which datestamps get updated for certain operations. */ #include "dos.h" /* * 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. */ PORT *DosPort; /* Our DOS port... this is slick... */ PROC *DosProc; /* Our Process */ DEVNODE *DosNode; /* Our DOS node.. created by DOS for us */ DEVLIST *DevList; /* Device List structure for our volume node */ void *SysBase; /* EXEC library base */ DOSLIB *DOSBase; /* DOS library base for debug process */ RAMFILE RFRoot; /* Directory/File structure (root node) */ LIST FHBase; /* Open Files */ LIST LCBase; /* Open Locks */ long TotalBytes; /* total bytes of data in filesystem */ /* DEBUGGING */ PORT *Dbport; /* owned by the debug process */ PORT *Dback; /* owned by the DOS device driver */ MSG DummyMsg; /* Dummy message that debug proc can use */ RAMFILE xpath; /* This is used in case of off device path */ char *buf1; /* This holds the translated path names */ char *volname; /* This is my volume name */ RAMFILE * checkoutpath(); RAMFILE * vsearchpath(); void *DeviceProc(); /* * Don't call the entry point main(). This way, if you make a mistake * with the compile options you'll get a link error. */ void noname() { register PACKET *packet; register short error; register ubyte *ptr; MSG *msg; ubyte notdone; ubyte buf[256]; void *tmp; /* * Initialize all global variables. SysBase MUST be initialized before * we can make Exec calls. AbsExecBase is a library symbol * referencing absolute memory location 4. The DOS library is openned * for the debug process only. */ Dbport = Dback = NULL; TotalBytes = 0; SysBase = AbsExecBase; DOSBase = OpenLibrary("dos.library",0); DosProc = FindTask(NULL); DosPort = (PORT *)AllocMem(sizeof(PORT), MEMF_CLEAR | MEMF_PUBLIC); DosPort->mp_Node.ln_Type = NT_MSGPORT; DosPort->mp_Node.ln_Name = "Dos Port"; DosPort->mp_Flags = PA_SIGNAL; DosPort->mp_SigBit = AllocSignal(-1); DosPort->mp_SigTask = FindTask(NULL); NewList(&DosPort->mp_MsgList); buf1 = AllocMem(512, MEMF_PUBLIC); { WaitPort(&DosProc->pr_MsgPort); /* Get Startup Packet */ msg = GetMsg(&DosProc->pr_MsgPort); packet = (PACKET *)msg->mn_Node.ln_Name; /* * Loading DosNode->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. */ if (DOSBase) { DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info); register DEVLIST *dl = dosalloc(sizeof(DEVLIST)); DosNode = BTOC(PArg3); /* * 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 RAM: disk, Volume node or not. */ volname = AllocMem(32,MEMF_PUBLIC); volname[0] = 11; strcpy(volname+1,"Path Server"); DevList = dl; dl->dl_Type = DLT_VOLUME; dl->dl_Task = DosPort; dl->dl_DiskType = ID_DOS_DISK; dl->dl_Name = CTOB(volname); /* DosNode->dn_Name*/; dl->dl_Next = di->di_DevInfo; di->di_DevInfo = (long)CTOB(dl); /* * Set dn_Task field which tells DOS not to startup a new * process on every reference. */ DosNode->dn_Task = DosPort; PRes1 = DOS_TRUE; PRes2 = 0; } else { /* couldn't open dos.library */ PRes1 = DOS_FALSE; returnpacket(packet); return; /* exit process */ } returnpacket(packet); } /* * Initialize debugging code */ /*DB*/ dbinit(); /* Initialize RAM disk */ { ubyte *ptr = BTOC(DosNode->dn_Name); short len = *ptr; NewList(&FHBase); /* more globals */ NewList(&LCBase); bzero(&RFRoot,sizeof(RFRoot)); RFRoot.type = FILE_DIR; /* root directory */ DateStamp(&RFRoot.date); /* datestamp */ NewList(&RFRoot.list); /* sub dirs */ RFRoot.name = AllocMem(len+1, MEMF_PUBLIC); /* Root NAME */ bmov(ptr+1,RFRoot.name,len); RFRoot.name[len] = 0; /*DB*/ dbprintf("ROOT NAME: %ld '%s'\n", len, RFRoot.name); } /* * Here begins the endless loop, waiting for requests over our * message port and executing them. Since requests are sent over * our message port, this precludes being able to call DOS functions * ourselves (that is why the debugging routines are a separate process) */ WaitPort(&DosProc->pr_MsgPort); /* Get Startup Packet */ msg = GetMsg(&DosProc->pr_MsgPort); notdone = 1; goto entry; top: for (notdone = 1; notdone;) { WaitPort(DosPort); while (msg = GetMsg(DosPort)) { entry: packet = (PACKET *)msg->mn_Node.ln_Name; PRes1 = DOS_TRUE; PRes2 = 0; error = 0; /*DB*/ dbprintf("Packet: %3ld %08lx %08lx %08lx %10s ", /*DB*/ PType, PArg1, PArg2, PArg3, typetostr(PType) ); switch(PType) { case ACTION_DIE: /* attempt to die? */ notdone = 0; /* try to die */ break; case ACTION_OPENRW: /* FileHandle,Lock,Name Bool */ case ACTION_OPENOLD: /* FileHandle,Lock,Name Bool */ case ACTION_OPENNEW: /* FileHandle,Lock,Name Bool */ { register RAMFILE *ramfile; RAMFILE *parentdir = getlockfile(PArg2); char *ptr; btos(PArg3,buf); /*DB*/ dbprintf("'%s' ", buf); if (ramfile = searchpath(&parentdir,buf,&ptr)) { if (ramfile == &xpath) { FH *p; if (p = Open(buf1,PType)) { bmov(BTOC(p),BTOC(PArg1),sizeof(FH)); FreeMem(BTOC(p),sizeof(FH)); } else error = IoErr(); goto openbreak; } if (ramfile->type == FILE_DIR) { error = ERROR_OBJECT_WRONG_TYPE; goto openbreak; } if (ramfile->locks < 0) { error = ERROR_OBJECT_IN_USE; goto openbreak; } if (PType == ACTION_OPENOLD) { ++ramfile->locks; } else { if (ramfile->locks > 0) { error = ERROR_OBJECT_IN_USE; } else { if (PType == ACTION_OPENNEW) { freedata(ramfile); ramfile->protection = 0; } --ramfile->locks; } } } else { if (!parentdir) { error = ERROR_INVALID_COMPONENT_NAME; goto openbreak; } if (PType == ACTION_OPENNEW) { ramfile = createramfile(parentdir,FILE_FILE,ptr); --ramfile->locks; } else { error = ERROR_OBJECT_NOT_FOUND; } } if (!error) { register MYFH *mfh = AllocMem(sizeof(MYFH), MEMF_PUBLIC|MEMF_CLEAR); ((FH *)BTOC(PArg1))->fh_Arg1 = (long)mfh; mfh->file = ramfile; mfh->fentry = GetHead(&ramfile->list); AddHead(&FHBase,mfh); } } openbreak: if (!GetHead(&FHBase) && !GetHead(&LCBase)) notdone = 0; break; case ACTION_READ: /* FHArg1,CPTRBuffer,Length ActLength */ { register MYFH *mfh = (MYFH *)PArg1; register FENTRY *fen = mfh->fentry; register ubyte *ptr = (ubyte *)PArg2; register long left = PArg3; register long scr; while (left && fen) { scr = fen->bytes - mfh->offset; if (left < scr) { bmov(fen->buf + mfh->offset, ptr, left); mfh->offset += left; left = 0; } else { bmov(fen->buf + mfh->offset, ptr, scr); left -= scr; ptr += scr; mfh->base += fen->bytes; mfh->offset = 0; fen = NextNode(fen); } } mfh->fentry = fen; PRes1 = PArg3 - left; } break; case ACTION_WRITE: /* FHArg1,CPTRBuffer,Length ActLength */ { register MYFH *mfh = (MYFH *)PArg1; register FENTRY *fen = (FENTRY *)mfh->fentry; ubyte *ptr = (ubyte *)PArg2; long left = PArg3; long scr; /* * Doesn't work right if multiple readers/appenders. */ while (left) { if (fen) { /*DB*/ dbprintf("FEN: %ld left: %ld\n",fen->bytes,left); scr = fen->bytes - mfh->offset; if (left < scr) { if (fen->bytes < mfh->offset + left) { /*DB*/ dbprintf("PANIC! AWR0\n"); } else bmov(ptr, fen->buf + mfh->offset, left); mfh->offset += left; left = 0; } else { if (fen->bytes < mfh->offset + scr) { /*DB*/ dbprintf("PANIC! AWR1\n"); } else bmov(ptr, fen->buf + mfh->offset, scr); ptr += scr; left -= scr; mfh->base += fen->bytes; mfh->offset = 0; fen = NextNode(fen); } } else { fen = AllocMem(sizeof(FENTRY), MEMF_PUBLIC); if (fen->buf = AllocMem(left, MEMF_PUBLIC)) { fen->bytes = left; mfh->file->bytes += left; mfh->base += left; mfh->offset = 0; TotalBytes += left; AddTail(&mfh->file->list, fen); /*DB*/ dbprintf("NEWFEN: (%ld)\n", fen->bytes); bmov(ptr, fen->buf, left); left = 0; } else { FreeMem(fen, sizeof(FENTRY)); /*DB*/ dbprintf("NEWFEN: ****** Unable to allocate buffer %ld\n", left); mfh->offset = 0; break; } fen = NULL; /* cause append */ } } PRes1 = PArg3 - left; mfh->fentry = fen; } break; case ACTION_CLOSE: /* FHArg1 Bool:TRUE */ { register MYFH *mfh = (MYFH *)PArg1; register RAMFILE *file = mfh->file; Remove(mfh); FreeMem(mfh,sizeof(*mfh)); if (--file->locks < 0) file->locks = 0; } if (!GetHead(&FHBase) && !GetHead(&LCBase)) notdone = 0; break; case ACTION_SEEK: /* FHArg1,Position,Mode OldPosition*/ { register MYFH *mfh = (MYFH *)PArg1; register FENTRY *fen; register long absseek; PRes1 = mfh->base + mfh->offset; absseek = PArg2; if (PArg3 == 0) absseek += mfh->base + mfh->offset; if (PArg3 == 1) absseek = mfh->file->bytes + absseek; if (absseek < 0 || absseek > mfh->file->bytes) { error = ERROR_SEEK_ERROR; break; } mfh->base = mfh->offset = 0; /* * Stupid way to do it but.... */ for (fen = GetHead(&mfh->file->list); fen; fen = NextNode(fen)) { if (mfh->base + fen->bytes > absseek) { mfh->offset = absseek - mfh->base; break; } mfh->base += fen->bytes; } mfh->fentry = fen; } break; /* * This implementation sucks. The right way to do it is with * a hash table. The directory must be searched for the file * name, then the next entry retrieved. If the next entry is * NULL there are no more entries. If the filename could not * be found we return the first entry, if any. * * You can't simply keep a pointer around to the next node * because it can be moved or removed at any time. */ case ACTION_EXAMINE_NEXT: /* Lock,Fib Bool */ { register FIB *fib = BTOC(PArg2); register RAMFILE *dir = getlockfile(PArg1); register RAMFILE *file; if (dir->type == FILE_FILE) { error = ERROR_OBJECT_WRONG_TYPE; break; } file = GetHead(&dir->list); if (fib->fib_DiskKey) { register int len = *(ubyte *)fib->fib_FileName; for (; file; file = NextNode(file)) { if (strlen(file->name) == len && nccmp(file->name, fib->fib_FileName+1, len)) break; } if (file) file = NextNode(file); else file = GetHead(&dir->list); } fib->fib_DiskKey = 1; error = -1; if (!(tmp=file)) { error = ERROR_NO_MORE_ENTRIES; break; } } /* fall through */ case ACTION_EXAMINE_OBJECT: /* Lock,Fib Bool */ { register FIB *fib; register RAMFILE *file; register RAMFILE *dummy; fib = BTOC(PArg2); if (error) { file = tmp; /* fall through from above */ } else { file = getlockfile(PArg1); fib->fib_DiskKey = 0; } error = 0; fib->fib_DirEntryType = file->type; strcpy(fib->fib_FileName+1, file->name); fib->fib_FileName[0] = strlen(file->name); fib->fib_Protection = file->protection; fib->fib_EntryType = NULL; fib->fib_Size = file->bytes; fib->fib_NumBlocks = file->bytes >> 9; fib->fib_Date = file->date; if (file->comment) { strcpy(fib->fib_Comment+1, file->comment); fib->fib_Comment[0] = strlen(file->comment); } else { fib->fib_Comment[0] = 0; } } break; case ACTION_INFO: /* Lock, InfoData Bool:TRUE */ tmp = BTOC(PArg2); error = -1; /* fall through */ case ACTION_DISK_INFO: /* InfoData Bool:TRUE */ { register INFODATA *id; /* * Note: id_NumBlocks is never 0, but only to get * around a bug I found in my shell (where I divide * by id_NumBlocks). Other programs probably break * as well. */ (error) ? (id = tmp) : (id = BTOC(PArg1)); error = 0; bzero(id, sizeof(*id)); id->id_DiskState = ID_VALIDATED; id->id_NumBlocks = (TotalBytes >> 9) + 1; id->id_NumBlocksUsed = (TotalBytes >> 9) + 1; id->id_BytesPerBlock = 512; id->id_DiskType = ID_DOS_DISK; id->id_VolumeNode = (long)CTOB(DosNode); id->id_InUse = (long)GetHead(&LCBase); } break; case ACTION_PARENT: /* Lock ParentLock */ { register RAMFILE *file = getlockfile(PArg1); /* if (file->type == FILE_FILE) { error = ERROR_OBJECT_NOT_FOUND; break; } */ if (file->locks < 0) { error = ERROR_OBJECT_IN_USE; break; } if (file->parent) PRes1 = (long)CTOB(ramlock(file->parent, ACCESS_READ)); else error = ERROR_OBJECT_NOT_FOUND; } break; case ACTION_DELETE_OBJECT: /*Lock,Name Bool */ { RAMFILE *parentdir = getlockfile(PArg1); RAMFILE *ramfile; btos(PArg2, buf); if (ramfile = searchpath(&parentdir,buf,NULL)) { if (ramfile == &xpath) { if (!DeleteFile(buf1)) error = IoErr(); break; } if (ramfile->locks || ramfile == &RFRoot) { error = ERROR_OBJECT_IN_USE; break; } if (ramfile->type == FILE_DIR) { if (GetHead(&ramfile->list)) error = ERROR_DIRECTORY_NOT_EMPTY; } else { freedata(ramfile); } if (!error) { freeramfile(ramfile); DateStamp(&parentdir->date); } } else { if (!parentdir) error = ERROR_INVALID_COMPONENT_NAME; else error = ERROR_OBJECT_NOT_FOUND; } } if (!GetHead(&FHBase) && !GetHead(&LCBase)) notdone = 0; break; case ACTION_CREATE_DIR: /* Lock,Name Lock */ { RAMFILE *parentdir = getlockfile(PArg1); RAMFILE *ramfile; char *ptr; btos(PArg2, buf); if (ramfile = vsearchpath(&parentdir,buf,&ptr)) { error = ERROR_OBJECT_EXISTS; break; } if (!parentdir) { error = ERROR_INVALID_COMPONENT_NAME; break; } ramfile = createramfile(parentdir, FILE_DIR, ptr); PRes1 = (long)CTOB(ramlock(ramfile, ACCESS_WRITE)); } break; case ACTION_LOCATE_OBJECT: /* Lock,Name,Mode Lock */ { RAMFILE *parentdir = getlockfile(PArg1); RAMFILE *ramfile; btos(PArg2, buf); /*DB*/ dbprintf("'%s' %ld ", buf, PArg3); if (ramfile = searchpath(&parentdir,buf,NULL)) { if (ramfile == &xpath) { PRes1 = Lock(buf1,PArg3); if (!PRes1) PRes2 = IoErr(); break; } if (ramfile->locks < 0 || (ramfile->locks && PArg3 == ACCESS_WRITE)) { error = ERROR_OBJECT_IN_USE; break; } PRes1 = (long)CTOB(ramlock(ramfile, PArg3)); } else { if (!parentdir) error = ERROR_INVALID_COMPONENT_NAME; else error = ERROR_OBJECT_NOT_FOUND; } } break; case ACTION_COPY_DIR: /* Lock, Lock */ { register RAMFILE *ramfile = getlockfile(PArg1); if (ramfile->locks < 0) error = ERROR_OBJECT_IN_USE; else PRes1 = (long)CTOB(ramlock(ramfile, ACCESS_READ)); } break; case ACTION_FREE_LOCK: /* Lock, Bool */ if (PArg1); ramunlock(BTOC(PArg1)); if (!GetHead(&FHBase) && !GetHead(&LCBase)) notdone = 0; break; case ACTION_SET_PROTECT:/* -,Lock,Name,Mask Bool */ { register RAMFILE *ramfile; RAMFILE *parentdir = getlockfile(PArg2); char *ptr; btos(PArg3, buf); if (ramfile = searchpath(&parentdir,buf,&ptr)) { if (ramfile == &xpath) { if (!SetProtection(buf1,PArg4)) error = IoErr(); break; } ramfile->protection = PArg4; } else { if (parentdir) error = ERROR_OBJECT_NOT_FOUND; else error = ERROR_INVALID_COMPONENT_NAME; } } break; case ACTION_SET_COMMENT:/* -,Lock,Name,Comment Bool */ { register RAMFILE *ramfile; RAMFILE *parentdir = getlockfile(PArg2); char *ptr; btos(PArg3, buf); if (ramfile = searchpath(&parentdir,buf,&ptr)) { if (ramfile == &xpath) { btos(PArg4,buf); if (!SetComment(buf1,buf)) error = IoErr(); break; } btos(PArg4, buf); if (ramfile->comment) FreeMem(ramfile->comment,strlen(ramfile->comment)+1); ramfile->comment = AllocMem(strlen(buf)+1, MEMF_PUBLIC); strcpy(ramfile->comment, buf); } else { if (parentdir) error = ERROR_OBJECT_NOT_FOUND; else error = ERROR_INVALID_COMPONENT_NAME; } } break; case ACTION_RENAME_OBJECT:/* SLock,SName,DLock,DName Bool */ { register RAMFILE *file1; RAMFILE *sourcedir = getlockfile(PArg1); RAMFILE *destdir = getlockfile(PArg3); char *ptr; btos(PArg2,buf); /*DB*/ dbprintf("\nRENAME '%s' (%ld) ", buf, strlen(buf)); if (file1 = vsearchpath(&sourcedir,buf,NULL)) { btos(PArg4,buf); /*DB*/ dbprintf("TO '%s' (%ld)", buf, strlen(buf)); if (vsearchpath(&destdir,buf,&ptr)) { error = ERROR_OBJECT_EXISTS; } else { if (destdir) { if (file1 == destdir) { /* moving inside self */ error = ERROR_OBJECT_IN_USE; break; } /*DB*/ dbprintf("REN '%s' %ld", ptr, strlen(ptr)); DateStamp(&sourcedir->date); DateStamp(&destdir->date); Remove(file1); file1->name = AllocMem(strlen(ptr)+1,MEMF_PUBLIC); file1->parent = destdir; strcpy(file1->name, ptr); AddHead(&destdir->list, file1); } else { error = ERROR_INVALID_COMPONENT_NAME; } } } else { if (sourcedir) error = ERROR_OBJECT_NOT_FOUND; else error = ERROR_INVALID_COMPONENT_NAME; } } break; /* * A few other packet types which we do not support */ case ACTION_INHIBIT: /* Bool Bool */ /* Return success for the hell of it */ break; case ACTION_RENAME_DISK:/* BSTR:NewName Bool */ case ACTION_MORECACHE: /* #BufsToAdd Bool */ case ACTION_WAIT_CHAR: /* Timeout, ticks Bool */ case ACTION_FLUSH: /* writeout bufs, disk motor off */ case ACTION_RAWMODE: /* Bool(-1:RAW 0:CON) OldState */ default: error = ERROR_ACTION_NOT_KNOWN; break; } if (packet) { if (error) { /*DB*/ dbprintf("ERR=%ld\n", error); PRes1 = DOS_FALSE; PRes2 = error; } else { /*DB*/ dbprintf("RES=%06lx\n", PRes1); } returnpacket(packet); } else { /*DB*/ dbprintf("NOREP\n"); } } } /*DB*/ dbprintf("Can we remove ourselves? "); /*DB*/ Delay(50); /* I wanna even see the debug message! */ Forbid(); if (packetsqueued() || GetHead(&FHBase) || GetHead(&LCBase) || GetHead(&RFRoot.list)) { Permit(); /*DB*/ dbprintf(" .. not yet!\n"); goto top; /* sorry... can't exit */ } /* * Causes a new process to be created on next reference */ DosNode->dn_Task = FALSE; /* * Remove Volume entry. Since DOS uses singly linked lists, we * must (ugg) search it manually to find the link before our * Volume entry. */ { DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info); register DEVLIST *dl; register void *dlp; dlp = &di->di_DevInfo; for (dl = BTOC(di->di_DevInfo); dl && dl != DevList; dl = BTOC(dl->dl_Next)) dlp = &dl->dl_Next; if (dl == DevList) { *(BPTR *)dlp = dl->dl_Next; dosfree(dl); } else { /*DB*/ dbprintf("****PANIC: Unable to find volume node\n"); } } /* * Remove debug process, closedown, fall of the end of the world * (which is how you kill yourself if a PROCESS. A TASK would have * had to RemTask(NULL) itself). */ /*DB*/ dbuninit(); CloseLibrary(DOSBase); } /*DB*/ static FH *debugfh; /*DB*/ /*DB*/ dbinit() /*DB*/ { /*DB*/ debugfh = Open("con:0/0/640/150/debugwindow", 1006); /*DB*/ /*DB*/ } /*DB*/ /*DB*/ dbuninit() /*DB*/ { /*DB*/ Close(debugfh); /*DB*/ } /*DB*/ /*DB*/ dbprintf(a,b,c,d,e,f,g,h,i,j) /*DB*/ { /*DB*/ static char buf[256]; /*DB*/ /*DB*/ sprintf(buf,a,b,c,d,e,f,g,h,i,j); /*DB*/ Write(debugfh,buf,strlen(buf)); /*DB*/ /*DB*/ }