Here's the text of my 1989 unpublished article on relative file bugs. I'll post the program listings in a separate news article. If Cox blocks this because of the size, or there are other problems, I'll do it some other way. It's in ANSI. BEYOND RAID ----------------------------------- New REL bug insights, and a DOS fix ----------------------------------- by George Hug The management of relative files under Commodore DOS continues to be a source of frustration. Virulent REL bugs still inhabit the latest drive ROMs - the 1541-II, the 1571 upgrade ROMs (rev. 5), and DOS 3.1 in the 128D. Even the 1581, which claims immunity, retains one infectious strain. Previous descriptions of the feeding habits of these bugs, along with the "Raid" bug sprays offered as protection, need further refinement so as to eliminate the last of those mysterious crashes and corrupted files. Presented here is new information on the specific causes and effects of the bugs, together with an updated avoidance algorithm. For those with EPROM burners, there are also changes to the 1541/71/81 ROMs which will exterminate the bugs. It's All Relative The data section of a relative file looks identical to a sequential file. The first two bytes of each block point to the next block, and the remaining 254 bytes contain data. However, the file as a whole is logically divided into records, all of which are fixed at the same length when the file is created. One may access (i.e. - read or write) records at random by first pointing to each selected record via the command channel. One may also access records in strict sequence, without pointing, using successive reads or writes. Since a record may cross the boundary between two disk sectors, DOS assigns two of its RAM buffers to service the data sectors of a relative file. If the proper sectors are in the buffers, DOS has access to both portions of a "split" record. A record may be only partially filled with data, in which case the rest of that record will be filled with nulls. DOS assumes that the last non-null byte in a record is the last genuine data byte. If asked to read past that point, it will jump directly to the first byte of the next record. Similarly, if a write does not fill a record, DOS automatically provides the null fill. With small record lengths, a single disk sector may contain a large number of records. To avoid repeated re-writes of the same sector, DOS waits until it is about to leave a buffer to update the hard copy on disk. Prior to that time, all accesses to records within that buffer, whether reads or writes, involve only the buffer. If even one such access is a write, the buffer is flagged as "dirty", which means that its contents now differ from the hard copy. A dirty buffer is written to disk (a) as part of a point to a record not in that buffer, (b) in the process of accessing a split record, or (c) when the file is closed. The Major Bug The most destructive relative file bug can corrupt several records at a time. Consider any three consecutive sectors - A, B, and C - of a relative file. Suppose that the split record spanning sectors A and B is accessed when the sector A buffer is dirty, possibly followed by any number of accesses to records lying wholly within sector B. If one then points and writes to a record which begins in sector C, the new data will not go to sector C, but rather to sector A, thus corrupting the file. If the access to the sector C record is a read, DOS will read from sector A instead of sector C. The physical byte position within sector A where DOS begins to read or write is the position where it should begin within sector C. In sector A, however, that point may fall in the middle of a record. So a write to C (including null fill) will tend to corrupt three records - two that are partially overwritten in sector A, and the record in sector C which never received the new data. Similarly, a read may begin in the middle of data or somewhere in the null fill area. This "major" bug cannot occur in strict sequential access of the file. An access to the record which spans sectors B and C will cancel the bug previously set up with respect to sector C (but if B is dirty, it will set up a new one for sector D). Therefore, the B/C split record must be skipped over, via a point command, for the bug to strike in sector C. Accessing a record which ends exactly at the end of sector B has the same effect as accessing a B/C split record. DOS treats all such records as split even though they do not physically span two sectors. To be precise then, a record is "split" if that record and the one following it begin in different sectors. Thus if the record length is 127, all even-numbered records are split records. The Minor Bug Somewhat less destructive is the bug which points to the wrong byte as the end of data in a split record. To determine where the data ends and the null fill begins, DOS starts at the very end of the record and searches backward for the first non-null byte. When the record is split, DOS must begin its search in the sector buffer which contains the end of the record. The "minor" bug occurs when DOS looks at the wrong sector to begin that search. The minor bug is set up in the same manner as the major bug - an access of the A/B split record when A is dirty, possibly followed by any number of accesses to records wholly within sector B. If one then reads the B/C split record, DOS will go to sector A, rather than C, to begin its backward search for the end of data. Oddly enough, the actual read of the data in the forward direction correctly fetches data from sector C, but the end-of-data pointer, which determines how much of that data will be forthcoming, has already been set incorrectly. As a result, the read may be truncated, or have extra nulls tacked onto the end. The minor bug can occur during strict sequential access of the file, provided at least one of those accesses is a write. A Pound of Prevention Certain patterns of relative file access are immune to the bugs. If, for example, all accesses are reads, regardless of order, then the bugs will not occur because no buffer will ever become dirty. If all accesses are strictly sequential writes, then the major bug will not occur because the access is sequential, and the minor bug is irrelevant because it only affects reads. Finally, if there are no nulls in a file, either as data or as fill, that file will be immune to the minor bug. Absent such immunity, special procedures must be adopted to prevent the bugs from striking. Both bugs require, as a setup, the accessing of an A/B split record when the sector A buffer is dirty. That access may be a write, which automatically makes the A buffer dirty, or it may be a read if the A buffer is dirty from an earlier write. Fortunately, the bug setup can be cancelled by pointing back to the split record after accessing it. That would call for a point/read/point or a point/write/point sequence for all split records. A record is split if ((r*s)mod254)