wednesday, 23 may 2007

posted at 15:48
tags:

As noted, I've started hacking on DOS. The first thing on my list is to make it use struct FileLock correctly.

AmigaOS has two types for referring to some kind of on-disk object - struct FileLock, which can reference any type of object (file or directory) and struct FileHandle, which is only used for files, but contains extra information such as a buffer and current position, allowing I/O. Internally it contains a lock to the underlying file as well. For the most part, a filesystem handler only operates on locks, leaving handles to dos.library. (There's a couple of minor exceptions where handles are manipulated by the handler, but its not really of any consequence and so I'm not going any more detail).

When AROS was given its own filesystem API, it also did away with locks as well, using handles for everything. The main functions of the lock - providing a pointer to the handler, a pointer to the underlying file context, and the current access mode - were all added to struct FileHandle, reusing undefined DOS-private fields (fh_Func1, fh_Func2 and fh_Func3). Since the pointers accepted and returned by DOS functions are opaque BPTRs, its not actually an issue for most programs, and so life has continued happily for the past ten-odd years.

Where this system falls down is with the DOS packet functions SendPkt() and WaitPkt() (and indirectly, AbortPkt(), DoPkt() and ReplyPkt()). The problem is simple: under AmigaOS these functions don't deal with locks or handles, but with the message port the filesystem handler uses to receive packets on. That handler is usually obtained by using BADDR() on a BPTR returned by Lock() to get a struct FileLock, and then getting the port from its fl_Task.

This used to be completely impossible, as until my recent work with packet.handler struct FileLock didn't even exist on AROS, so your code wouldn't even compile. Now it does, but if you try to fish fl_Task out of a "lock" you end up with some random stuff that patently isn't a port, and so sending to it just won't work. Of course, AROS filesystems don't take packets and don't use ports anyway, which is why SendPkt() and ReplyPkt() try to do packet conversion (which doesn't really work), but some programs also like to send their own packets. If anything tries to send to the "port" obtained from the filehandle its likely the system will crash (that position in the struct is held by fh_Buf, which is the I/O buffer.

One of the other issues here is that even if locks are used, AROS filesystems don't use ports, so even if we did use struct FileLock properly the fl_Task won't be anything useful, unless populated with a port owned by packet.handler that can do packet->IOFS conversion.

The goal to remove IOFS has come from a few things. Its not adding any real value to have it, we have a handful of minor devices that use it directly (CDVDFS and SFS are both packet-based handlers with IOFS wrappers that coule be easily removed), and source compatibility isn't there. Replacing it however is a big job, so we're taking an incremental approach. The initial goal is to support both IOFS and packets natively inside DOS. The first step is to bring struct FileLock back to life, which I started on yesterday and is nearly done.

To do that these structures have been updated such that struct FileHandle no longer holds stuff to reference the filesystem, but instead contains a pointer to a struct FileLock which does have this information. The lock is held in fh_Arg1, as was always the way under AmigaOS.

With normal packet handlers, the lock contains two fields to reference the file: fl_Task which is a message port for the handler, and fl_Key which is some random data set by the handler that it can use to find the file on disk. IOFS handlers had a similar pair of fields held in the filehandle - fh_Device which is a pointer to the Exec device of the handler, and fh_Unit which is the opaque data. Pavel Fedin, in a stroke of genius that now seems completely obvious, suggested simply storing the IOFS device and unit into fl_Task and fl_Key as a fast and cheap way of bringing FileLock back.

This is fine if you only have one type of handler, but we have two, and so need to be able to tell the difference. Pavel came to the rescue here too - give struct FileLock and extra field, fl_Device. Put the device pointer there, and the unit pointer in fl_Key, and use fl_Task as a flag to determine the type - when its NULL, its a IOFS handler fl_Device is valid, if its NULL then its a packet handler and fl_Device has no meaning (and in fact shouldn't even be accessed, as locks are then entirely allocated by the handler which may have something different here (like struct ExtFileLock in fat.handler) or nothing at all if the handler is using original AmigaOS sizings).

So far I've set up these definitions and reworked the DOS internals (and a few other bits of code around the place that were using IOFS directly) to match. Its mostly a case of renaming FileHandle to FileLock, bouncing through fh_Arg1 to get the device and unit pointers, and of course allocating and deallocating lock structures in the right place. There's a small build issue to figure out (wiating for a reply from aros-dev) but once thats fine AROS should at least start, and then I can begin tracking down the several million edge cases that will probably arise from this.

If you want to see the code, ask me - not checking it in yet because I have no desire to break the tree.