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

Iridient Developer 3.3.12 - Powerful ima...
Iridient Developer (was RAW Developer) is a powerful image-conversion application designed specifically for OS X. Iridient Developer gives advanced photographers total control over every aspect of... Read more
DxO PhotoLab - Image enhancemen...
DxO PhotoLab (was DxO Optics Pro) provides a complete set of smart assisted corrections that you can manually fine-tune at any time. Take control on every aspect of your photos: effectively remove... Read more
Airfoil 5.9.1 - Send audio from any app...
Airfoil allows you to send any audio to AirPort Express units, Apple TVs, and even other Macs and PCs, all in sync! It's your audio - everywhere. With Airfoil you can take audio from any... Read more
TunnelBear 3.9.8 - Subscription-based pr...
TunnelBear is a subscription-based virtual private network (VPN) service and companion app, enabling you to browse the internet privately and securely. Features Browse privately - Secure your data... Read more
Things 3.12.4 - Elegant personal task ma...
Things is a task management solution that helps to organize your tasks in an elegant and intuitive way. Things combines powerful features with simplicity through the use of tags and its intelligent... Read more
Vitamin-R 3.17 - Personal productivity t...
Vitamin-R creates the optimal conditions for your brain to work at its best by structuring your work into short bursts of distraction-free, highly focused activity alternating with opportunities for... Read more
iMazing 2.11.6 - Complete iOS device man...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
BBEdit 13.1 - Powerful text and HTML edi...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more
Apple Security Update 2020-003 - For mac...
Apple Security Update is recommended for all users and improves the security of OS X. For detailed information about the security content of this update, please visit lists security updates for... Read more
BetterTouchTool 3.379 - Customize multi-...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom)... Read more

Latest Forum Discussions

See All

