home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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 <srcdevice> <destdevice> <start> <stop> <newstart>
- *
- * The parameters are as follows (example values are in brackets)
- *
- * <srcdevice> The device to copy from (DF0:)
- * <destdevice> The device to copy to (RAD:)
- * <start> The starting cylinder on <srcdevice> (60)
- * <end> The ending cylinder on <srcdevice> (79)
- * <newstart> The starting cylinder on <destdevice> (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<name> Sets RAD: volume name to be <name> ("RAMB0")
- * -n<name> Sets RAD: device name to be <name> ("RAD") (no colon!)
- * -d<name> Sets RAD: exec device to be <name> ("ramdrive.device")
- * -e<txt> Echos <txt> 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 <flags> <from> <to> <start> <end> <newstart>\n\n",
- " <flags> If any of the following are included, then RAD: will be\n",
- " automatically mounted when tcopy is run:\n\n",
- " -v<name> Sets RAD: volume name to be <name> (\"RAMB0\")\n",
- " -n<name> Sets RAD: device name to be <name> (\"RAD\")\n",
- " -d<name> Use exec device <name> (\"ramdrive.device\")\n",
- " \"-e<txt>\" Prints <txt> 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 <from> <to> <start> <end> <newstart>\n\n");
- #endif RAMBO
- print(" <from> Device to copy from (e.g. DF0:)\n");
- print(" <to> Device to copy to (e.g. RAD:)\n");
- print(" <start> Cylinder to start reading from (e.g. 70)\n");
- print(" <end> Cylinder to end reading at (e.g. 79)\n");
- print(" <newstart> 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);
- }
-