The File System: 3.2 Disks and Drives: Accessing Disks

Up: GEOS SDK TechDocs | Up | Prev: 3.1 Accessing Drives | Next: 4 Directories and Paths

Applications will work with disks more than they will work with drives. Once a geode knows a disk's handle, it can ignore such questions as whether the disk is in a drive; it need merely provide the disk's handle. If necessary, the system will prompt the user to insert the disk in the appropriate drive.

Registering Disks

DiskRegisterDisk(), DiskRegisterDiskSilently()

GEOS automatically keeps track of all disks used. The first time a disk is accessed in a session, it is registered . This means that it is assigned a disk handle .

The disk handle records certain information, such as the disk's volume name and whether the disk is writable. It also notes in which drive the disk was last inserted; if the system prompts the user to reinsert the disk, it will insist on that drive. A disk is automatically reregistered when certain actions are performed which might invalidate a disk's handle-table entry; for example, it is reregistered if it is formatted. It is also reregistered if someone tries to write a file to a disk which is marked read-only; the user may have ejected the disk and removed its write-protect tab. Note that reregistering a disk does not change its handle; it just brings GEOS's information about the disk up-to-date.

Note that the disk handle is not a reference to the global handle table; thus, Swat commands like phandle will not work with disk handles. Disk handles should always be treated as opaque 16-bit tokens.

You can specifically instruct the system to register a disk by calling the routine DiskRegisterDisk() . The routine is passed a single argument, namely the drive number. If the disk has an entry in the disk table, the routine will not reregister the disk; it will just return the disk's handle. If the disk has no entry in the table, the system will create an entry and register the disk. In this case, also, the routine will return the (new) disk handle. If the routine fails (for example, because there is no valid disk in the specified drive, or the drive itself does not exist), it returns a null handle.

When a disk is registered, the system notes the volume label. This label is used when the system has to prompt the user to insert a disk. If an unlabeled disk is inserted, the system will choose an arbitrary label for the volume (e.g. "UNNAMED1"). The system does not actually write this label to the disk; the label is used by the system and discarded when the session ends. Ordinarily, the system will present an alert box to inform the user about the temporary label. You can suppress this notification by calling the system routine DiskRegisterDiskSilently() . This routine has the same arguments and return values as DiskRegisterDisk() .

Getting Information about a Disk

DiskGetVolumeInfo(), DiskGetVolumeFreeSpace(), DiskGetDrive(), DiskGetVolumeName(), DiskFind(), DiskCheckWritable(), DiskCheckInUse(), DiskCheckUnnamed(), DiskForEach(), DiskInfoStruct, DiskFindResult

GEOS provides many routines to get information about disks. If geodes call disk routines at all, they are most likely to call these. Most of these routines are passed the handle of the disk. If you know the disk's volume label and need to find out its handle, call the routine DiskFind() (described below). If you know the disk is currently in a drive and you need to find out its handle, register the disk (see Registering Disks ). Note that any routine which is passed a disk handle can be passed a standard path constant; in this case, the routine will give you information about the disk containing the geos.ini file.

The basic disk-information routine is DiskGetVolumeInfo() . This returns information about the size of the disk and the amount of free space available. The routine is passed two arguments: the disk handle and a pointer to a DiskInfoStruct structure (shown below). The routine fills in the fields of the DiskInfoStruct structure and returns zero if it was successful. If it fails for any reason, it returns an error value and sets the thread's error value (which can be recovered with ThreadGetError() ). The usual error value returned is the constant ERROR_INVALID_VOLUME.

Code Display 17-1 The DiskInfoStruct Structure

typedef struct {
	word		DIS_blockSize;		/* # of bytes in a block; smallest size
					 * file system can allocate at once */
	sdword		DIS_freeSpace;		/* # of bytes free on disk */
	sdword		DIS_totalSpace;		/* Total size of the disk in bytes */
	char		DIS_name[VOLUME_BUFFER_SIZE];				
					/* Volume name; if disk is unnamed, this
					 * is the temporary name. String is
					 * null-terminated. */
} DiskInfoStruct;

