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

Microsoft Office 2016 16.11 - Popular pr...
Microsoft Office 2016 - Unmistakably Office, designed for Mac. The new versions of Word, Excel, PowerPoint, Outlook, and OneNote provide the best of both worlds for Mac users - the familiar Office... Read more
Adobe Photoshop CC 2018 19.1.2 - Profess...
Photoshop CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Photoshop customer). Adobe Photoshop CC 2018, the industry standard... Read more
Adobe Dreamweaver CC 2018 -...
Dreamweaver CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Dreamweaver customer). Adobe Dreamweaver CC 2018 allows you to... Read more
Adobe Flash Player - Plug-in...
Adobe Flash Player is a cross-platform, browser-based application runtime that provides uncompromised viewing of expressive applications, content, and videos across browsers and operating systems.... Read more
Drive Genius 5.2.0 - $79.00
Drive Genius features a comprehensive Malware Scan. Automate your malware protection. Protect your investment from any threat. The Malware Scan is part of the automated DrivePulse utility. DrivePulse... Read more
MegaSeg 6.0.6 - Professional DJ and radi...
MegaSeg is a complete solution for pro audio/video DJ mixing, radio automation, and music scheduling with rock-solid performance and an easy-to-use design. Mix with visual waveforms and Magic... Read more
ffWorks 1.0.7 - Convert multimedia files...
ffWorks (was iFFmpeg), focused on simplicity, brings a fresh approach to the use of FFmpeg, allowing you to create ultra-high-quality movies without the need to write a single line of code on the... Read more
Dash 4.1.5 - 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
Evernote 7.0.3 - Create searchable notes...
Evernote allows you to easily capture information in any environment using whatever device or platform you find most convenient, and makes this information accessible and searchable at anytime, from... Read more
jAlbum Pro 15.3 - Organize your digital...
jAlbum Pro has all the features you love in jAlbum, but comes with a commercial license. You can create gorgeous custom photo galleries for the Web without writing a line of code! Beginner-friendly... Read more

Latest Forum Discussions

See All

Around the Empire: What have you missed...
Oh hi nice reader, and thanks for popping in to check out our weekly round-up of all the stuff that you might have missed across the Steel Media network. Yeah, that's right, it's a big ol' network. Obviously 148Apps is the best, but there are some... | Read more »
All the best games on sale for iPhone an...
It might not have been the greatest week for new releases on the App Store, but don't let that get you down, because there are some truly incredible games on sale for iPhone and iPad right now. Seriously, you could buy anything on this list and I... | Read more »
Everything You Need to Know About The Fo...
In just over a week, Epic Games has made a flurry of announcements. First, they revealed that Fortnite—their ultra-popular PUBG competitor—is coming to mobile. This was followed by brief sign-up period for interested beta testers before sending out... | Read more »
The best games that came out for iPhone...
It's not been the best week for games on the App Store. There are a few decent ones here and there, but nothing that's really going to make you throw down what you're doing and run to the nearest WiFi hotspot in order to download it. That's not to... | Read more »
Death Coming (Games)
Death Coming Device: iOS Universal Category: Games Price: $1.99, Version: (iTunes) Description: --- Background Story ---You Died. Pure and simple, but death was not the end. You have become an agent of Death: a... | Read more »
Hints, tips, and tricks for Empires and...
Empires and Puzzles is a slick match-stuff RPG that mixes in a bunch of city-building aspects to keep things fresh. And it's currently the Game of the Day over on the App Store. So, if you're picking it up for the first time today, we thought it'd... | Read more »
What You Need to Know About Sam Barlow’s...
Sam Barlow’s follow up to Her Story is #WarGames, an interactive video series that reimagines the 1983 film WarGames in a more present day context. It’s not exactly a game, but it’s definitely still interesting. Here are the top things you should... | Read more »
Pixel Plex Guide - How to Build Better T...
Pixel Plex is the latest city builder that has come to the App Store, and it takes a pretty different tact than the ones that came before it. Instead of being in charge of your own city by yourself, you have to work together with other players to... | Read more »
Fortnite Will Be Better Than PUBG on Mob...
Before last week, if you asked me which game I prefer between Fortnite Battle Royale and PlayerUnknown’s Battlegrounds (PUBG), I’d choose the latter just about 100% of the time. Now that we know that both games are primed to hit our mobile screens... | Read more »
Siege of Dragonspear (Games)
Siege of Dragonspear 2.5.12 Device: iOS Universal Category: Games Price: $9.99, Version: 2.5.12 (iTunes) Description: Experience the Siege of Dragonspear, an epic Baldur’s Gate tale, filled with with intrigue, magic, and monsters.... | Read more »

