TweetFollow Us on Twitter

Volume Manager
Volume Number:2
Issue Number:4
Column Tag:Advanced Mac'ing

Nested Volume Manager DA
in Megamax C

By Mike Schuster, Adobe Systems Engineer, MacTutor Contributing Editor

A Nested Volume Manager

Most third party Macintosh hard disk suppliers provide mount manager applications that allow you to partition their disks into several logical volumes, all sharing the same storage medium. These schemes were implemented primarily as work arounds for several limitations in the original Macintosh File System (MFS). For large volumes, MFS suffers from slow file directory searches and poor volume space management. Apple's solution to these problems is the new Hierarchical File System (HFS), introduced in the Fall 1985 on its own 20 megabyte hard disk HD20 product, and again in January 1986 on its 800k 3.5" disk drive in the Macintosh Plus.

Since HFS provides an better mechanism for handling large storage devices, Apple saw no need to implement a volume partitioning scheme on its HD20. The HD20 is shipped pre-formatted as a single large volume. Other suppliers have done the same, such as SuperMac Technology for their DataFrame 20 megabyte external SCSI hard disk.

In this month's article, I show you how you can partition your HD20 or other third party HFS hard disk into independent, variable sized nested volumes. Each of these volumes is built by creating a host file whose contents look like a properly formatted MFS or HFS volume. Once the host file is created and properly initialized, its nested volume is available on the Finder's desktop and in application's open and save MiniFinder dialogs just like any other ordinary volume.

Why Use Nested Volumes?