If you just want to know a disk's name, call DiskGetVolumeName() . This routine takes two arguments: the disk handle and the address of a character buffer. (The buffer must be at least VOLUME_NAME_LENGTH_ZT characters long.) It writes the volume name to the buffer as a null-terminated string, and it returns the buffer's address. If the volume is unnamed, DiskGetVolumeName() writes the temporary volume name.

Note that all the routines which return a volume's name will return the temporary name if the volume is unnamed. For this reason, if you want to find out if a volume is unnamed, you must use a special purpose routine, namely DiskCheckUnnamed() . This Boolean routine is passed the disk's handle. If the volume does not have a permanent label, the routine returns true ; otherwise, it returns false .

If you want to know how much free space is available on a disk, call the routine DiskGetVolumeFreeSpace() . The routine is passed the disk handle; it returns (as a dword) the number of free bytes available. If the volume is currently read-only (e.g. a floppy disk with the write-protect tab set), it returns the amount of space that would be available if the volume were made read/write. If the volume is, by its nature, not writable (e.g. a CD-ROM disk), the routine will return zero. It will also return zero if an error condition occurs; in this case, it will also set the thread's error value.

If you want to know what drive a volume is associated with, call DiskGetDrive() . This routine takes one argument, namely the volume's disk handle. It returns the number of the drive which had that disk. Note that it will return this value even if that drive is no longer usable.

If you know the label of a volume which has been registered and you need to find out its handle, call the routine DiskFind() . The routine takes two arguments: the address of a null-terminated string containing the volume name and a pointer to a variable of the DiskFindResult enumerated type. It will return the disk's handle; if no disk with the specified name has been registered, it will return a null handle. DiskFindResult has the following possible values:

DFR_UNIQUE
Exactly one volume with the specified name has been registered. The handle of that volume was returned.
DFR_NOT_UNIQUE
Two or more volumes with the specified label have been registered. The handle of an arbitrary one of these volumes was returned. If you want to find the handles of all of these disks, call DiskForEach() , described below.
DFR_NOT_FOUND
No disk with the specified label has been registered. A null handle was returned.

To check if a volume is writable, call the Boolean routine DiskCheckWritable() . The routine takes one argument, the disk's handle. If the disk is writable, the routine returns true (i.e. non-zero). If the disk is non-writable, the routine returns false (i.e. zero).

To see if a disk is being used by any threads, call DiskCheckInUse() . The routine takes one argument: the disk's handle. It returns true (i.e. non-zero) if a file on the disk is open or if any thread has a directory on that disk in its directory stack (see Current Path and Directory Stack ). If neither condition applies, the routine returns false (i.e. zero).

If you want to perform an action on every disk, call DiskForEach() . This routine takes one argument, a pointer to a Boolean callback routine. The callback routine should take a single argument, the handle of a disk. DiskForEach() calls the callback routine once for every registered disk. It passes the disk handle to the callback routine, which can take any action it wants; for example, it could call one of the other disk-information routines. The callback routine can make DiskForEach() halt prematurely by returning a non-zero value. If the callback routine forced an early halt, DiskForEach() returns the last disk handle which had been passed to the callback routine; otherwise it returns a null handle. This routine is commonly called to look for a specific disk. To do this, simply have the callback routine check each disk to see if it is the one sought; if it is, simply return true , and DiskForEach() will return that disk's handle.

DiskForEach() does not need to examine the actual disks; it works from the information the file-system stores about all registered disks. This means that DiskForEach() will not have to prompt the user to insert any disks. Of course, the callback routine may need to examine the disks, in which case the user will be prompted when necessary.

Saving and Restoring a Disk Handle

DiskSave(), DiskRestore(), DiskRestoreError

A disk does not necessarily have the same handle from one execution of GEOS to another. This can pose a problem for an application which is restarting from a state file. In order to reopen a file, it has to know the file's location. If it knows the file's location relative to a standard path, there is no problem, since the application can use the standard path constant in the place of a disk handle. If the file is not in a standard path, the application will need some way of figuring out the disk's handle on restart.

For this reason, GEOS provides DiskSave() and DiskRestore() . DiskSave() saves information about a disk in an opaque data structure. DiskRestore() reads such a data buffer and returns the handle of the disk described; it even arranges to prompt the user if the disk has not been registered yet.

To save a disk handle, call DiskSave() . This routine takes three arguments:

If DiskSave() was successful, it will return true . The integer parameter will contain the size of the buffer actually needed; for example, if the buffer had been 100 bytes long and DiskSave() returns 60, you can safely free the last 40 bytes in the buffer. If DiskSave() failed, it will return false . If it failed because the buffer was too small, it will write the size needed into the integer passed; simply call DiskSave() again with a large enough buffer. If DiskSave() failed for some other reason (e.g. the disk belongs to a drive which no longer exists), it will write a zero value to the integer.

To restore a disk, call DiskRestore() . This routine takes two arguments:

If the disk in question has already been registered or is currently in its drive, DiskRestore() will return its handle. If the disk is not registered and is not in any drive, DiskRestore() will call the callback routine. The callback routine should accept the following four arguments:

If the callback routine believes the user inserted the correct disk, it should return DRE_DISK_IN_DRIVE. Otherwise, it should return a DiskRestoreError constant. In this case, DiskRestore() will fail and set the thread's error value to the constant specified. If the callback routine returns an error, that error will generally be DRE_USER_CANCELLED_RESTORE.

If DiskRestore() is successful, it will return the disk handle; this may be different from the disk's handle in the previous execution. You may now free the data buffer, if you like. If DiskRestore() fails, it will return a null handle and set the thread's error value. There are several different DiskRestoreError values; they are listed below.

DRE_DRIVE_NO_LONGER_EXISTS
The disk had last been used in a drive which is no longer attached to the system (or the appropriate file-system driver is no longer present).
DRE_REMOVABLE_DRIVE_DOESNT_HOLD_DISK
The disk was in a removable-media drive, and that drive doesn't contain the disk.
DRE_USER_CANCELLED_RESTORE
A callback routine was called and returned this value to DiskRestore() .
DRE_COULDNT_CREATE_NEW_DISK_HANDLE
DiskRestore() found the disk in the drive but was for some reason unable to create the disk handle.
DRE_REMOVABLE_DRIVE_IS_BUSY
The appropriate drive exists but is unavailable due to some time-consuming operation (e.g. a disk format).

Other Disk Utilities

DiskSetVolumeName(), DiskFormat(), DiskCopy(), FormatError, DiskCopyCallback, DiskCopyError

GEOS provides several utilities for working with disks. These utilities allow geodes to copy disks, format them, and change their volume names. Most applications will never need to use these utilities; they can rely on the users to take care of disk formatting with an application like GeoManager. However, some applications will want to make use of them. For example, an archiving program might automatically format storage disks and give them appropriate labels.

Changing a Volume Name

If you want to set or change a volume's name, you should call DiskSetVolumeName() . This routine takes two arguments: the volume's handle and the address of a null-terminated string (containing the new volume name). If it is able to change the volume's name, it returns zero; otherwise, it returns an error code. It sets or clears the thread's error value appropriately. The following error codes may be returned:

ERROR_INVALID_VOLUME
An invalid disk handle was passed to the routine.
ERROR_ACCESS_DENIED
For some reason, the volume's name could not be changed. For example, the volume might not be writable.
ERROR_DISK_STALE
The drive containing that disk has been deleted. This usually only happens with network drives.

Formatting a Disk

If a geode needs to format a disk, it can call the routine DiskFormat() . This routine can do either low-level or high-level ("soft") formats. The routine does not interact with the user interface; instead, it calls a callback routine, which can arrange any such interaction. DiskFormat() takes seven arguments:

DFF_CALLBACK_PERCENT_DONE
A callback routine will be called periodically. The callback routine will be passed a single argument, namely the percentage of the format which has been done, expressed as an integer.
DFF_CALLBACK_CYL_HEAD
A callback routine will be called periodically. The callback routine will be passed a single argument, namely the cylinder head being formatted. If both DFF_CALLBACK_PERCENT_DONE and DFF_CALLBACK_CYL_HEAD are passed, results are undefined.
DFF_FORCE_ERASE
A "hard format" should be done; that is, the sectors should be rewritten and initialized to zeros. If this flag is not set, DiskFormat() will do a "soft format" if possible; it will check the sectors and write a blank file allocation table, but it will not necessarily erase the data from the disk.

DiskFormat() returns a member of the FormatError enumerated type. If the format was successful, it will return the constant FMT_DONE (which is guaranteed to equal zero). Otherwise, it will return one of the following constants:

FMT_DRIVE_NOT_READY
FMT_ERROR_WRITING_BOOT
FMT_ERROR_WRITING_ROOT_DIR
FMT_ERROR_WRITING_FAT
FMT_ABORTED
FMT_SET_VOLUME_NAME_ERROR
FMT_CANNOT_FORMAT_FIXED_DISKS_IN_CUR_RELEASE
FMT_BAD_PARTITION_TABLE,
FMT_ERR_READING_PARTITION_TABLE,
FMT_ERR_NO_PARTITION_FOUND,
FMT_ERR_MULTIPLE_PRIMARY_PARTITIONS,
FMT_ERR_NO_EXTENDED_PARTITION_FOUND
FMT_ERR_CANNOT_ALLOC_SECTOR_BUFFER
FMT_ERR_DISK_IS_IN_USE
FMT_ERR_WRITE_PROTECTED
FMT_ERR_DRIVE_CANNOT_SUPPORT_GIVEN_FORMAT
FMT_ERR_INVALID_DRIVE_SPECIFIED
FMT_ERR_DRIVE_CANNOT_BE_FORMATTED
FMT_ERR_DISK_UNAVAILABLE

Copying Disks

GEOS provides a routine for copying disks. This routine, DiskCopy() , maintains a balance between the two goals of limiting memory usage and minimizing disk swapping. It will reformat the destination disk if necessary. The routine does a sector-for-sector copy; therefore, the destination disk must either be of exactly the same type as the source disk (i.e., same medium and size), or it must be reformatable to be the same size. For this reason, neither the source nor the destination may be a fixed disk.

DiskCopy() does not interact with the user directly, even though the user may have to swap disks. Instead, it calls a callback routine whenever interaction with the user may be necessary. The routine takes the following arguments:

The callback routine is called under a variety of circumstances. When it is called, the first argument passed is a member of the DiskCopyCallback enumerated type, which specifies both why the callback routine was called and what the other arguments signify. DiskCopyCallback contains the following types:

CALLBACK_GET_SOURCE_DISK
The callback routine should prompt the user to insert the source disk. The second argument is meaningless for this call. The third argument is the number identifying the drive.
CALLBACK_GET_DEST_DISK
The callback routine should prompt the user to insert the destination disk. The second argument is meaningless for this call. The third argument is the number identifying the drive.
CALLBACK_REPORT_NUM_SWAPS
The second argument is meaningless for this call. The third argument is the number of disk swaps that will be necessary to copy the disk. The callback routine may wish to report this number to the user and ask for confirmation.
CALLBACK_VERIFY_DEST_DESTRUCTION
If the destination disk has already been formatted, the callback routine will be called with this parameter. The callback routine may wish to remind the user that the destination disk will be erased. The second argument is the handle of the destination disk; this is useful if, for example, you want to report the disk's name. The third argument is the destination drive's number. As in the other cases, the callback routine can abort the format by returning non-zero.
CALLBACK_REPORT_FORMAT_PERCT
If the destination disk needs to be formatted, DiskCopy() will periodically call the callback routine with this parameter. In this case, the second argument will be meaningless; the third parameter will be the percentage of the destination disk which has been formatted.
CALLBACK_REPORT_COPY_PERCT
While the copy is taking place, DiskCopy() will periodically call the callback routine with this parameter. In this case, the second parameter will be meaningless; the third parameter will be the percentage of the copy which has been completed.

If the copy was successful, DiskCopy() returns zero. Otherwise, it returns a member of the DiskCopyError enumerated type, which has the following members:

ERR_DISKCOPY_INSUFFICIENT_MEM
This is returned if the routine was unable to get adequate memory.
ERR_CANT_COPY_FIXED_DISKS
ERR_CANT_READ_FROM_SOURCE
ERR_CANT_WRITE_TO_DEST
ERR_INCOMPATIBLE_FORMATS
ERR_OPERATION_CANCELLED
This is returned if the callback routine ever returned a non-zero value, thus aborting the copy.
ERR_CANT_FORMAT_DEST

Up: GEOS SDK TechDocs | Up | Prev: 3.1 Accessing Drives | Next: 4 Directories and Paths