The ChAoS Disk Utility, or "CDU", is a command-line based program intended to manipulate sectors on a disk at low-level. Its main use is for disk-to-disk or disk-to-file backups, although, because of the flexibility of the command-line interface, it can also be pressed into service when partition tables, boot records and so on have to be manipulated.
At present, there is one important CDU limitation point of which you should be aware:
All devices must use a 512 byte sector size. We tried, at first, to make CDU handle any combination of sector size but there was a problem - using a compiler with a maximum of 32-bit integers means that, if one was to store sizes and offsets as byte relative, disk and file sizes would be limited to only four gigabytes and disks, of course, are already much bigger than that. Therefore, we're currently employing a scheme whereby all offsets and size are sector relative. As a result, handling differing sector sizes becomes much more difficult. Therefore, for the time being at least, CDU will only handle the standard IBM PC sector size.
This has repercussions for non-block devices. In particular, if the length of a file is not a multiple of 512 bytes, the "remainder" is ignored.
This should not be too much of a limitation if you stick to manipulating standard IBM PC volumes. However, CD-ROMs, for example may very well be "out of bounds" to CDU.
In order to have a "standard" environment to run this tutorial, do the following:
Create a temporary directory somewhere and make it your "current" directory.
Make sure that the CDU.EXE program is on your system's search path but don't put it into your temporary directory.
Obtain a scratch 3.5" floppy diskette. Format it and make sure that it has no "bad" sectors on it by checking the totals at the end of the format.
CDU is command-line based and we've tried to build as much flexibility in as possible by allowing anything that appears on the command-line as parameters to be included in a "response" file. Indeed, the only significant differences between the command-line parameters and those in a response file is that the former has "CDU" at the start of the line and that the latter can have comments included.
CDU will always read the "response" file CDU.CDU (if it exists) before it executes the command-line. Thus, if one uses something like:
CDU INCLUDE EXAMPLES
CDU will run the CDU.CDU file and then execute commands in the command-line cited response file. In this case, CDU.CDU will be executed first and then EXAMPLES.CDU will be executed.
It's time to actually execute CDU so let's try this:
CDU always tells you its version number regardless of how little or much it does. In this case, it "knows" that there is nothing to do and, hence, it displays a little text to tell you what it is and a hint about how to get more help.
Try invoking the help page - CDU will produce loads of output and, thus, you may wish to pipe it to your favourite text viewer or editor:
The help pages give the full grammar of CDU's script "language".
Let's try provoking CDU into giving us some information about the disk drives that it can "see". Try this:
On our machine, we get:
CDU V0.6.038 Mar 02 2001 HD0: 1547 MB HD1: 6173 MB FD0: 1440 KB
Each line (except the "sign-on" text) starts with the name of a disk stream device. (In the rest of this tutorial, we have removed the sign-on text since it is always present and may actually be different to the text generated by your copy of CDU.) In our case, we have three disk drives. Namely, HD0, HD1 and FD0. It's time to meet the DETAIL directive. Run this:
CDU DETAIL ON INFO
Our results are:
HD0: i13ex 0003169152 512 786 64 63 HD1: i13ex 0012643155 512 787 255 63 FD0: i13ex 0000002880 512 80 2 18
This tells us that CDU is using the extended INT13 API to access all three drives. It also tells us the last logical sector number, the sector length in bytes, the number of cylinders, the number of heads and the number of sectors on each track.
By the way, the DETAIL word cited above is one of CDU's "switches" - it takes one of only two states: "on" or "off". When changing the state of a switch, one can follow it by an ON word, an OFF word or neither. If you leave the new state out, it will toggle the value. Hence, since the DETAIL switch defaults to an "off" state, the previous command line could've been written as follows and produced the same results:
CDU DETAIL INFO
In fact, this command can be shortened even further since, just for convenience, the sequence "DETAIL ON INFO" has a synonym. Therefore, the command could've been specified as:
Commands can be strung together both on the command-line and within response files. The limitation on line length in the latter is fairly large and unlikely to seriously hamper the programmer.
Let's try another simple directive - this next one, the VERBOSE directive, is another switch. Once more, it merely alters the amount of "drivel" that CDU generates. In the case of VERBOSE, it's usually only employed when bug hunting a script:
CDU VERBOSE INFO
This will produce a whole load of junk including some interesting information gleaned when CDU tries to "interrogate" the disk devices. (Once again, notice that the short form of "VERBOSE ON" has been used.)
When running commands, CDU does not interrogate the hard drives at an arbitrary point - only certain commands provoke a probe of the disk drives. The list is as follows:
The reason why only certain commands start a disk probe is that the user can manipulate the list of disks "seen" by CDU before the disk probe is done. Given that your disk list included an entry for hard drive zero ("HD0"), try the following:
CDU DELETE HD0 VINFO
On our machine, this gives:
HD1: i13ex 0012643155 512 787 255 63 FD0: i13ex 0000002880 512 80 2 18
Notice that "HD0" has mysteriously gone missing from the list.
The justification for including such facilities is that the disk detection procedure is not fool-proof. Hence, you may have to fiddle with the system until CDU "gets it right". Usually, you do not.
In addition to be able to delete and add drives to the disk list, you can also dictate which disk interface CDU will use on a particular drive. For example, let's say that I want to remove HD0 from list (that is, prevent CDU from "seeing" it) and to force HD1 to be accessed via the bog-standard INT13 API instead of the go-faster INT13EX API:
CDU DELETE HD0 IFACE HD1 INT13 VINFO
This gives us:
HD1: i13 0012643155 512 787 255 63 FD0: i13ex 0000002880 512 80 2 18
Note that, as before, HD0 is missing but that also the interface entry for HD1 has changed from "i13ex" to "i13". (In fact, I sometimes have to force an INT13 interface when accessing floppy drives - MSdos and Win95 appear to be very touchy in this respect.)
One of the reasons behind wanting to write CDU was that we could find no commercial backup programs that would allow us to do fool-proof backups. Let us explain: let's say that you have two hard drives and, in the absence of removable storage devices of a sufficiently large capacity, you employ a disk-to-disk backup strategy. That is, each time you want to backup, you copy the entire contents, on a cylinder-by-cylinder basis, from the master disk to the backup. If you want to do a restore, this is no problem - you merely copy the data in the other direction.
However, one day, you're a bit tired and you copy from, HD1 to HD0 instead of from HD0 to HD1. Result? Disaster! You've over-written your master disk with an out-of-date backup. So, how can we avoid this situation?
The fact is that each IDE hard drive has an identifier that comprises the make/model string and serial number of the drive. The combination of these two items is unique - no two drives have the same combination of strings. So, how does this help us? Well, if the backup program can retrieve this information and you assign a disk name to each of your hard disks, you're much less likely to make a mistake.
In fact, given the above non-removable hard drive scenario, the benefit isn't so great. However, we backup, using the same disk-to-disk copy strategy, on removable media. Not only is it possible to get the wrong hard drive number, but we can end up with the wrong drive in the wrong drive bay . As a result, it's possible that even though we've nominated the correct drive numbers, being in the wrong drive bays means that the copy will still be in the wrong direction. If, though, we can specify drive names and the program can identify the drives via those names, things are much better.
So far, so good. But, there's a problem - in their wisdom, the writers of the INT13EX API did not see fit to allow the programmer to do a "get drive parameters". The very latest versions of this API do have a command that allows the programmer to send an arbitrary command to the drive and that could, indeed, be used to retrieve the disk parameters. However, we've yet to see a machine that has that version of the API. So, for now at least, we're stuck with the kludge - we've written our own software that goes directly to the disk hardware to get the identification information that we need.
It goes without saying that this is not an ideal situation. Since, though, it does appear to work, we've left the code in. However, since it isn't totally reliable on all machines, we've turned it off by default.
Enough waffle - let's play with this magic facility! Try this:
CDU IDENTIFY INFO
On our machine, we get:
HD0: 1547 MB WDCAC21600H:WD-WM3365525706 HD1: 6173 MB FUJITSUMPC3064AT:05022143 FD0: 1440 KB
Note that some drivel has appear at the end of each hard disk line. That's the combination of make/model string and serial number. In fact, CDU removes all non-printable characters from the data and concatenates the two strings using a colon.
Assuming that you do have something that is recognisably close to what we have above, it's time to make a script file that'll allow us to use these disk serial numbers.
Run the above command again but, this time, re-direct it to a file called NAMES.CDU in your current (temporary) directory, thus:
CDU IDENTIFY INFO > NAMES.CDU
Now, using your favourite text editor, alter it to look like this (but using your own serial numbers of course):
IDENTIFY ON NAME JIM WDCAC21600H:WD-WM3365525706 NAME BILL FUJITSUMPC3064AT:05022143
Now try the following so that CDU will run your newly-created script file:
CDU @NAMES INFO
We get the following results:
HD0: 1547 MB JIM WDCAC21600H:WD-WM3365525706 HD1: 6173 MB BILL FUJITSUMPC3064AT:05022143 FD0: 1440 KB
Note the appearance of the strings "JIM" and "BILL" that identify the two drives HD0 and HD1.
Finally, rename the file NAMES.CDU to CDU.CDU and try this:
You should have been presented with the same results as before. When CDU starts up, it tries to find a file called CDU.CDU in the current directory and, if it can't find it, it looks in the "program" directory (that is, where the executable was stored). As soon as it finds a file called CDU.CDU, it runs it. Hence, because we renamed the NAMES.CDU file as CDU.CDU, it will now run it every time CDU is run with the temporary directory as the "current" directory.
So, given that CDU.CDU is executed correctly, instead of using "DISK HD0" and "DISK HD1", we could use "DISK JIM" and "DISK BILL" respectively. Note, too, by the way, that we have included an "IDENTIFY ON" directive in the CDU.CDU file so that drive identification is turned on no matter what default is currently in use.
Since some people may not be able to use the disk naming facility and we'd like all user's systems to be in a common state for the duration of this tutorial, delete the CDU.CDU file, now. (You can always replace it after you've finished using the tutorial.)
Please note the following:
Before using CDU for the first time, it's a good idea to just use "CDU INFO" to find out what drives it can "see". If the set of drives detected doesn't match what you were expecting, then use something like "CDU VERBOSE VINFO" instead. In other words, do the same thing again after invoking "waffle" mode.
Note that drives get disqualified if they don't appear to have extended INT13, ordinary INT13 or, if compiled in, the CHS values of a "get drive parameters" are not valid. The latter can happen if an "alien" floppy is put in the drive and a "DIR A:" is done.
A floppy drive will also not be "seen" if the operating system thinks that there is no floppy in the drive. In other words, it is sometimes necessary, before accessing a floppy disk, to make an attempt to access it by using the standard operating system commands. For example, a simply "DIR A:" is usually enough to allow CDU to access a floppy diskette.
CDU is a "stream based" program. In other words, the command operate on data streams and, with few exceptions, the commands operate in a similar manner regardless of the type of stream upon which it is operating.
The stream types are as follows:
DISK streams are the most fundamental. CDU accesses disk streams via the INT13 and INT13EX APIs and it's this stream type that would probably be used, for example, to backup a hard disk. Disk stream names are similar to those produced by the INFO command, above. Therefore, on our machine, we have three disk streams named "DISK HD0", DISK HD1" and "DISK FD0".
Most uses will be familiar with VOLUME streams. Your boot hard disk is probably "VOLUME C:" and your floppy drive is more than likely accessed by citing volume "VOLUME A:". Volume streams are almost as useful as disk streams. Note CDU may very well be able to access a "strange" device (a SmartMedia card reader, for example) as a volume stream whereas it would have no chance accessing it as a disk stream. (Quite simply, because it ain't a physical hard disk or floppy drive.)
FILE streams are merely MSdos files. Hence, an example of a file stream name would be "FILE FLOPPY.IMG". (We have adopted the standard extension of .IMG for disk image files. We suggest that you do the same since, at a later stage, we may alter CDU to recognise such a name automatically and, therefore, reduce the complexity of the CDU command-line.)
An ARCHIVE stream is merely a file which has been compressed via the GZIP system. Hence, an example name would be "ARCHIVE FLOPPY.GZ". (Once again, we suggest that you stick to the "standard" extension.) CDU always compresses a single file into each archive file. Please note that these are standard GZIP files and, hence, can be accessed by other compression programs.
A BYTE stream is an infinite sequence of a single data byte. The "name" of the stream is, in fact, merely the numeric value of the required byte. For example, to generate an infinite stream of exclamation mark characters, you could use the stream names "BYTE 0x21" or "BYTE 33".
You should note that, at least with the current version of CDU, when a stream name is required, you must always supply the stream type and stream name.
Making sure that you've still got your scratch floppy disk in the drive, try searching for bad sectors on it. For this, we will first try the DUMP command:
CDU DUMP DISK FD0 RANGE :+1
When you run this, will present you with something ike the following:
COMMAND: DUMP source disk FD0 range start:start Do you wish to execute this dump? (Y/N):
Note that we've asked to dump only the first sector of the diskette by citing the range "RANGE :+1". (The syntax of the RANGE clause is explained in a separate section.)
CDU has asked you to confirm that you want to carry on with the DUMP command. Reply with a 'Y'. After a very short pause, CDU should give a "happy" beep and the output on the screen should change to:
COMMAND: DUMP source disk FD0 range start:start Do you wish to execute this dump? (Y/N): Y DUMP: 0000000001 0000000000 100% Dump completed
The line of text starting with "DUMP:" is user "comfort". That is, it serves merely to let you know that something is happening. The first number is the number of sectors processed so far (here, just the one) and the second number is the number of sectors that remain unprocessed (none, of course). The last number is the percentage of the task that's been done so far and, in this case, we've finished the task so it reads one hundred percent. Finally, "Dump completed" tells us that all went OK.
In this instance, the DUMP command hasn't actually done anything with the data that it's read. (Mind you, it has indicated that the data is readable, of course. In other words, you've done a bad sector search on the diskette's first sector.) Let's make something more interesting happen:
CDU DETAIL DUMP DISK FD0 RANGE :+1
As before, CDU will ask whether you want to continue and you should reply with a 'Y'. (From now on, always reply with a "yes" to the "do you want to execute" questions unless otherwise stated.) This time, you will note that CDU has printed the contents of the first diskette sector on the screen.
Having seen the DUMP command in action, let's try doing a COPY command.
The COPY command requires that you nominate an input stream and an output stream. Let's assume that we want to take the whole of the floppy's image and stuff it into a file on our hard disk. Do this:
CDU COPY DISK FD0 TO FILE FLOPPY.IMG
While the COPY runs, you'll see that the comfort text steadily changes the numbers until each operation has been carried out. At the end, CDU should have given you a happy beep. After that copy, a DIR command now shows the following. (Please note that for the purpose of writing this tutorial, we are running CDU under Win95, not MSdos.)
FLOPPY IMG 1,474,560 04/03/01 14:31 FLOPPY.IMG
Have a look in the FLOPPY.IMG file using your favourite text viewer or editor. Hereinafter, we assume that you've the sense to do this when we say "look at the data" or whatever. Yes, you've guessed it! The data from your floppy disk is, indeed, in the FLOPPY.IMG file.
Note, by the way, that CDU will not ask you whether you want to overwrite any existing target file - it just goes ahead and does it. This is a damned good reason for adopting the "standard" CDU file extensions; if you overwrite anything by accident, at least it's likely to be a file of the same type... You could, of course, set the "read only" attribute of an image file if the data was particularly valuable.
Using DISK streams isn't the only way of accessing the floppy - let's try saving our floppy disk image yet again but, this time, we'll copy it as a VOLUME stream. (In the following command, we are assuming that your floppy appears as drive A:; if it does not, then merely substitute the correct drive letter.) Try this:
CDU COPY VOLUME A: TO FILE FLOPPY2.IMG
The files in the current directory now look like this:
FLOPPY IMG 1,474,560 04/03/01 14:31 FLOPPY.IMG FLOPPY2 IMG 1,474,560 04/03/01 14:37 FLOPPY2.IMG
You've seen an example of how to access FILE and VOLUME streams but now let's try making an ARCHIVE. As you might expect, we can use a variation on a command we've already used. Let's try making a compressed image of our scratch floppy. Type this:
CDU COPY DISK FD0 TO ARCHIVE FLOPPY.GZ
The resulting file that you get will, of course, depend upon the data that was originally on the diskette. (Even though you formatted it, unless you did an "unconditional" format, most of the original data that was on there will still be on the diskette.)
After the previous command has run, on our machine, the directory listing looks like this:
FLOPPY IMG 1,474,560 04/03/01 14:31 FLOPPY.IMG FLOPPY2 IMG 1,474,560 04/03/01 14:37 FLOPPY2.IMG FLOPPY GZ 1,060,555 04/03/01 14:40 FLOPPY.GZ
Since the latest file that we've created was a compressed one, FLOPPY.GZ is somewhat smaller than FLOPPY.IMG. As we've already said, the compression format used is a pretty standard one and so we can do this sort of thing:
[WIN95] C:\Z\z>gzip -l floppy compressed uncompress ratio uncompressed_name 1060555 1474560 28.0% floppy
Finally, we introduce one more stream type: the BYTE stream. For example, Let us assume that you wanted a file that consisted of one thousand sectors worth of exclamation marks. This would do what we wanted:
CDU COPY BYTE '!' RANGE :+1000 TO FILE PLING.TXT
The files that we have now look like this:
FLOPPY IMG 1,474,560 04/03/01 14:31 FLOPPY.IMG FLOPPY2 IMG 1,474,560 04/03/01 14:37 FLOPPY2.IMG FLOPPY GZ 1,060,555 04/03/01 14:40 FLOPPY.GZ PLING TXT 512,000 04/03/01 14:43 PLING.TXT
If we had been backing up data, we might want to check that the copy really was the same as the original. What we need to do, of course, is a COMPARE command. Let's first see if our original diskette backup was, indeed, a true copy:
CDU COMPARE DISK FD0 WITH FILE FLOPPY.IMG
Our results are:
COMMAND: COMPARE source disk FD0 source file FLOPPY.IMG Do you wish to execute this compare? (Y/N): Y COMPARE: 0000002880 0000000000 100% Compare completed
From this, you can see that all the data was compared (we are at "100%") and that the compare completed OK (from the last line of text).
You should be able to compare any of the following items against each other and have them compare "OK": FD0, A:, FLOPPY.IMG, FLOPPY2.IMG, FLOPPY.GZ. In particular, try this:
CDU COMPARE FILE FLOPPY2.IMG WITH ARCHIVE FLOPPY.GZ
This will compare the second disk image (taken, if you remember, from "VOLUME A:") against the compressed file FLOPPY.GZ. Note that any data streams can be compared and that ARCHIVEs are always compressed when being written to and decompressed when being read if you specify them as an ARCHIVE stream. In other words, this compare will fail:
CDU COMPARE FILE FLOPPY2.IMG WITH FILE FLOPPY.GZ
In fact, if you try the above, CDU will almost certainly complain that FLOPPY.GZ is shorter than FLOPPY2.IMG and, therefore, cannot be identical. It will, though, allow you to continue and do the compare anyway. If you do that, you'll see something like this:
ERROR: Compare fails! ERROR: First source not exhausted ERROR: Second source not exhausted Compare failed - SourceA: sector 0 Compare failed - SourceB: sector 0 ERROR: Compare FAILED
Now let's try something that takes a bit longer to complete. This time, we'll try to capture the whole floppy image into a file using the COPY command and then check that the new copy is OK by using COMPARE. We will, however, being using a script file to do this.
Edit a new file called, say, FLOPPY.CDU and place in it this text:
CONFIRM OFF SOUND ON_ERROR COPY DISK FD0 TO FILE FLOPPY.IMG SOUND ON COMPARE DISK FD0 WITH FILE FLOPPY.IMG
The first line will stop CDU asking the "Do you want to do this?" questions. We're not doing any potentially destructive writing, so this is OK for now. The second line will stop us getting a happy beep if the COPY goes OK. (If the COPY fails, then the script will still terminate with a sad beep because we used "BEEP ON_ERROR" rather than "BEEP OFF".) The third line will backup up the entire diskette as no RANGE is specified. The fourth line re-enables the sound maker so that we'll get a beep whatever the outcome of the COMPARE on the last line. The COMPARE, of course, just ensures that the COPY went OK.
Now run this file by typing:
This should have copied a new floppy image to FLOPPY.IMG (notice that CDU did not ask you whether it was OK to overwrite the existing copy) and carried on to do a compare with no user intervention.
We now turn our attention to the SEARCH facility. Each MSdos floppy should have the byte sequence "55AA" in the last two bytes of the first sector. Let's assume that we want to look for it and confirm that it exists. Try this at the command-line:
CDU CONFIRM DETAIL SEARCH DISK FD0 FOR \x55\xAA
You should find that the pattern is found at offset 0x1FE. (Since there should be about forty lines of output, you'll have to pipe the output to a text viewer if your screen is only showing twenty-five lines.) Please note that, for once, case is important - the character following the text escapement back-slash must be lower-case. In other words, while "\x55\xAA" is all right, "\X55\XAA" would not work. This is one of the few places where case is significant to CDU.
Of course, since the floppy disk image is identical to the copy in the .IMG file on disk, this should produce the same result:
CDU CONFIRM DETAIL SEARCH FILE FLOPPY.IMG FOR \x55\xAA
Sector ranges are specified by citing a start sector and an end sector which are separated by a colon.
Each of these two sector numbers are made up of two parts, the absolute part and the relative part. Just before the command is executed, the relative part is added to (or subtracted from) the absolute part.
The absolute part can be either a simple number or it can be one of two words that indicate the start or end of the device's stream. So, for example, both "2880" and "END" can be used to refer to the last sector of a standard three inch floppy diskette. In order to refer to the sector before the last sector, one could use "2879" or the more lucid "END-1".
Let's say you want to copy all of a floppy except the first hundred sectors and the last hundred sectors. You could use a range of "100:2781". However, this range can also be written as "START+100:END-100".
If one of the sector numbers in a range has the absolute part missing, then that relative part is treated as a length and the absolute part is copied from the other sector number's absolute part.
For example, the first hundred sectors on our three inch floppy can be specified as:
"0:99" or "START:START+99" or "START:+100"
Note that when calculating "length" sector numbers, the calculation is applied taking into account any relative sector number. For instance, in the previous example, the start of the range was at the start of the diskette. That is, the range started at sector zero. What if, though, we wanted the next fifty sectors? Well, there are three ways we could do it:
"100:149" or "START+100:START+49" or "START+100:+50"
Also note that in the last form, the final length of "+50" was applied after the "+100" has been applied to "START".
We can use negative lengths in a similar manner. When the length is negative, then that sector number that has an absolute part is deemed to be the ending sector of the range. Another example: what if we wanted the last hundred sectors of the floppy diskette? Take a look at this:
"2780:2879" or "END-99:END" or "-100:END"
Note, in that last form, by the way, that "END:-100" won't work because, when the length is applied to the "end of range" sector number, it'll end up being smaller than the "start of range" sector number and CDU will not allow that.
For "completeness", as it were, let's say that we want the fifty sectors before those in the last example. Consider these:
"2730:2779" or "END-149:END-100" or "-50:END-100"
As the required block gets more difficult to calculate by hand, of course, the more benefit these is in using offsets and length rather than merely calculating absolute sector numbers. (In fact, some of the above examples were written sometime ago. While validating some new ones we'd added, we discovered that we'd got one of the hand-calculated ones wrong - now d'you see how useful relative sector numbers and length are?)
Now for a final couple of examples of CDU "idiom".
If you wanted only the first sector of a stream, one might see the use of ":+1". The start absolute sector number will default to START giving "START:+1". Since the end number has no absolute part but it does have a relative part, the relative part is tread as a length (in this case, a single sector) and the resulting block is deemed to begin at the start sector. In other words, the resulting range is "START:START+0".
In a similar manner, the last sector of a stream can be specified using "-1:". The expansion of this is as follows: the ending sector number isn't present and so it defaults to END giving a result of "-1:END". The start sector has no absolute part so, once again, we assume it's a length and it's negative and so specifies that the block will finish at the ending sector number. Hence, it's equivalent to "END-0:END".
As we've already seen, using ":-1" will not work because this would expand to "START:-1" and, thence, to "START:START-1". Since, in this case, the start sector is less than the end sector, CDU will not accept the range.
It should be noted that our two idiom examples could also be written as ":START" and "END:", respectively.
Since any low-level disk utility can be so destructive, we have made the CDU default state as "read only" as far as DISK and VOLUME streams are concerned. So far in this tutorial, we have only caused files to be written and we allow that by default since we assume that the operating system is "safe". Let us now try writing to a DISK device.
We will start by "washing" your scratch floppy - this is where you zap the diskette's contents so you'd better make sure that it's an unwanted floppy... The following command will cause all of the floppy to be overwritten with 0xAA bytes except for the first cylinder. (We don't write over that since the BIOS may then refuse to read the diskette at all.)
CDU COPY BYTE 0XAA TO DISK FD0 OFFSET 36
The above command will not work because we have forgotten to set CDU into the "write enable" state. Alter the command-line until it reads:
CDU FWRITE COPY BYTE 0XAA TO DISK FD0 OFFSET 36
Note that we have used OFFSET which is an alternative form of the RANGE clause. Since, in this instance, we don't want to bother specifying an end of range sector number, we let that default to the last sector number value. Therefore, the RANGE clause would look like "RANGE 36" and "OFFSET 36" reads so much better. Note, though, that the two words have an identical effect - as we have already stated, CDU allows synonyms for many of its "reserved" words.
Assuming that the wash went OK, have a look at the first couple of cylinders on the floppy. (As before, you'll have to find some way to pause or view CDU's output.)
CDU DETAIL DUMP DISK FD0 RANGE :+72
Notice that, starting at sector number 36, all of each sector is filled with the required 0xAA byte. This pattern should, of course, be continued all of the way until the end of the diskette.
An an aside, try re-copying the diskette data to a second archive:
CDU COPY DISK FD0 TO ARCHIVE FLOPPY2.GZ
Notice the size of our second archive:
FLOPPY IMG 1,474,560 04/03/01 14:58 FLOPPY.IMG FLOPPY2 IMG 1,474,560 04/03/01 14:37 FLOPPY2.IMG FLOPPY GZ 1,060,555 04/03/01 14:40 FLOPPY.GZ PLING TXT 512,000 04/03/01 14:43 PLING.TXT FLOPPY2 GZ 3,241 06/03/01 17:05 FLOPPY2.GZ FLOPPY CDU 154 04/03/01 14:57 FLOPPY.CDU
Since the majority of the diskette's data now consists of the same bit pattern, the compression algorithm of the archiving library has really gone to town - our FLOPPY2.GZ is only a little over three thousand bytes long!
A few words on the differences between WRITE, FWRITE and HWRITE are in order: FWRITE has to be enabled in order for CDU to write to a floppy DISK stream. Likewise, HWRITE controls writes to hard DISK streams. However, plain old WRITE will allow either ; essentially, setting the state of the WRITE switch sets the FWRITE and HWRITE switches to the same state. (It's a historical thing - earlier versions of CDU used WRITE to control access to floppies and hard disks.)
Hence, unless there's a really good reason to do otherwise, if you intend to write to a floppy, use FWRITE and if you intend to write to a hard disk, use HWRITE. That way, if you get the wrong target, CDU may very well refuse to do anything. However, it will not stop you writing to the wrong device of the right type!
Finally, note that writing to VOLUME streams requires that WRITE be used - at present, CDU does not distinguish between floppy and hard drive volumes. (This may change at a later date.)
OK - let's try restoring our floppy to its former state. Let us write the original archive back onto it:
CDU FWRITE COPY ARCHIVE FLOPPY.GZ TO DISK FD0
Once again, convince yourself that it's done the Right Thing:
CDU CONFIRM COMPARE VOLUME A: WITH FILE FLOPPY2.IMG
Notice that we use CONFIRM to inhibit the "d'you want to do this?" question. This could be a good time to make one thing clear: while DISK FD0 is equivalent to VOLUME A:, this is not true for hard drives. Even if, say, your DISK HD0 is partitioned as a large single volume, the first sector of VOLUME C: is not the first sector of the disk. For this reason, I tend to use DISK streams when backing up a device. That way, I know I've got all the disk's data. (This is especially true if AUTOEND is enabled.)
When long disk processes are being carried out, it's not unusual for the perpetrator to take the chance for making a cup of tea, or whatever. Quite often, it'd be nice for the computer to be able to tell you when something has finished. This is the purpose of the REMIND directive.
By default, it's turned off. Hence, when CDU exits, it will only make a single beep depending upon the outcome of the command that ran. If one uses the REMIND directive, however, CDU can be made to "nag" you until you press a key on the keyboard.
CDU CONFIRM BADSCAN DISK FD0 RANGE :+1
This will merely read-check the first sector on your floppy. Now try this instead:
CDU CONFIRM REMIND 5 BADSCAN DISK FD0 RANGE :+1
This will make CDU sound the last beep continuously until you press a key. A good use of this is when you've made a CDU script that backs up your main hard drive. This is likely to take some time so you set your intercom for "baby minder" mode and wander downstairs for a cup of tea. When the backup finishes, whatever the outcome (successful or otherwise), CDU'll keep beeping and you'll be able to hear it over the intercom. Using this facility, you won't have to keep going to check whether the back up has finished yet. (Normally, one knows roughly how long a particular backup should take and tends to only check a little before it is due to finish. The trouble with this approach is that something may have gone wrong as soon as you left the room and the computer may have been sitting there, idle, when you thought it was busy doing a backup.)
We have left a large amount of debugging code in CDU. Partly, this is to aid diagnosis of user problems. (We can just ask the user to repeat whatever they did after invoking "maximum waffle" mode.) It also may help the user to sort out any problems they may be having with CDU - by its very nature, CDU isn't a "load 'n' run" application. In order to enable debug mode, merely turn the DEBUG switch on:
Since the above directive produces loads of output, we've added a "filter" facility. The output produced by "DEBUG ON" prints, as the first item on each line, the function entry point name. One can use the TRACE directive to dictate which functions will be listed and which will be ignored. The strings after the TRACE directive is a wild-card string that will be tested against the current function name before the debug output is actually printed.
To illustrate the effects of the TRACE directive, note the difference in output when you run the following two examples:
CDU DEBUG INFO CDU TRACE *DISK* DEBUG INFO
We have not documented the functions names, by the way; just use DEBUG without invoking TRACE and peruse the output to get an idea of what's in CDU.
CDU can be used interactively. We hadn't written CDU with the intention of making it an interactive program but, in the event, it didn't take much code to make it somewhat easier to use interactively so we decided to leave the code in.
By far the best way to start CDU with a view to using it interactively is to use this:
CDU will prompt you with "CDU>" when it requires input. Type "INFO" and a carriage-return. (We'll assume, now, that you have the sense to place a carriage-return at the end of each line we tell you to type.) CDU should display the same information as it would if you had used "CDU INFO" on the command-line.
Note, by the way, that one directive is very useful when using CDU interactively: the PROBE directive makes CDU re-detect the disk drives. Consider this next dialogue:
CDU> info HD0: 1547 MB HD1: 6173 MB FD0: 1440 KB CDU> del hd0 CDU> info HD1: 6173 MB FD0: 1440 KB CDU> add hd0 CDU> info HD0: ? HD1: 6173 MB FD0: 1440 KB
Note that although CDU had "deleted" (that is, hidden) drive HD0 after we had asked it to, when the ADD was done, the following INFO obviously didn't display the correct information again. This was because when the drive was added back into the drive list, the properties for that drive were added back in their default state. However, we can overcome that like this:
CDU> info HD0: ? HD1: 6173 MB FD0: 1440 KB CDU> probe CDU> info HD0: 1547 MB HD1: 6173 MB FD0: 1440 KB
Using PROBE forces CDU to take another look at the set of existing disk drives.
Try this - the typo is deliberate, by the way:
CDU> dunp disk fd0 ERROR: Command name expected
One of the differences between interactive CDU and command-line CDU is that the former will not abort when an error is detected. Hence, in the above example, the misspelt DUMP invocation does not result in program termination.
So, given that CDU won't exit when an error is detected, how do we terminate an interactive CDU session? We use the EXIT word:
CDU> exit [WIN95] C:\Z\z>
It is worth re-stating that CDU was not primarily intended as an interactive program. Where there is any conflict between the requirements of the two modes, we will lean towards favouring non-interactive use.
For further details, see the INTERACTIVE switch entry in the reference manual.
You can define "macros" much in the same way as in many programming languages. For example:
DEFINE FILENAME "C:\WORK\C\TC\CDU\Z.IMG"
After this macro definition, you could use something like:
COPY DISK HD0 TO FILE %FILENAME%
Note that all text processed by CDU is scanned for the macro expansion characters. (These are the percent signs in the above example.) Text between these characters are tested for environment variable names and CDU text macro names in that order. If either is found, then the corresponding text is substituted. Hence, one can try this:
CDU SAY "The system path is '%%PROMPT%%'"
This will display your system's PROMPT environment variable. However, there are one or two caveats that apply when using macro expansion and the SAY directive. For more information, take a look at the section on the VCHAR word in the reference manual.
Currently, CDU uses two "track" buffers for handling the data stream and, currently, these are allocated dynamically. However, this area is, at present, giving trouble possibly concerned with segment limitations and the like.
One can, using the BUFSIZ keyword, alter the size of the the buffers allocated. These buffers, by the way, are only allocated when a command (as opposed to a directive) is executed. In fact, it is done each time the disk identification is done.
Note that the buffer sizes are, also "at present", specified in multiples of the IBM standard sector size - viz 512 bytes. Thus, if you wanted CDU to allocate a couple of two KB (2048 byte) buffers, use:
By the way, when errors are detected, CDU may complain that the BUFSIZ parameter is greater than one. The problem with any BUFSIZ greater than one is that the reported location of the error really refers to the first sector of the block that contains the error. Hence, to fully locate a bad sector, BUFSIZ must be set to one. This is exactly what is done by the BADSCAN command.
If one wants to do a "read check" a data stream, there's more than one way of doing this. The first way is to use the operating system's "byte bucket". Under MSdos, and derivatives, this is the file called "NUL". Hence, this would read-test the first hard drive:
CDU CONFIRM COPY DISK HD0 TO FILE NUL
Alternatively, one could do a SEARCH for a string that is unlikely to be found:
CDU CONFIRM SEARCH DISK HD0 FOR GHGHGHJGHJKGHGHJG
One could also invoke the DUMP word after having left DETAIL in its default OFF state:
CDU CONFIRM DUMP DISK HD0
Finally, the "official" way is to use the BADSCAN word. This is similar to the DUMP word except that:
It doesn't allow you to display a sector's contents as it is read.
It will not stop when it detects a read error.
There is no "comfort" displayed at all because:
The BUFSIZ is automatically set to "one" so that sector numbers are reported properly.
CDU CONFIRM BADSCAN DISK HD0
Note that using BADSCAN on a hard disk is, perhaps, three times slower than using DUMP because of the automatic alteration of the BUFSIZ parameter. Hence, if you merely want to know if the stream is readable, use the DUMP word. If you want to know how many sectors are unreadable or you want an accurate list of their sector numbers, use BADSCAN.
Perhaps the fastest way would be to use a combination of techniques: use a plain old DUMP and, if it failed, use the reported sector number in the OFFSET clause of a BADSCAN command. However, doing this means that you'd have to "babysit" your computer because you wouldn't know when the first read failure (if any) is going to occur.
The most convenient, assuming time isn't pressing, would be to use a combination of the BADSCAN command with a REMIND clause and redirect the output to a file:
CDU CONFIRM NAG 5 BADSCAN DISK HD0 > BADSCAN.TXT
Since this will, at program termination, provide you with a complete list of bad sectors and it does not need babysitting, while it's running, you can have a cup of tea or just get on with something else.