I've found the "nested volume in a host file" capability to be useful in a variety of ways. You can port MFS-only systems, such as Apple's MDS and third party development systems, onto an HD20 or other HFS disk simply by creating one or more MFS nested volumes. (Of course, as development system suppliers release HFS upgrades, this capability will be less useful. As of March 1986, the only shipping HFS C language systems I'm aware of are Consulair Mac C and Megamax C). Another potential use is as a simple volume level security system. You might want to associated a password with each nested volume, in a manner similar to that done by General Computer on its Hyperdrive product. A final use is purely academic. The manager provides a good excuse for an inside look at the organization of the Macintosh File and Disk Driver operating system components. An understanding of these components is especially useful now with the new opportunities available in the Macintosh Plus - SCSI based peripheral market.

File System and Disk Driver Organization

The Macintosh File System is the part of the Operating System that manages the communication between an application and files on a block oriented storage medium, such as a hard disk drive. Each storage medium is formatted into one or more volumes, each of which contains some descriptive information along with a set of files.

Fig. 1: File System and Disk Driver Organization

Before an application can access the files on a volume, the volume must be mounted. The File System mounts a volume by reading the volume's descriptive information into memory and using it to build a new volume control block, which is inserted into the volume control block queue. The File System performs this mounting process automatically both at boot time and whenever a 3.5" disk is inserted into a drive. Usually, non-ejectable volumes, such as those on hard disks, are always mounted. However, an application may direct the File System to mount or unmount any volume at any time. The third party mount managers mentioned above perform just these functions.

In order to mount a volume, as well as to access its files, the File System must first determine which storage medium contains the volume. (Typically, the medium is a disk drive, but it may be a section of memory allocated to a RAM disk.) Furthermore, once the medium is located, the File System must be able to read and write any accessible block on that medium. The File System does not do these things itself, instead it relies on the drive queue and on a disk driver. The drive queue contains one drive queue element for each available storage medium. Each drive queue element contains, among other things, a drive number uniquely identifying the medium and a disk driver number uniquely identifying the disk driver program that controls that medium. The drive number of the drive containing a volume is also placed in the volume's control block. Hence, given a reference to a volume, the File System can easily find its corresponding drive queue element and the identity of its disk driver.

Once the File Manager has determined the drive number and the controlling disk driver of the medium containing the volume, it calls the disk driver to transfer blocks of 512 characters to and from the device. The disk driver takes the positioning information and data from the File System and converts them into commands for the disk drive's hardware controller and interface electronics. Any resulting data and completion codes are returned to the File System when the transfer operation completes. The biggest advantage of this scheme is that new RAM based disk drivers can be added to the system to support new storage media without any modification to the fixed, ROM based File System.

Fig. 2: Creating an 800k HFS Nested Volume Named Editor

Normally, the drive queue contains one element for each physical storage medium connected to the Macintosh, including the internal and external drives, RAM disks, and hard disks, etc. In the case where several logical volumes share the same storage medium, convention dictates that one element be placed in the drive queue for each logical volume. This convention guarentees that each mounted volume has associated with it a unqiue drive number. Although the drive queue elements of these volumes have differing drive numbers, they all share the same disk driver number. Hence, given a drive number of a particular mounted volume, a disk driver should be able to determine both the identity of the storage medium containing the volume, as well as the location of the volume's data within that medium. In the case of a volume nested within a host file, this information consists of the drive number of the volume that contains the nested volume's host file, as well as the location within the volume of the host file's contents.

Nested Volumes

Two pieces of software are required to implement nested volumes:

• A mount manager, and

• A disk driver.

The mount manager, which I implemented as a desk accessory named Nest, presents a user interface that allows you to create, mount and unmount nested volumes. The desk accessory is responsible for establishing the necessary drive queue elements and other data structures that allow File System and the disk driver to correctly locate and transfer information to and from the volume's host file. For those of you familiar with General Computer's Hyperdrive product, Nest incorporates the functions of both their Drawers desk accessory and their Manager application.

The second component of the system, the disk driver .Nest, is responsible for redirecting and translating the File System's block level read and write requests to and from the nested volume into reads and writes to and from the contents of the volume's host file. The driver is shared by all of the mounted nested volumes, and uses information defined by the desk accessory to associate each volume with the contents of its host file.

When creating a volume, the desk accessory displays a standard save dialog allowing you to name the volume, as well as specify its size and whether it should be initialized as an MFS or HFS volume. In the example below, I show the creation of the 800K HFS nested volume Editor. The host file containing the new volume, which is also named Editor, will be saved in the Nest folder of the volume DataFrame. (Normally, the name of a nested volume is the same as the name of its host file, however, you may later change the name of either one in the Finder with no ill effects other than your own confusion.) I implemented the dialog's extra buttons and text items in the standard way using the standard file packages documented hooks.

The desk accessory creates the nested volume's host file by calling the routine newnest. Its arguments are the file name and volume reference number returned by the standard file save dialog, along with the desired size of the volume in blocks of 512 characters. Newnest first uses the File Manager's routine create to create the file, and then the new HFS routine alloccontig to allocate the desired number of blocks in one contiguous area. Alloccontig is identical to the original File System routine allocate, except that if there isn't enough contiguous, empty space on the volume to satisfy the allocation request, alloccontig will do nothing and will return dskfulerr as its function result. By allocating the space in one contiguous area, we greatly simplify the implementation of the disk driver.

If the allocation succeeds, the routine seteof is called to set the file's logical end-of-file equal to the desired size of the volume. Finally, newnest closes the file and flushes the volume on which the file resides. If the allocation request cannot be satisfied, newnest deletes the file before returning.

int newnest(fname, vrefnum, blocks)
 char *fname;
 int vrefnum;
 int blocks;
 long count;
 int frefnum;
 int result;

 /* create and open file */
 fsdelete(fname, vrefnum);
 create(fname, vrefnum, NESTCREATOR, NESTTYPE);
 fsopen(fname, vrefnum, &frefnum);

 /* allocate contiguous area, save result */
 count = (long) blocks * 512;
 result = alloccontig(frefnum, &count);

 /* set logical end-of-file */
 count = (long) blocks * 512;
 seteof(frefnum, count);

 /* close, flush, delete if allocation failed, and */
 /* return result */
 if (result)
 fsdelete(fname, vrefnum);
 flushvol(0l, vrefnum);
 return result;

When mounting a volume, the desk accessory displays a standard open dialog allowing you to select one of the previously created host files. In the example below, I show the selection of the nested volume contained in the host file Editor from the Nest folder of DataFrame.

In this dialog, the button labeled Mount actually switches between the three titles Mount, Unmount and Open. It is Mount if the selected item is a closed host file whose volume is not mounted. It is Unmount if the selected item is an open host file whose volume is mounted. Finally, it is Open if the selected item is a folder. The New button brings up the save dialog discussed above.

Fig. 3: Desk Accessory file selection

The desk accessory mounts the volume contained within a host file by calling the routine mountnest. Its arguments are the file name and volume reference number returned by the standard file open dialog, along with the a flag that indicates whether or not the volume should be initialized.

After calling newnest to create a new host file and nested volume, the desk accessory assumes you want to immediately mount the volume and hence calls mountnest directly, rather than displaying the open dialog. In this case, the volume needs to be initialized and hence the initialize flag is set true. Otherwise, the desk accessory assumes that the volume is already initialized and sets the initialize flag false.

Mountnest begins by opening the file with fsopen and determining the size of the volume with geteof. Then a new drive queue element is allocated in the system heap. In addition to the standard fields, the drive queue elements contains a set of custom fields shown in the definition below. These fields are used by the disk driver when it translates block level data transfers into operations on the host file.

/* customized drive queue element */
typedef struct _drvqel
 /* standard fields */
 char dqwriteprot; /* write protected */
 char dqdiskinplace; /* = 8 for non-eject media */
 char dqinstalled; /* unused */
 char dqsides;   /* unused */
 struct _drvqel *qlink; /* next drive que element */
 int qtype; /* unused */
 int dqdrive;    /* drive number */
 int dqrefnum;   /* disk driver ref number */
 int dqfsid;/* = 0 for Mac File System */
 int dqdrvsize;  /* size of volume in blocks */
 /* custom fields */
 int dqfrefnum;  /* host file reference number */
 int dqvrefnum;  /* nested volume reference number */
 int dqdqrefnum; /* host disk driver ref number */
 int dqdqdrive;  /* host volume drive number */
 long dqvstart;  /* host byte position of first block */
 long dqvmark;   /* host byte position current block */
 long dqvend;    /* host byte position of last block */
 } drvqel, *drvqelptr;

Mountnest then initializes the standard fields of this drive queue element. The field dqdiskinplace is set to the standard value of 8 to indicate that the volume is non-ejectable. This causes the Finder's Eject command and the MiniFinder's Eject button to be dimmed when the nested volume is selected.

Mountnest then finds an unused drive number by calling the routine finddrvnum and saves the returned value in the field dqdrive. Finddrvnum looks at all of the existing drive queue elements for an available number.

Next, the disk driver reference number of the disk driver .Nest is saved in the dqrefnum field. The desk accessory obtains this value when it opens the .Nest with the call

 opendriver(".Nest", &dqrefnum);

After initializing the standard fields, mountnest saves the file reference number of the volume's host file in the field dqfrefnum, the disk driver reference number of the volume containing the host file in the field dqdqrefnum, and the drive number of the volume containing the host file in the field dqdqdrive. The first of these three fields is used to close the host file when the nested volume is unmounted. The second and third of these fields, which are returned by the call to the routine infonest, are used by the disk driver .Nest. They allow the disk driver to translate a block level data transfer request to the nested volume into a block level request to the disk driver of the volume containing the host file.