The 5 Best Mobile Real Time Strategy Gam...
Real-time strategy games feel like they’d be a perfect fit for mobile, but they’re trickier to pull off that you might think. The traditional mold of base-building and micro management can work on touch screens, but needs to be carefully honed so... | Read more »
Using your phone in a protest
I can't write about games today. There is a struggle happening in the streets right now and it needs everyone's attention. Here's some good info on how you can use your iOS device safely amidst a protest. | Read more »
Dungonian is a card-based dungeon crawle...
Dungonian is a card-based dungeon crawler from developer SandFish Games that only recently launched as a free-to-play title. It offers an extensive roster of playable heroes to collect and enemies to take down, and it's available right now for iOS... | Read more »
Steam Link Spotlight - Signs of the Sojo...
Steam Link Spotlight is a feature where we look at PC games that play exceptionally well using the Steam Link app. Our last entry was XCOM: Chimera Squad. Read about how it plays using Steam Link's new mouse and keyboard support over here. | Read more »
Steampunk Tower 2, DreamGate's sequ...
Steampunk Tower 2 is a DreamGate's follow up to their previous tower defence game. It's available now for both iOS and Android as a free-to-play title and will see players defending their lone base by kitting it out with a variety of turrets. [... | Read more »
Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
Pokemon Go Fest 2020 will be a virtual e...
Niantic has announced that Pokemon Go Fest will still take place this year although understandably it won't be a physical event. Instead, it will become a virtual celebration and is set to be held on 25th and 26th July. [Read more] | Read more »
Marvel Future Fight's major May upd...
Marvel Future Fight's latest update has now landed, and it sounds like a big one. The focus this time around is on Marvel's Guardians of the Galaxy, and it introduces all-new characters, quests, and uniforms for players to collect. [Read more] | Read more »
SINoALICE, Yoko Taro and Pokelabo's...
Yoko Taro and developer Pokelabo's SINoALICE has now opened for pre-registration over on the App Store. It's already amassed 1.5 million Android pre-registrations, and it's currently slated to launch on July 1st. [Read more] | Read more »
Masketeers: Idle Has Fallen's lates...
Masketeers: Idle Has Fallen is the latest endeavour from Appxplore, the folks behind Crab War, Thor: War of Tapnarok and Light A Way. It's an idle RPG that's currently available for Android in Early Access and will head to iOS at a later date. [... | Read more »

Price Scanner via

Apple’s new 2020 13″ MacBook Pros on sale for...
Apple reseller Abt Electronics has new 2020 13″ MacBook Pros on sale today for up to $140 off MSRP, starting at $1208. Shipping is free, and most configurations are in stock today. Note that Abt’s... Read more
Apple CEO Reacts To Nationwide Protests Over...
NEWS: 06.03.20 – With the recent death of a black man in the custody of a white police officer igniting outrage among Americans from all walks of life, which resulted in protests and civil unrest... Read more
At up to $420 off MSRP, these Certified Refur...
Apple has Certified Refurbished 2019 16″ MacBook Pros available for up to $420 off the cost of new models, starting at $2039. Each model features a new outer case, shipping is free, and an Apple 1-... Read more
Apple restocks refurbished 3rd generation 12....
Apple restocked select 3rd generation 12.9″ WiFi iPad Pros starting at only $699 and up to $330 off original MSRP. Each iPad comes with a standard Apple one-year warranty, outer cases are new, and... Read more
These wireless carriers are offering Apple’s...
Looking for a deal on Apple’s new iPhone SE? Apple itself offers the unlocked 64GB iPhone SE for $399 or $16.62/month. If you’re willing to try a new carrier, two of Apple’s wireless carriers are... Read more
Save $80 on the 64GB 10.5″ iPad Air with this...
Apple has 10.5″ 64GB WiFi iPad Airs models available for $80 off MSRP, Certified Refurbished. Each iPad comes with Apple’s standard one-year warranty and includes a new outer case. Shipping is free... Read more
Apple offers wide range of refurbished custom...
Save up to $7740 on a custom-configured Mac Pro with these Certified Refurbished models available at Apple. Each Mac Pro features a new outer case, free shipping, and includes Apple’s standard 1-year... Read more
Sale! $200 off on select 2020 13″ MacBook Pro...
Amazon has select 2020 13″ MacBook Pro configurations on sale today for $200 off Apple’s MSRP. Shipping is free. Be sure to purchase the MacBook Pro from Amazon, rather than a third-party seller, and... Read more
June 1 only: $100 off Apple’s iPhones at Boos...
Boost Mobile is offering Apple iPhone 11, 11 Pro, and iPhone 11 Pro Max models for $100 off MSRP with service. Their discount reduces the cost of an iPhone 11/64GB to $599, iPhone 11 Pro to $899 for... Read more
Sams Club Sales Event: $100 off every Apple W...
Sams Club is discounting all Apple Watch Series 5 models by $100 off Apple’s MSRP through June 3, 2020. Choose free shipping or free local store pickup (if available). Sale prices for online orders... Read more

Jobs Board

Security Officer ($23.00/Hourly) - *Apple*...
**Security Officer \($23\.00/Hourly\) \- Apple Store** **Description** About NMS Built on a culture of safety and integrity, NMSdelivers award\-winning, integrated Read more
*Apple* Service Desk Analyst - Zones LLC (Un...
Apple Service Desk Analyst Professional Services Albany, New York Apply Description Position OverviewAs a Service Desk Analyst, you will provide customer-friendly Read more
*Apple* Mac Desktop Support - Global Dimensi...
…Operate and support an Active Directory (AD) server-client environment for all Apple devices operating on the BUMED network + Leverage necessary industry enterprise Read more
*Apple* Mac Desktop Support - Global Dimensi...
…Operate and support an Active Directory (AD) server-client environment for all Apple devices operating on the BUMED network + Leverage necessary industry enterprise Read more
Surgical Technologist III, *Apple* Hill Sur...
Surgical Technologist III, Apple Hill Surgical Center - Full Time Tracking Code D5.29.2020 Job Description Surgical Technologist III Apple Hill Surgical Center Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.