/* * TRACKCOPY.C * * (C) Copyright Eddy Carroll 1989 * * This program allows you to copy a portion of one disk device to a portion * of another device which has similar sector and track layout, though may * have a different number of cylinders. It is primarily intended to allow * a recoverable ram disk like RAD: to be preloaded directly from floppy * disk at high speed, a track at a time, instead of having to copy in files * manually. * * Usage: tcopy * * The parameters are as follows (example values are in brackets) * * The device to copy from (DF0:) * The device to copy to (RAD:) * The starting cylinder on (60) * The ending cylinder on (79) * The starting cylinder on (0) * * For example, supposing you want to setup a disk that you can load into * a 264K RAD (equals 24 cylinders of 2 tracks of 11 sectors of 512 bytes). * First of all, you setup RAD: manually by mounting it and then copying * in the appropriate files. Then you put a blank formatted disk into * drive 0 and give the command: * * tcopy RAD: DF0: 0 23 56 * * Meaning: Copy cylinders 0-23 of RAD: to cylinders 56-79 of DF0:. * * You then add enough files to DF0: to make a minimal boot disk, taking care * not to add more than (56-40 = 16) cylinders of data (or 176K), since * this would cause the data you've just written directly onto the disk * to be overwritten. Then put the command: * * tcopy DF0: RAD: 56 79 0 * * into your startup-sequence, and when you reboot, RAD: will be reloaded * with the appropriate information. * ---- * An additional option may be compiled in by defining the constant * RAMBO at compile time. When this is done, tcopy will have * some additional options which allow rad: to be automatically mounted * and initialised. This is designed to remove the overhead of having * to do a MOUNT RAD: in your startup-sequence. When this is the case, * the following options may be specified before the rest of the command * line. If none are specified, RAD: is not mounted. Defaults are in (). * * -v Sets RAD: volume name to be ("RAMB0") * -n Sets RAD: device name to be ("RAD") (no colon!) * -d Sets RAD: exec device to be ("ramdrive.device") * -e Echos to stdout * -a Always do track copy, even if RAD: already mounted * -f# Sets flags for OpenDevice() to # (0) * -m# Sets BufMemType to # (1) * -p# Sets boot priority to # (-1) * -t# Sets number of tracks(%) to # (10) * -u# Sets unit number of device to be # (0) * * (%) Actually the number of cylinders, but people seem to like to think * of them as tracks. */ #define TRUE 1 #define FALSE 0 #define LASTCHAR(s) (s[strlen(s)-1]) #ifndef LATTICE_50 #include "system.h" #endif extern struct DosLibrary *DOSBase; typedef struct IORequest IOREQ; typedef struct MsgPort MSGPORT; typedef struct Process PROC; typedef struct StandardPacket STDPKT; void inhibit(MSGPORT *devport, int mode); /* * Structure representing a disk device */ typedef struct { char *devname; /* Name of exec device for this disk */ int isfloppy; /* True if device is trackdisk.device */ ULONG unit; /* Unit number of above exec device */ ULONG blocksize; /* Number of bytes per block */ ULONG blockspertrack; /* Number of blocks/sectors per track */ ULONG surfaces; /* Number of tracks per cylinder */ ULONG lowcyl; /* Starting cylinder of disk on device */ ULONG numcyl; /* Number of cylinders on this disk */ } DISKDEV; #ifdef RAMBO /************************** Declarations for RAMBO **************************/ struct ExpansionBase *ExpansionBase; char execname[] = "ramdrive.device"; char dosname[] = "RAD"; char *volname = "RAMB0"; int bootpri = -1; /* * Parameter block for mounting RAD: */ struct MountParamBlock { char *dosname; char *execname; ULONG unit; ULONG flags; ULONG size; ULONG blocksize; ULONG origin; ULONG surfaces; ULONG sectorperblock; ULONG sectorspertrack; ULONG reserved; ULONG prealloc; ULONG interleave; ULONG lowcyl; ULONG highcyl; ULONG numbuffers; ULONG bufmemtype; } mount = { dosname, execname, 0, /* 2: Unit number */ 0, /* 3: OpenDevice() flags */ /* This is where the environment block starts */ 11, /* 4: Table upper bound */ 512>>2, /* 5: Number of longwords per block */ 0, /* 6: Sector origin - unused */ 2, /* 7: Number of surfaces */ 1, /* 8: Sectors per block - unused */ 11, /* 9: Sectors per track */ 2, /* 10: Reserved blocks - boot block */ 0, /* 11: Reserved blocks at end */ 0, /* 12: Interleave */ 0, /* 13: Low cylinder */ 10, /* 14: High cylinder */ 5 /* 15: Number of buffers */ }; /*********************** End of declarations for RAMBO *********************/ #endif RAMBO /* * Global variables */ char *srcname; /* AmigaDos name of source device */ char *destname; /* AmigaDos name of destination device */ DISKDEV src[1]; /* Source disk device */ DISKDEV dest[1]; /* Destination disk device */ struct IOStdReq *srcreq; /* Standard request for source device */ struct IOStdReq *destreq; /* Standard request for destination device */ MSGPORT *reqport; /* Message port for replies from devices */ MSGPORT *destport; /* Message port of process of dest. device */ void *buffer; /* Pointer to data buffer for track r/w */ long cylsize; /* Size in bytes of a single cylinder */ int srcdevopen; /* True if source exec device is open */ int destdevopen; /* True if destination device is open */ int inhibited; /* True if destination device is inhibited */ /* * print() * ------- * Outputs a message to stdout */ void print(char *s) { Write(Output(), s, strlen(s)); } #define print2(s1,s2) (print(s1),print(s2)) #define print3(s1,s2,s3) (print(s1),print(s2),print(s3)) /* * numtostr() * ---------- * Simple little function which returns a pointer to a static string * representation of the passed in number. */ char *numtostr(int n) { static char s[20]; int i = 19; s[19] = '\0'; if (n) while (n) { s[--i] = '0' + (n % 10); n /= 10; } else s[--i] = '0'; return(&s[i]); } /* * cleanup() * --------- * Closes all opened resources, and exits with specified error code. */ void cleanup(int code) { if (buffer) FreeMem(buffer, cylsize); if (srcdevopen) { if (src->isfloppy) { /* Turn off drive motor if floppy disk */ srcreq->io_Command = TD_MOTOR; srcreq->io_Length = 0; DoIO((IOREQ *)srcreq); } CloseDevice((IOREQ *)srcreq); } if (destdevopen) { if (dest->isfloppy) { /* Turn off drive motor if floppy disk */ destreq->io_Command = TD_MOTOR; destreq->io_Length = 0; DoIO((IOREQ *)destreq); } CloseDevice((IOREQ *)destreq); } if (inhibited) inhibit(destport, FALSE); if (srcreq) DeleteStdIO(srcreq); if (destreq) DeleteStdIO(destreq); if (reqport) DeletePort(reqport); #ifdef RAMBO if (ExpansionBase) CloseLibrary((struct Library *)ExpansionBase); #endif RAMBO exit(code); } /* * chkabort() * ---------- * A replacement for Lattice's chkabort(), which doesn't carry all * the extra baggage. If CTRL-C is detected, this function never * returns but instead calls cleanup. Since Lattice's exit() code * may call chkabort(), a check is made to ensure that cleanup() * only gets called once, otherwise there would be a problem if the * user pressed Ctrl-C twice in quick succession. */ void chkabort() { static int gotctrlc = FALSE; if (!gotctrlc && (SetSignal(0,0) & SIGBREAKF_CTRL_C)) { gotctrlc = TRUE; print("^C\n"); cleanup(20); } } /* * GetVolume() * ----------- * This function searches the device list for the named volume, and * fills in the passed DISKDEV structure with information about the * volume. If the named volume is not a device, or is not a disk, * then FALSE is returned. It is not an error to pass in NULL as * a pointer to the DISKDEV structure. In this case, nothing will * be filled in, but TRUE or FALSE will be returned indicating whether * the device is a disk device or not. */ int GetVolume(char *devname, DISKDEV *dev) { struct RootNode *rootnode; struct DosInfo *dosinfo; struct DeviceNode *devnode; struct FileSysStartupMsg *startup; struct DosEnvec *dosenvec; unsigned char *p; int namelen = strlen(devname); if (LASTCHAR(devname) != ':') /* Device names must end with ':' */ return (FALSE); /* * First of all, find the device */ rootnode = (struct RootNode *)DOSBase->dl_Root; dosinfo = (struct DosInfo *)BADDR(rootnode->rn_Info); devnode = (struct DeviceNode *)BADDR(dosinfo->di_DevInfo); Forbid(); /* Make sure list doesn't change while we scan it */ while (devnode != NULL) { p = (unsigned char *)BADDR(devnode->dn_Name)+1; if (!strnicmp(devname, p, namelen-1)) { /* Don't compare the ':' */ /* * Matched name successfully. Now check if it's a device. * Note that we carry on searching if it's not a device * (rather than returning FALSE immediately) since there * may be a volume called RAD: as well as a device called * RAD:, for example. */ if (devnode->dn_Type == DLT_DEVICE) { if (devnode->dn_Startup < 20) /* Is there a startup bit? */ goto notfound; /* If not, not disk device */ /* Eek! A GOTO! */ startup = (struct FileSysStartupMsg *) BADDR(devnode->dn_Startup); if (dev) { dev->devname = ((char *)BADDR(startup->fssm_Device))+1; dev->isfloppy = (!strcmp(TD_NAME, dev->devname)); dev->unit = startup->fssm_Unit; } if (startup->fssm_Environ < 20) goto notfound; /* Another GOTO! The Earth will end in 5 seconds... */ dosenvec = (struct DosEnvec *)BADDR(startup->fssm_Environ); if (dev) { dev->blocksize = dosenvec->de_SizeBlock << 2; dev->blockspertrack = dosenvec->de_BlocksPerTrack; dev->surfaces = dosenvec->de_Surfaces; dev->lowcyl = dosenvec->de_LowCyl; dev->numcyl = (dosenvec->de_HighCyl - dosenvec->de_LowCyl) + 1; } Permit(); return (TRUE); } } devnode = (struct DeviceNode *)BADDR(devnode->dn_Next); } notfound: Permit(); return (FALSE); } /* * help() * ------ * Prints out a help message about tcopy */ void help() { print( "Tcopy (C) Copyright Eddy Carroll, January 1990. Freely distributable.\n" ); #ifdef RAMBO #define print13(a,b,c,d,e,f,g,h,i,j,k,l,m) (print3(a,b,c),print3(d,e,f),\ print3(g,h,i),print3(j,k,l),print(m)) print13( "Usage: tcopy \n\n", " If any of the following are included, then RAD: will be\n", " automatically mounted when tcopy is run:\n\n", " -v Sets RAD: volume name to be (\"RAMB0\")\n", " -n Sets RAD: device name to be (\"RAD\")\n", " -d Use exec device (\"ramdrive.device\")\n", " \"-e\" Prints to stdout\n", " -a Do trackcopy, even if RAD: already mounted\n", " -f# Sets flags for OpenDevice() to # (0)\n", " -m# Sets BufMemType to # (1)\n", " -p# Sets boot priority to # (-1)\n", " -t# Sets number of tracks to # (10)\n", " -u# Sets unit number of exec device to # (0)\n\n" ); #else print("Usage: tcopy \n\n"); #endif RAMBO print(" Device to copy from (e.g. DF0:)\n"); print(" Device to copy to (e.g. RAD:)\n"); print(" Cylinder to start reading from (e.g. 70)\n"); print(" Cylinder to end reading at (e.g. 79)\n"); print(" Cylinder to start writing at (e.g. 0)\n"); } /* * opendevs() * ---------- * Opens the source and destination devices, allocates buffer for * reading and writing etc. Note that if either device is a floppy * drive, the buffer must be allocated in chip memory or trackdisk.device * won't be able to blit into it. */ void opendevs() { long memflags = 0; if (src->isfloppy || dest->isfloppy) memflags |= MEMF_CHIP; cylsize = src->blocksize * src->blockspertrack * src->surfaces; buffer = AllocMem(cylsize, memflags); if (!buffer) { print("Not enough memory for track buffer\n"); cleanup(20); } reqport = (MSGPORT *)CreatePort(0,0); if (!reqport) { print("Couldn't allocate message port\n"); cleanup(20); } srcreq = CreateStdIO(reqport); destreq = CreateStdIO(reqport); if (!srcreq || !destreq) { print("Couldn't allocate IO request - memory is low!\n"); cleanup(20); } if (OpenDevice(src->devname, src->unit, (IOREQ *)srcreq, 0L)) { print3("Can't open source ", src->devname, "\n"); cleanup(20); } srcdevopen = TRUE; if (OpenDevice(dest->devname, dest->unit, (IOREQ *)destreq, 0L)) { print3("Can't open destination ", dest->devname, "\n"); cleanup(20); } destdevopen = TRUE; } /* * copytracks() * ------------ * This is where the actual work gets done. Tracks (cylinders actually) * are copied from start to end on the source device to newstart on * the destination device. */ void copytracks(int start, int end, int newstart) { int cyl, retry, numcyls = (end - start) + 1; for (cyl = 0; cyl < numcyls; cyl++) { /* * First read in track from source device */ for (retry = 0; retry < 3; retry++) { chkabort(); srcreq->io_Command = CMD_READ; srcreq->io_Length = cylsize; srcreq->io_Offset = (src->lowcyl + cyl + start) * cylsize; srcreq->io_Data = buffer; if (!DoIO((IOREQ *)srcreq)) break; /* Succeeded, so break out of loop */ } if (retry == 3) { print3("Error reading track ", numtostr(cyl+start)," from disk\n"); cleanup(20); } /* * Now write out track to destination device */ for (retry = 0; retry < 3; retry++) { chkabort(); destreq->io_Command = CMD_WRITE; destreq->io_Length = cylsize; destreq->io_Offset = (dest->lowcyl + cyl + newstart) * cylsize; destreq->io_Data = buffer; if (!DoIO((IOREQ *)destreq)) break; /* Succeeded */ } if (retry == 3) { print3("Error writing track ", numtostr(cyl), " to disk\n"); cleanup(20); } } } /* * SendPacket() * ------------ * ``Sort of'' simulates the ARP SendPacket() routine which sends * a packet to AmigaDos, and gets a reply if appropriate. What is * passed in is the action to be executed (one of the ACTION_xxx * definitions in dosextens.h), a pointer to a longword array of 7 * arguments to be passed to the device, and the msgport of the device * as returned by DeviceProc("DF0:") for example. If result is non-NULL * then it should be a pointer to a two element array of ULONGs, and it * fills in the 0th element with the primary result, and the the * 1st element with the secondary result. */ int SendPacket(ULONG action, void *args, MSGPORT *devport, ULONG *result) { PROC *proc = (PROC *)FindTask(NULL); STDPKT *packet; packet = (STDPKT *)AllocMem(sizeof(STDPKT), MEMF_CLEAR | MEMF_PUBLIC); if (!packet) return (FALSE); packet->sp_Msg.mn_Node.ln_Name = (char *)&packet->sp_Pkt; packet->sp_Pkt.dp_Link = &packet->sp_Msg; packet->sp_Pkt.dp_Port = &proc->pr_MsgPort; packet->sp_Pkt.dp_Type = action; memcpy(&packet->sp_Pkt.dp_Arg1, args, sizeof(ULONG) * 7); /* * Okay, we've done the necessary magic to create an AmigaDos * packet lookalike (thanks to Matt Dillon in Transactor V1.1). * Now we send the message to the Dos process, and get the reply. * Then our message will be filled in with the response from the * Dos process. */ PutMsg(devport, (struct Message *)packet); WaitPort(&proc->pr_MsgPort); GetMsg(&proc->pr_MsgPort); if (result) { result[0] = packet->sp_Pkt.dp_Res1; result[1] = packet->sp_Pkt.dp_Res2; } FreeMem(packet, sizeof(STDPKT)); return (TRUE); } /* * inhibit() * --------- * This function inhibits (if mode is TRUE) or hibits (if mode is FALSE) * (is hibit the opposite of inhibit? Hmmm...) the specified device. */ void inhibit(MSGPORT *devport, int mode) { ULONG pktargs[7]; int i; pktargs[0] = mode; /* Select inhibit or opposite */ for (i = 1; i < 7; i++) /* Clear other arguments */ pktargs[i] = 0; if (!SendPacket(ACTION_INHIBIT, pktargs, devport, NULL)) { print("Couldn't send inhibit packet to device\n"); cleanup(20); } } #ifdef RAMBO /* * mountramdisk() * -------------- * This procedure simply mounts the device specified in the mount * parameter block. */ int mountramdisk() { struct DeviceNode *devnode; long args[7]; int i; char *diskname; MSGPORT *devport; static char dosname[200]; /* Temporary storage for dos device name */ ExpansionBase = (struct ExpansionBase *) OpenLibrary("expansion.library", 0L); if (!ExpansionBase) { print("Couldn't open expansion.library\n"); cleanup(20); } devnode = MakeDosNode(&mount); if (!devnode) return (FALSE); AddDosNode(bootpri, ADNF_STARTPROC, devnode); /* * Now we've mounted the device, let's try and rename it to * something different. */ strcpy(dosname, mount.dosname); strcat(dosname, ":"); devport = (MSGPORT *)DeviceProc(dosname); if (!devport) return (FALSE); for (i = 1; i < 7; i++) args[i] = 0; /* * Some horrible messing around to make a BSTR */ diskname = AllocMem(strlen(volname)+1,MEMF_PUBLIC); strcpy(diskname+1, volname); *diskname = strlen(volname); args[0] = ((long)diskname)>>2; if (!SendPacket(ACTION_RENAME_DISK, args, devport, NULL)) print("(Couldn't relabel disk\n"); /* Don't return an error, even if SendPacket failed! */ FreeMem(diskname, strlen(volname)+1); return (TRUE); } #endif RAMBO /* * mainline() * ---------- */ void main(argc,argv) int argc; char **argv; { unsigned int start, end, newstart; /* Cylinder numbers */ #ifdef RAMBO /************************* Start of RAMBO Stuff ***************************/ int doload = FALSE; /* If true, always copy to device even if mounted */ if (argc > 1 && argv[1][0] == '-') { /* Handle Rambo options */ static char tempname[200]; while (argc > 1 && argv[1][0] == '-') { char *str = &argv[1][2]; /* Have handy ptr to string */ int num = atoi(str); /* Have parameter ready */ switch (argv[1][1]) { case 'v': /* Set volume name */ volname = str; break; case 'n': /* Set device name */ /* Strip trailing ':' */ if (LASTCHAR(str) == ':') LASTCHAR(str) = '\0'; mount.dosname = str; break; case 'd': /* Set exec device name */ mount.execname = str; break; case 'u': /* Set device unit number */ mount.unit = num; break; case 'f': /* Set flags for OpenDevice() */ mount.flags = num; break; case 't': /* Set number of tracks/cylinders */ mount.highcyl = num - 1; break; case 'm': /* Set memory type */ mount.bufmemtype = num; break; case 'p': /* Set boot priority */ bootpri = num; break; case 'e': /* Echo message to stdout */ print2(&argv[1][2], "\n"); break; case 'a': /* Force loading into RAD: */ doload = TRUE; break; default: print3("Unknown switch ", argv[1], "\n"); help(); cleanup(5); } argv++; argc--; } /* * Setup defaults for mount okay. Now see if the device is * to be mounted. If it is, then try and mount it. If it was * already mounted, then quit immediately, unless forceload is * in effect. * * Else, see are there any files in RAD:. If there are, quit * immediately (since we are under Kikstart 1.2, and RAD: has * just recovered itself from memory), unless forceload is * in effect. */ strcpy(tempname, mount.dosname); strcat(tempname, ":"); if (!GetVolume(tempname, NULL)) { if (!mountramdisk()) { print3("Error: Couldn't mount device ",tempname,"\n"); cleanup(20); } } /* * Scan device and see if there are files on it. * Do this by seeing if there are any files in * the directory. If there aren't, then we can * force the load, otherwise don't force it. */ { struct FileInfoBlock *fib; BPTR lock; fib = AllocMem(sizeof(struct FileInfoBlock), 0L); if (!fib) { print("Out of memory allocating FileInfoBlock!\n"); cleanup(20); } lock = Lock(tempname, ACCESS_READ); if (lock) { if (Examine(lock, fib)) { if (!ExNext(lock, fib)) doload = TRUE; } UnLock(lock); } FreeMem(fib, sizeof(struct FileInfoBlock)); } /* * Now see if after all that, we still want to go * ahead with the load into RAD:. If not, then just * exit silently (to let the startup-sequence carry * on okay). */ if (!doload) cleanup(0); } /************************* End of RAMBO Stuff ***************************/ #endif RAMBO #define NONUM(x) (!isdigit(argv[x][0])) if (argc != 6 || NONUM(3) || NONUM(4) || NONUM(5)) { help(); cleanup(5); } srcname = argv[1]; destname = argv[2]; start = atoi(argv[3]); end = atoi(argv[4]); newstart = atoi(argv[5]); if (!GetVolume(srcname, src)) { print2(srcname, " is not a valid disk device\n"); cleanup(20); } if (!GetVolume(destname, dest)) { print2(destname, " is not a valid disk device\n"); cleanup(20); } #define NOTSAME(x) (src->x != dest->x) if (NOTSAME(blocksize) || NOTSAME(blockspertrack) || NOTSAME(surfaces)) { print3(srcname, " and ", destname); print(" do not have same track sizes\n"); cleanup(20); } if (start > end) { print("Start track is greater than end track.\n"); cleanup(20); } if (end >= src->numcyl) { print3("Maximum end track on ", srcname, " is"); print2(numtostr(src->numcyl - 1), ".\n"); cleanup(20); } if ((newstart + (end - start)) >= dest->numcyl) { print2("There is not room for ", numtostr(1 + end - start)); print3(" tracks on ", destname, "\n"); cleanup(20); } destport = (MSGPORT *)DeviceProc(destname); if (!destport) { print3("Can't locate process for device ", destname,"\n"); cleanup(20); } /* * The two devices are valid, so now open the exec devices which * they use. */ opendevs(); inhibit(destport, TRUE); inhibited = TRUE; copytracks(start, end, newstart); cleanup(0); }