Mountnest then determines dqvstart, the byte position of the first byte in the first block of the nested volume's host file. The value of this field is the key to the implementation of nested volumes. Whenever the File System requests the first block of a nested volume, the disk driver must return the first block of the host file. Similarly, when the second block of the volume is requested, the second block of the host file must be returned, and so on. Since the host file's blocks are allocated in one contiguous section on the host volume, the disk driver only needs to know the byte position of the host file's first block. All other positioning can be implemented by adding the appropriate offset to this base location. [Remember, we allocated space for this nested volume as a continguous section to simplify this operation.]

This byte position is determined by three values:

• the number of the first allocation block of the host file,

• the size of an allocation block on the host volume, and

• the block number of the host volume's first allocation block.

The first of these values is contained in the fcbextrec[0] field of the host file's file control block, whose structure is outlined below. The File System maintains one file control block for each open file. Given a file reference number of an open file, the define FCBPTR returns a pointer to its file control block. The second and third of the above values are obtained by the File System's routine pbgetvolinfo. Hence, the byte position of the host file's first block equals the product of the first two of these values added to 512 times the value of the third.

The file control block's fcbextrec field contains the first three file extent descriptors of the open file. A file extent is a series of contiguous allocation blocks. Ideally, a file would be stored in a single extent, as our host files are, but in general, the contents of a file are stored in more than one extent in different places on the storage medium. Each extent is identified by an extent descriptor, which specifies the number of the first allocation block of the extent and the length of the extent in blocks. Space on a volume is allocated by the File System in units of allocation blocks, each equal to some number of 512 character blocks. Hence, fcbextrec[0] and fcbextrec[1] form the extent descriptor of the file's first extent, where the first field is an allocation block number and the second a block length. In a similar manner, fcbextrec[2] and fcbextrec[3] describe the file's second extent; fcbextrec[4] and fcbextrec[5] describe the file's third extent, if they exist. Since all host file's are contiguous, we only need to be concerned with the value of fcbextrec[0].

/* file control block */
typedef struct
 long fcbFlNum;  /* file number */
 char fcbMdRByt; /* flags */
 char fcbTypByt; /* version number */
 int fcbSBlk;    /* first allocation block of file */
 long fcbEOF;    /* logical end-of-file */
 long fcbPLen;   /* physical end-of-file */
 long fcbCrPs;   /* current position */
 ptr fcbVPtr;    /* pointer to volume control block */
 ptr fcbBfAdr;   /* pointer to access path buffer */
 int fcbFlPos;   /* used internally */
 long fcbclmpsiz;/* file clump size */
 ptr fcbbtcbptr; /* pointer to B*-tree control block */
 int fcbextrec[6]; /* first three file extents */
 long fcbftype;  /* file's finder type bytes */
 long fcbcatpos; /* used internally */
 long fcbdirid;  /* file's parent id */
 char fcbcname[32];/* name of open file */
 } fcb, *fcbptr;
#define FCBPTR(refnum) \
 ((fcbptr) ((*(char **) 0x34e) + (refnum)))

Once the value of dqvstart is computed, mountnest initializes the fields dqvmark and dqvend. These fields are used by the disk driver when translating positioning information, and correspond to the byte position of the next block to be read or written and the position of the byte just beyond the end of the host file, respectively.

Mountnest then inserts the initialized drive queue element into the drive queue by calling the routine enqueue. Finally, mountnest call the File System routine mountvol to mount the volume, unless the volume must be first initialized, in which case mountnest calls zeronest. In either case, the new drive queue element is ready to provide the File System with the information it needs to locate the disk driver responsible for data transfer operations.