Price Scanner via

Sunday Sales: $200 off 13″ Touch Bar MacBook...
Amazon has new 2017 13″ 3.1GHz Touch Bar MacBook Pros on sale this weekend for $200 off MSRP, each including free shipping: – 13″ 3.1GHz/256GB Space Gray MacBook Pro (MPXV2LL/A): $1599.99 $200 off... Read more
B&H drops prices on 15″ MacBook Pros up t...
B&H Photo has dropped prices on new 2017 15″ MacBook Pros, now up to $300 off MSRP and matching Adorama’s price drop yesterday. Shipping is free, and B&H charges sales tax for NY & NJ... Read more
Apple restocks Certified Refurbished 2017 13″...
Apple has restocked Certified Refurbished 2017 13″ 2.3GHz MacBook Pros for $200-$230 off MSRP. A standard Apple one-year warranty is included with each MacBook, models receive new outer cases, and... Read more
13″ Space Gray Touch Bar MacBook Pros on sale...
Adorama has new 2017 13″ Space Gray Touch Bar MacBook Pros on sale for $150 off MSRP. Shipping is free, and Adorama charges sales tax in NY & NJ only: – 13″ 3.1GHz/256GB Space Gray MacBook Pro (... Read more
Best deal of the year on 15″ Apple MacBook Pr...
Adorama has New 2017 15″ MacBook Pros on sale for up to $300 off MSRP. Shipping is free, and Adorama charges sales tax in NJ and NY only: – 15″ 2.8GHz Touch Bar MacBook Pro Space Gray (MPTR2LL/A): $... Read more
Save $100-$150+ on 13″ Touch Bar MacBook Pros...
B&H Photo has 13″ Touch Bar MacBook Pros on sale for $100-$150 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 13″ 3.1GHz/256GB Space Gray MacBook Pro... Read more
Current deals on 27″ Apple iMacs, models up t...
B&H Photo has 27″ iMacs on sale for up to $150 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 27″ 3.8GHz iMac (MNED2LL/A): $2149 $150 off MSRP – 27″ 3... Read more
Thursday Deal: 13″ 2.3GHz MacBook Pro for $11...
B&H Photo has the 13″ 2.3GHz/128GB Space Gray MacBook Pro on sale for $100 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 13-inch 2.3GHz/128GB Space... Read more
How to save $100-$190 on 10″ & 12″ iPad P...
Apple is now offering Certified Refurbished 2017 10″ and 12″ iPad Pros for $100-$190 off MSRP, depending on the model. An Apple one-year warranty is included with each model, and shipping is free: –... Read more
Silver 12″ 1.3GHz MacBook on sale at B&H...
B&H Photo has the 2017 12″ 1.3GHz Silver MacBook on sale for $1399.99 including free shipping plus sales tax for NY & NJ residents only. Their price is $200 off MSRP, and it’s the lowest... Read more

Jobs Board

*Apple* Genius - Technical Customer Service...
Job Description:Job SummaryAs a Genius at the Apple Store, you maintain customers' trust in Apple as the skilled technical customer service expert, Read more
*Apple* Endpoint Administrator - Massachuset...
Reporting to the Director of Client Services (DCS), the Apple Endpoint Administrator serves as a member of the Client Services team, which provides technical support Read more
Franchise Operations Advisor- *APPLE* Exper...
UFG, Inc. Franchise Operations Advisor - MUST HAVE APPLE OR WINDOWS EXPERIENCE (Outside Sales / Business Development / Customer Service / Full Time) Top Five Reasons Read more
Engineering- Platform- *Apple* Enterprise P...
…& RESPONSIBILITIES You are responsible for creating and supporting our next-gen Apple Platform Installers from ground-up. We use Jamf Composer tool along with Read more
*Apple* OS X Server Administrator (Active Se...
Apple OS X Server Administrator \(Active Secret Clearance\) Description Come be a part of a top notch team, apply today\!\! Tuva TUVA provides turnkey solutions that Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.