int mountnest(fname, vrefnum, zero)
 char *fname;
 int vrefnum;
 int zero;
 int frefnum;
 int blocks;
 long alblksize;
 int alblstart;
 drvqelptr thedrvqel;

 /* open file and determine size of volume */
 fsopen(fname, vrefnum, &frefnum);
 geteof(frefnum, &alblksize);
 blocks = alblksize / 512;
 /* allocate standard drive queue element */
 thedrvqel = (drvqelptr) newsysptr((long) sizeof(drvqel));
 thedrvqel->dqdiskinplace = 8;
 thedrvqel->dqdrive = finddrvnum(1024);
 thedrvqel->dqrefnum = dqrefnum;
 thedrvqel->dqdrvsize = blocks;
 /* customize drive queue element */
 thedrvqel->dqfrefnum = frefnum;
 infonest(0l, vrefnum, &thedrvqel->dqdqrefnum, 
 &thedrvqel->dqdqdrive, &alblksize, &alblstart);
 thedrvqel->dqvstart = FCBPTR(frefnum)->fcbextrec[0] * 
 alblksize + alblstart * 512l;
 thedrvqel->dqvmark = thedrvqel->dqvstart;
 thedrvqel->dqvend = 
 thedrvqel->dqvstart + (long) blocks * 512;
 /* enqueue element on drive queue */
 enqueue(&thedrvqel->qlink, getdrvqhdr());
 /* optionally zero, then mount volume */
 if (zero)
 return zeronest(thedrvqel, fname, putformat);
 return mountvol(thedrvqel->dqdrive, 

When unmounting a nested volume, the desk accessory displays a standard open dialog allowing you to select one of the currently open host files. The desk accessory unmounts the volume contained within the selected host file by calling the routine unmountnest. Its arguments are the file name and volume reference number of the selected host file, along with its file reference number. This latter number is discovered with a call to the File System routine pbgetfileinfo, which returns, among other things, a file reference number if the file is open.

Unmountnest first calls finddrvqel, which searches the drive queue for the element corresponding to the selected host file's nested volume. Finddrvqel looks for an element with the appropriate disk driver reference number in the dqrefnum field as well as a matching file reference number in dqfrefnum. Then the File System routine unmountvol is called to unmount the nested volume. Finally, the host file is closed and the drive queue element is removed from the drive queue.

int unmountnest(fname, vrefnum, frefnum)
 char *fname;
 int vrefnum;
 int frefnum;
 drvqelptr thedrvqel;
 thedrvqel = finddrvqel(dqrefnum, frefnum);
 unmountvol(0l, thedrvqel->dqvrefnum);
 dequeue(&thedrvqel->qlink, getdrvqhdr());
 flushvol(0l, vrefnum);
 return 0;

Initializing Nested Volumes

When a host file is created, its contents must be initialized to that of a properly formatted volume before it can be mounted. The routine zeronest performs this function. Its arguments are a pointer to the volume's drive queue element, the volume's name, the a flag indicating whether the volume should be formated as an HFS or MFS volume. If HFS formatting is desired, zeronest simply calls the disk initialization package routine dizero. Its arguments are the volume's drive number and name. Dizero will first format and then mount the volume. (In fact, dizero will HFS format only those volumes that are larger than 400k; smaller volumes are forced to be MFS.) It is important to realize that dizero will call on the disk driver directly to write formatting information to the volume. This is ok since the desk accessory has already opened the disk driver and mountnest has inserted a proper drive queue element into the drive queue.

If a MFS volume is desired, zeronest writes the appropriate information described in the "Data Organization on Volumes" section of the "The File Manager" chapter of Inside Macintosh. First, the system startup, master directory, block map, and file directory blocks are zeroed with direct write calls to the disk driver. Then the volume information area in the master directory block is written. The allocation block size is chosen to minimize the size of the master directory's block map, and then the number of allocation blocks on the volume is determined. The constant DRDIRST defines the block number of the first block in the file directory. DRBLLEN defines the number of blocks in the file directory. Finally, the routine mountvol is called to mount the volume.

int zeronest(thedrvqel, fname, format)
 drvqelptr thedrvqel;
 char *fname;
 int format;
 paramblockrec pb;
 int block;
 volinfoptr thevolinfo;
 long size;
 if (format == puthfs)
 dizero(thedrvqel->dqdrive, fname);
 return getvinfo(thedrvqel->dqdrive, fname, 
 &thedrvqel->dqvrefnum, &size);
 pb.paramunion.ioparam.iorefnum = 
 pb.iovrefnum = thedrvqel->dqdrive;
 pb.paramunion.ioparam.ioposmode = fsfromstart;
 pb.paramunion.ioparam.ioposoffset = 0l;
 pb.paramunion.ioparam.ioreqcount = 512l;
 pb.paramunion.ioparam.iobuffer = 
 for (block = DRDIRST + DRBLLEN; block; block--, pb.paramunion.ioparam.ioposoffset 
+= 512l)
 pbwrite(&pb, 0);
 thevolinfo = (volinfoptr) 
 thevolinfo->drsigword = 0xd2d7;
 thevolinfo->drlsbkup = thevolinfo->drcrdate;
 thevolinfo->dratrb = 0;
 thevolinfo->drnmfls = 0;
 thevolinfo->drdirst = DRDIRST;
 thevolinfo->drbllen = DRBLLEN;
 thevolinfo->dralblst = thevolinfo->drbllen + 
 thevolinfo->dralblksiz = ((thedrvqel->dqdrvsize - 
 thevolinfo->dralblst) / 400) * 1024l;
 if (thevolinfo->dralblksiz < 1024l)
 thevolinfo->dralblksiz = 1024l;
 thevolinfo->drclpsiz = thevolinfo->dralblksiz * 8;
 thevolinfo->drfreebks = thevolinfo->drnmalblks = 
 ((thedrvqel->dqdrvsize - thevolinfo->dralblst) * 
 512l) / thevolinfo->dralblksiz;
 thevolinfo->drnxtfnum = 1l;
 strcpy(thevolinfo->drvn, fname);
 pb.paramunion.ioparam.ioposoffset = 1024l;
 pbwrite(&pb, 0);
 return mountvol(thedrvqel->dqdrive, 

Nested Disk Driver

The nested volume disk driver .Nest is built as a standard device driver consisting of five routines drvropen, drvrclose, drvrcontrol, drvrstatus and drvrprime. Parameters to these routines are passed in standard low level parameter blocks, along with a pointer to the driver's device control entry, which are both described in detail in the File Manager and Device Driver chapters of Inside Macintosh.

Each of these five routines returns a long result, which is rather unusual. The lower 16-bits of this result is the result code returned to the Device Manager. The high order bit of the upper 16-bits is used distinguished completed I/O requests from those that are still pending. In this version of the disk driver, all of the requests are completed synchronously, so the high order bit is aways set, except for the special case of a killio control call.

The routines drvropen and drvrclose are especially simple. They simply return the noerr result code.

Drvrstatus is a bit more complex. It redirects any status calls to the disk driver of the drive containing the host file's volume. The procedure is quite simple. First the argument parameter block is copied into a local variable. Then the copy's iorefnum field is changed to the disk driver number of the host file's volume, and its iovrefnum is changed to the drive number of the host file's volume. This information is found by searching the drive queue for the nested volume's element and uses the information saved in that element by the desk accessory when the volume was mounted. Next, the redirected request is executed with a call to the routine pbstatus. Finally, the results of the pbstatus call are copied back into the proper place in the orginal, argument parameter block.

This scheme of redirecting the requests to the disk driver of the host file's volume is used in drvrcontrol and drvrprime, as well. In drvrcontrol, the calls to format and verify the nested volume, and the call to return icon information are handled as special cases. The calls to format and verify a volume are made by the Finder when its Erase Disk command is used. (The first time I tried the Erase Disk command on a nested volume, I ended up erasing my whole hard disk!) Since we only want the contents of the host file to change, not the volume containing the host file, we are careful not to forward either of these requests. Drvrcontrol simply returns the result code noerr. The third case, when the parameter's cscode field equals icondesccode, is the call used by the Finder to discover the volume's desktop icon and its Get Info information string. This request is redirected, and its result, which consist of a single pointer to an an area of memory containing an icon, an icon mask, and a pascal string are appropriately returned.

Finally, drvrcontrol intercepts the killio call and returns noerr, using the return macro IORESULT rather than IODONE, as required by a warning in the Device Manager chapter in Inside Macintosh.

The heart of the driver is, of course, the drvrprime routine. It implements the actual data transfer operations to and from the nested volume. With the help of the information saved in the drive queue element by the desk accessory, its job is relatively straightforward. First it determines which block of the volume's host file is to be accessed. It does this by checking the positioning information contained in the parameter block's ioposmod and ioposoffset fields. Depending on the value of ioposmode, drvrprime adds the appropriate starting, ending, or current byte position values saved in the drive queue element to the value of ioposoffset. Then the I/O operation is performed by calling the disk driver of the volume containing the host file. Finally, the appropriate information is returned in the argument parameter block, which consists of the actual number of bytes transfered in the ioactcount field, and the new current position, in the ioposoffset field. This latter value is also used the current position saved in the the drive queue element itself.

The rest of the source implements the parameter block copying and drive queue search utility routines initpb and finddrvqel.

 * Nest Disk Driver
 * Copyright (c) 1986 by Mike Schuster
 * All Rights Reserved

#include "drvr.h"
#include "file.h"
#include "nest.h"


extern drvqelptr finddrvqel();
extern drvqelptr initpb();

/* handle open request */
long drvropen(thepb, thedctl)
 paramblkptr thepb;
 dctlentryptr thedctl;
 return noerr;

/* handle close request */
long drvrclose(thepb, thedctl)
 paramblkptr thepb;
 dctlentryptr thedctl;
 return noerr;

/* handle status request */
long drvrstatus(thepb, thedctl)
 paramblkptr thepb;
 dctlentryptr thedctl;
 paramblockrec pb;
 drvqelptr thedrvqel;
 /* initialize parameter block */
 thedrvqel = initpb(&pb, thepb);

 /* pass on all requests to host */
 pbstatus(&pb, 0);
 &thepb->paramunion.cntrlparam.csparam, 22l);
 return IODONE(pb.ioresult);

/* handle control request */
long drvrcontrol(thepb, thedctl)
 paramblkptr thepb;
 dctlentryptr thedctl;
 paramblockrec pb;
 drvqelptr thedrvqel;
 /* initialize parameter block */
 thedrvqel = initpb(&pb, thepb);
 /* handle format code */
 if (pb.paramunion.cntrlparam.cscode == formatcode)
 pb.ioresult = noerr;
 /* handle verify code */
 else if (pb.paramunion.cntrlparam.cscode == verifycode)
 pb.ioresult = noerr;

 else if (pb.paramunion.cntrlparam.cscode == killcode)
 return IORESULT(pb.ioresult);
 /* handle finder's icon description code */
 else if (pb.paramunion.cntrlparam.cscode == 
 pbcontrol(&pb, 0);
 *(ptr *) &thepb->paramunion.cntrlparam.csparam = 
 *(ptr *) &pb.paramunion.cntrlparam.csparam;
 /* pass on all other requests to host */
 pbcontrol(&pb, 0);
 &thepb->paramunion.cntrlparam.csparam, 22l);
 return IODONE(pb.ioresult);

/* handle read and write requests */
long drvrprime(thepb, thedctl)
 paramblkptr thepb;
 dctlentryptr thedctl;
 paramblockrec pb;
 drvqelptr thedrvqel;
 /* initialize parameter block */
 thedrvqel = initpb(&pb, thepb);
 /* remap position relative to start of volume */
 switch (pb.paramunion.ioparam.ioposmode)
 case fsfromstart:
 pb.paramunion.ioparam.ioposoffset += 
 case fsfrommark:
 pb.paramunion.ioparam.ioposoffset += 
 case fsfromleof:
 pb.paramunion.ioparam.ioposoffset += 
 case fsatmark:
 pb.paramunion.ioparam.ioposoffset = 
 pb.paramunion.ioparam.ioposmode = fsfromstart;
 /* perform the request */
 if (pb.iotrap & 0x1)
 pbwrite(&pb, 0);
 pbread(&pb, 0);
 /* update current position */
 thedrvqel->dqvmark = 
 /* return results */
 thepb->paramunion.ioparam.ioactcount = 
 thepb->paramunion.ioparam.ioposoffset = 
 pb.paramunion.ioparam.ioposoffset - 
 return IODONE(pb.ioresult);

/* find drvqel, given driver reference number and drive number */
drvqelptr finddrvqel(dqrefnum, dqdrive)
 int dqrefnum;
 int dqdrive;
 drvqelptr thedrvqel;
 for (thedrvqel = ((qhdrptr) getdrvqhdr())->qhead; 
 thedrvqel && (DRVQELPTR(thedrvqel)->dqrefnum != 
 dqrefnum || DRVQELPTR(thedrvqel)->dqdrive != 
 thedrvqel = DRVQELPTR(thedrvqel)->qlink)
 return thedrvqel ? DRVQELPTR(thedrvqel) : 0l;

/* initialize parameter block */
drvqelptr initpb(pb, thepb)
 paramblkptr pb;
 paramblkptr thepb;
 drvqelptr thedrvqel;
 *pb = *thepb;
 /* find drive queue element */
 thedrvqel = finddrvqel(pb->paramunion.ioparam.iorefnum, 
 /* install driver reference number and drive number */
 pb->paramunion.ioparam.iorefnum = 
 pb->iovrefnum = thedrvqel->dqdqdrive;
 return thedrvqel;

The remaining source listed below shows the glue code that interfaces the C language implementation of the disk driver to the Device Manager. They replace the "acc.h" and "acc.c" files in the Megamax C language development system. The fundamental difference between these files and those provided in the Megamax system is that the IODone routine is called appropriately by checking the high-order bit of the result returned by the C language routines. (I hope development system suppliers will standardize their interfaces so that porting drivers and desk accessories is will become easier. The idea of returning a long result originated with Bill Croft in his SUMACC C language UNIX-based cross development system.)

 * Driver Interface
 * Copyright (c) 1986 by Mike Schuster
 * All Rights Reserved


extern int _drvropen();
extern int _drvrprime();
extern int _drvrcontrol();
extern int _drvrstatus();
extern int _drvrclose();

#define IORESULT(result) \
 ((long) (result) & 0x0000ffffl)
#define IODONE(result) \
 (((long) (result) & 0x0000ffffl) | 0x80000000l)

#define DRVR(F, D, E, M, L, S) \
asm { \
 dc.w   F \
 dc.w   D \
 dc.w   E \
 dc.w   M \
 dc.w   _drvropen+8 \
 dc.w   _drvrprime+10 \
 dc.w   _drvrcontrol+12 \
 dc.w   _drvrstatus+14 \
 dc.w   _drvrclose+16 \
 dc.b   L \
 dc.b   S \

 * Driver Interface
 * Copyright (c) 1986 by Mike Schuster
 * All Rights Reserved

extern int _GLBLSIZE;
#define SAVEA4 694

extern int _drvropen();
extern int _drvrprime();
extern int _drvrcontrol();
extern int _drvrstatus();
extern int _drvrclose();

extern int _opendrvr();
extern int _calldrvr();
extern int _closedrvr();

extern long drvropen();
extern long drvrprime();
extern long drvrcontrol();
extern long drvrstatus();
extern long drvrclose();

 lea    drvropen(PC),A2
 jmp    _opendrvr(PC)
 lea    drvrclose(PC),A2
 jmp    _closedrvr(PC)
 lea    drvrcontrol(PC),A2
 jmp    _calldrvr(PC)
 lea    drvrstatus(PC),A2
 jmp    _calldrvr(PC)
 lea    drvrprime(PC),A2
 jmp    _calldrvr(PC)

 move.l 20(A1),A4; dctlstorage
 move.l (A4),A4  ; globals
 dc.w 0x98fc; suba.w #_GLBLSIZE,A4
 move.l A4,SAVEA4; save A4

 tst.l  20(A1)   ; dctlstorage
 bne  _calldrvr
 move.l SAVEA4,-(A7) ; save context
 movem.lD4-D7,-(A7); save D registers
 movem.lA0-A4,-(A7); save A registers
 moveq  #0,D0
 dc.w 0x907c; sub.w #_GLBLSIZE,D0
 dc.w 0xa322; newhandle()
 dc.w 0xa029; hlock()
 move.l A0,20(A1); save dctlstorage
 jsr    _context
 move.w 24(A1),D3; compute owned resource id
 not.w  D3
 asl.w  #5,D3
 ori.w  #0xc000,D3
 subq.l #4,A7    ; get data
 move.l #'DATA',-(A7)
 move.w D3,-(A7)
 dc.w 0xa9a0; data = getresource('DATA', ownid)
 move.l (A7)+,D0
 beq  _nodata
 move.l 20(A1),A1; dctlstorage
 move.l D0,A0
 move.l D0,-(A7) ; save data
 dc.w 0xa9e4; handandhand()
 dc.w 0xa9a3; releaseresource(data)

 suba.l #4,A7    ; get init code
 move.l #'INIT',-(A7)
 move.w D3,-(A7)
 dc.w 0xa9a0; init = getresource('INIT', ownid)
 move.l (A7)+,D0
 beq  _callopen

 move.l D0,A0
 move.l D0,-(A7) ; save init
 move.l (A0),A0
 jsr    (A0); call init
 dc.w 0xa9a3; releaseresource(init)
 bra    _callopen

 move.l SAVEA4,-(A7) ; save context
 movem.lD4-D7,-(A7); save D registers
 movem.lA0-A4,-(A7); save A registers
 jsr    _context
 jsr    (A2)
 movem.l(A7)+,A0-A4; restore A registers
 movem.l(A7)+,D4-D7; restore D registers
 move.l (A7)+,SAVEA4 ; restore context
 tst.l  D0; call iodone?
 bge  _callrts
 move.l 0x8fc,-(A7); jiodone address
 tst.w  D0; test result

 move.l SAVEA4,-(A7) ; save context
 movem.lD4-D7,-(A7); save D registers
 movem.lA0-A4,-(A7); save A registers
 jsr    _context
 jsr    (A2)
 move.l D0,-(A7) ; save result
 movem.l(A7),D0/A0-A1; restore arguments
 move.l 20(A1),A0; dctlstorage
 clr.l  20(A1)   ; restore dctlstorage
 dc.w 0xa023; deallocate
 move.l (A7)+,D0 ; restore result
 bra    _openrts

A Few Comments

I hope this look at the File System and Device Drivers has been useful to you. If you have any questions or notice any blunders, please send them to MacTutor and I'll try to answer or fix them in a future column. The source and object of the nested volume manager Nest as well as the DA and all the supporting files are available on source code disk #7 from MacTutor's mail order store for $8. Remember, this software is only an example, and definitely is not bullet proof. Don't try it on a hard disk until you have backed-up its contents. Here is the procedure you might use to install it:

• Use Apple's Font/DA Mover application to copy the desk accessory Nest from the Nest suitcase and paste into your system folder.

• Use Apple's ResEdit application to copy the .Nest DRVR resource from the Nest suitcase and paste into your system folder. You may have to change the DRVR's resource number before pasting the resource. After pasting the resource, set its system heap bit.

Before using Nest, you should be aware of several problems I have discovered while using Version 1.0a1 on a SuperMac hard disk:

• HFS Systems Only: Nest will run only if the HFS file system has been installed on your Macintosh. This means that you either own an HD20, a Macintosh Plus, or your third party hard disk supplier provided you with an HFS update. I have tested Version 1.0a1 on both an HD20 and a SuperMac Technology DataFrame 20.

• Don't Switch Launch: Your system will crash if you attempt to switch launch to a different system folder since the disk driver .Nest is not detached from the system resource file when opened by opendriver. (I consider this an easily fixed bug in opendriver.)

• Don't Mount Non-Contiguous Volumes: Although the original allocated host file space is contiguous, it may not remain so if you duplicate the file or copy it to another volume. (Finder Version 5.2 does not seem to attempt contiguous allocations.) The mount manager checks for contiguous allocation, but does not attempt to re-copy a non-contiguous file to a contiguous area. (This is also an easily fixed bug.)

• Don't Unmount Volumes With Open Files: When unmounting volumes, the mount manager does not check to see if there exist open files on the volume. If your in the Finder, it suffices to drag the nested volume to the trash can. The Finder will update the desktop and unmount the volume automatically. If you unmount the volume using the desk accessory in the Finder, the desktop file may not be correctly updated. If you unmount a volume with an open application or document file, you'll may crash your system and lose your document.

• Beware of Finder's Erase Disk: The Finder's Erase Disk command will format a nested volumes with 400k bytes or fewer as an MFS volume; larger volumes will get HFS directories. This is done independently of the original formatting of the nested volume.

• Beware of Finder's Trask Can: When you drag a nested volume to the trash can, the volume will be unmounted, but the volume's host file will not be automatically closed. The mount manager will notice this and properly clean up the next time it is opened.


Community Search:
MacTech Search:

Software Updates via MacUpdate

Adium - Popular instant messagi...
Adium is a fast and free instant messaging client which supports AIM, ICQ, Jabber, MSN, Yahoo!, Google Talk, Yahoo! Japan, Bonjour, Gadu-Gadu, Novell Groupwise, SIP/SIMPLE (Text), and Lotus Sametime... Read more
CleanMyMac 3.8.1 - $39.95
CleanMyMac makes space for the things you love. Sporting a range of ingenious new features, CleanMyMac lets you safely and intelligently scan and clean your entire system, delete large, unused files... Read more
BusyCal 3.1.7 - Powerful calendar app wi...
BusyCal is an award-winning desktop calendar that combines personal productivity features for individuals with powerful calendar sharing capabilities for families and workgroups. Its unique features... Read more
Lyn 1.8.9 - Lightweight image browser an...
Lyn is a fast, lightweight image browser and viewer designed for photographers, graphic artists, and Web designers. Featuring an extremely versatile and aesthetically pleasing interface, it delivers... Read more
Tweetbot 2.5 - Popular Twitter client.
Tweetbot is a full-featured OS X Twitter client with a lot of personality. Whether it's the meticulously-crafted interface, sounds and animation, or features like multiple timelines and column views... Read more
Monolingual 1.7.8 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. If you use your computer in only one (human) language, you... Read more
Dash 4.0.3 - Instant search and offline...
Dash is an API documentation browser and code snippet manager. Dash helps you store snippets of code, as well as instantly search and browse documentation for almost any API you might use (for a full... Read more
Posterino 3.3.6 - Create posters, collag...
Posterino offers enhanced customization and flexibility including a variety of new, stylish templates featuring grids of identical or odd-sized image boxes. You can customize the size and shape of... Read more
Apple Numbers 4.1.1 - Apple's sprea...
With Apple Numbers, sophisticated spreadsheets are just the start. The whole sheet is your canvas. Just add dramatic interactive charts, tables, and images that paint a revealing picture of your data... Read more
Apple Pages 6.1.1 - Apple's word pr...
Apple Pages is a powerful word processor that gives you everything you need to create documents that look beautiful. And read beautifully. It lets you work seamlessly between Mac and iOS devices, and... Read more

Latest Forum Discussions

See All

Get Ike in the new Fire Emblem: Heroes u...
One of the most popular Fire Emblem characters is finally available in a new update to Nintendo'sFire Emblem: Heroes. [Read more] | Read more »
Die With Glory in a new viking adventure...
If you're a fan of classic adventure games you'll do well to pick upDie With Glory, the gorgeous new title from Cloud Castle inc. Die With Glory updatesthe gameplay of the same kind of adventure classics such asMonkey Island for modern, mobile... | Read more »
Get up to speed with everything you need...
In case you haven’t heard, MU Origin just got a colossal new update with new all-server events, battle modes, and systems making their way to the land of MU. Here’s a handy guide to everything you need to know about the latest content. [Read... | Read more »
Minimalist puzzle game, Cuts, free on iO...
If you're looking for a gorgeous puzzle experience on iOS devices, developer's aesthetically interesting puzzler, Cuts, is discounted to free on the iOS App Store right now. [Read more] | Read more »
Anime tactical RPG, War of Crown, comes...
If you're looking for another tactical RPG fix to go alongside your Fire Emblem Heroes campaigns check out Gamevil's newest, anime-inspired tactics RPG, War of Crown, which comes out tomorrow. [Read more] | Read more »
Fantasy MMORPG MU Origin adds new modes,...
MU Origin, Webzen’s highly popular fantasy MMORPG is getting ready to shake things up for the second time this year, as a new update makes its way to the Google Play and App Store from today. Introducing new systems, modes, and events, the land of... | Read more »
Blizzard is looking to hire a mobile dev...
A new thread on the popular video game rumor forum, NeoGAF, uncovered an interesting job listing over at Blizzard Entertainment. It appears the studio behindStarCraft, World of WarCraft, Hearthstone,andOverwatch is looking to bring on a new hire... | Read more »
Legend of Zelda meets Cooking Mama in ne...
Dungeon Chef is what happens when you mix the RPG elements (and style) of a Legend of Zelda game, with cooking elements. Although, now that The Legend of Zelda: Breath of the Wild also has cookingelements, so maybe the gameplay is not so novel.... | Read more »
ChordFlow (Music)
ChordFlow 1.0.0 Device: iOS Universal Category: Music Price: $6.99, Version: 1.0.0 (iTunes) Description: ChordFlow is a chord sequencer with a unique 4-track polyphonic arpeggiator, extensive chord library, MIDI out and Ableton Link... | Read more »
The Walking Dead: A New Frontier is out...
The newest season of Telltale Games'The Walking Dead is well underway. After the release of the third episode, "Above the Law" about a month ago, episode four, "Thicker Than Water" is hot and ready for more zombies and gut-wrenching emotional... | Read more »

Price Scanner via

Digital Paper Tablet Offers Distraction Free...
I typically spend 8-10 hours a day gazing at the screens in my laptops and iPad, as tools of my livelihood, I don’t as a rule use electronic devices for pleasure reading. I subscribe to a daily... Read more
“Today at Apple” Bringing New Educational Ses...
Apple has announced plans to launch dozens of new educational sessions next month in all 495 Apple Stores ranging in topics from photo and video to music, coding, art and design, and more. The hands-... Read more
Smart Finance Free Comprehensive Personal Fin...
Moscow-based indie developer, Alexander Survillo has announced the release and immediate availability of Smart Finance: Personal Finance, Budget & Money 1.1.4, an update to his comprehensive... Read more
12-inch 1.1GHz Retina MacBooks on sale for $1...
B&H has 12″ 1.1GHz Retina MacBooks on sale for $100 off MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: - 12″ 1.1GHz Space Gray Retina MacBook: $1199.99 $100 off MSRP - 12... Read more
13-inch 2.7GHz Retina MacBook Pro on sale for...
B&H Photo has the 13″ 2.7GHz Retina MacBook Pro on sale for $130 off MSRP. Shipping is free, and B&H charges NY & NJ tax only: - 13″ 2.7GHz/128GB Retina MacBook Pro (MF839LL/A): $1169 $... Read more
15-inch 2.2GHz Retina MacBook Pros available...
B&H Photo has the 15″ 2.2GHz Retina MacBook Pro available for $200 off MSRP including free shipping plus NY & NJ sales tax only: - 15″ 2.2GHz Retina MacBook Pro (MJLQ2LL/A): $1799.99 $200 off... Read more
13-inch Touch Bar MacBook Pros on sale for up...
B&H Photo has the 2016 Apple 13″ Touch Bar MacBook Pros in stock today for up to $150 off MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: - 13″ 2.9GHz/512GB Touch Bar... Read more
Apple refurbished Apple TVs available for up...
Apple has Certified Refurbished 32GB and 64GB Apple TVs available for up to $30 off the cost of new models. Apple’s standard one-year warranty is included with each model, and shipping is free: -... Read more
12-inch 1.2GHz Retina MacBooks on sale for up...
B&H has 12″ 1.2GHz Retina MacBooks on sale for up to $160 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 12″ 1.2GHz Space Gray Retina MacBook: $1439.99 $160 off MSRP - 12″ 1... Read more
HyperX Ships Pulsefire FPS Gaming Mouse, Winn...
Your reporter is a longtime fan of gaming mice for general purpose coomnputing use, finding them typically superior in comfort and performance. HyperX, a division of Kingston Technology Company, Inc... Read more

Jobs Board

Product Manager, *Apple* Platforms - Viacom...
…Product Manager to drive the execution of its iOS and AppleTV experiences. The Apple Platform Product Manager will be a leader in our Agile/Scrum environment and Read more
Geek Squad *Apple* Master Consultation Agen...
**500662BR** **Job Title:** Geek Squad Apple Master Consultation Agent **Location Number:** 000286-Canton-Store **Job Description:** **What does a Geek Squad Read more
*Apple* Mobile Master - Best Buy (United Sta...
**500710BR** **Job Title:** Apple Mobile Master **Location Number:** 000279-North Olmsted-Store **Job Description:** **What does a Best Buy Apple Mobile Master Read more
*Apple* Engineering Specialist - CSRA (Unite...
Apple Engineering Specialist All times are in Eastern Daylight Time Requisition ID Job Locations US DC Washington DC Posted Date Category Engineering Sciences Read more
*Apple* Mac Computer Technician - GeekHampto...
…complex computer issues over the phone and in person? GeekHampton, Long Island's Apple Premium Service Provider, is looking for you! Come work with our crew Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.