Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Database

Inside the ISO-9660 Filesystem Format

Source Code Accompanies This Article. Download It Now.


DEC92: INSIDE THE ISO-9660 FILESYSTEM FORMAT

INSIDE THE ISO-9660 FILESYSTEM FORMAT

Untangling CD-ROM standards

William Frederick Jolitz and Lynne Greer Jolitz

Bill and Lynne are the developers of 386BSD and authors of the long-running DDJ series, "Porting UNIX to the 386." They can be contacted at http://lynne.telemuse.net.


Over the last ten years, we've gone from shuffling through floppies to using hard disks and client/server networks to store-and-retrieve applications software. However, since data sets in use on the PC have been doubling in size every year, even hard disks are hard-pressed to meet demand. The need to archive and retrieve ever-growing amounts of information, coupled with the storage demands of today's software, has resulted in a search for better storage technologies, which cannot only store larger amounts of data, but also allow retrieval to be simple, error free, low cost, and timely. While fixed disk-drive technology has kept up admirably with current requirements, the pace of change in removable media technologies has been disappointing at best, with one exception: CD-ROM.

Skeptics point out that CD-ROM technology is close to a decade old, claim it's been made obsolete by read/write medium and improvements in recording density, and declare that CD-ROM drives and titles are only of interest to a limited audience. The economics of large software distribution make CD-ROM an excellent alternative to floppy distribution. Once a CD-ROM is mastered (at a typical cost of $1200.00), each individual CD-ROM costs the developer approximately $1.75. Instead of many failure-prone floppies, only one durable CD-ROM need be supplied per customer, with room for expansion later if need be.

Many firms with large software distributions or applications (Sun, Microsoft, and ParcPlace, for instance) or large databases (such as the Oxford English Dictionary) have moved to CD-ROM as their distribution media, and several PC manufacturers now supply PCs with CD-ROM drives as standard equipment.

Differences Between CD and CD-ROM

The media on which both audio CDs and data CD-ROM information is recorded is a wonderful example of engineering simplicity--a simple circle of plastic grooved to hold the fair portion of a gigabyte. Very little can go wrong: No magnetic fields can be erased; no temperature-sensitive compound (like those found in erasable-optical) can be cooked or frozen; and no fingerprints on the tracks can create errors.

However, CDs and CD-ROMs are not completely interchangeable. While you can insert a CD into a CD-ROM player and plug in headphones to hear music, you cannot read the CD digital information directly off the disk. This is because CDs and CD-ROMs are mastered in different formats. By the same token, while you can "play" a CD-ROM in a CD player, the sound is not exactly stimulating--all CD-ROMs play the same droning squeal. (It is possible to play audio off a portion of a CD-ROM by arranging the formats appropriately.)

Some Things Never Change...

CD-ROM drives in general function like any other write-protected disk drive, and at the device-driver level appear quite ordinary. (See the textbox entitled, "An Overview of CD-ROM Hardware.") However, CDs (including CD-ROMs) do not have a fixed number of sectors in a fixed-arm position. Instead, an inward spiral of records is arranged to maintain minimal latency from record to record. Thus, audio playback does not drop out or lag behind on obtaining encoded data for the digital-to-analog converter. (If you "shake" a CD player while it's operating, it will lose the track it's on, fall behind, and drop out the signal.)

CDs are not random access, but rather sequential access, which is why they tend to be slow--the head "hunts" to find the desired record. (The fastest CD-ROMs have 260-280 millisecond access rates with 300 Kbytes/sec transfer rates.) The records (or sectors, in the case of CD-ROMs) are indexed by track and running time into the track in the same way cylinder, head, and sector indices are used in a hard-disk drive. (With SCSI, these records are hidden by "logical" address translation in the drive's controller.) Unlike with hard drives, a separate head is not used for timing information, so each time a CD-ROM is moved for random access it must search up and down the spiral in the vicinity the head let down to find the desired sector. This "latency penalty" is a key difference between CD-ROM drives and other disk drives.

CD-ROM sector sizes are large, usually two Kbytes per sector, and larger sector sizes are possible. As a result, the CD-ROM filesystem uses "logical" sector sizes to standardize the increments to which the drive's contents can be referred. Some SCSI CD-ROM drives have a feature that allows them to make a drive appear to be at any selected block size (even ones less than the physical sector size of the actual CD-ROM) to accommodate software which insists on certain sector sizes.

Filesystem Organization

A CD-ROM may be mastered with any kind of information on it. (Sun Microsystems uses the Berkeley UNIX UFS filesystems on many CD-ROMs, and CD-ROMs embedded in equipment such as bitmaps for laser printers often have no filesystem arrangement.) However, because CD-ROMs are especially suited to the volume publishing of information, a standard filesystem useful across many kinds of architectures and appliances (such as a CD-ROM viewer or CD-I player) is very desirable. All early CD-ROM personal-computer applications used the High Sierra format, which arranged file information in a dense, sequential layout to minimize nonsequential access.

When a CD-ROM is browsed up and down directories, large delays occur as the drive moves its heads. Reading any of the files in that same directory results in much less delay, however. (This effect can be best seen in the software archive disks, where the aggregate contents of files in a directory are large, so the distance spanned by each directory is significant.)

The High Sierra filesystem format uses a hierarchical (eight levels of directories deep) tree filesystem arrangement, similar to UNIX and MS-DOS. It organizes information in a breadth-first traversal of this tree, with individual files contiguously allocated in dense organization, to reduce storage latency; see Figure 1.

Unlike UNIX and MS-DOS (and many other) filesystems which have blocks allocated from a separate list of nonsequential disk blocks, High Sierra stores file contents in a sequential extent-based arrangement, so that the file contents appear on consecutive logical sectors. This reduces file-access latency. Since the medium is read only, one does not have to "compact" out the holes that grow in extent-based filesystems used for read/write purposes (which justifies the nonsequential nature of block-allocated filesystems).

High Sierra has a minimal set of file attributes (directory or ordinary file and time of recording) and name attributes (name, extension, and version). The designers realized they could never get people to agree on a unified definition of file attributes, so the minimum "common" information was encoded, and a place for future optional extensions (system use area) was defined for each file.

High Sierra was soon adopted (with changes) as an international standard (ISO-9660-1988), and the ISO-9660 filesystem format is now used throughout the industry. It's truly remarkable that a standard this significant was developed so quickly and accurately.

ISO-9660 in Detail

An ISO-9660 CD-ROM is described in Figure 2. A reserved field at the beginning of the disk is present for use in booting the CD-ROM on a computer. Immediately afterwards, a series of volume descriptors details the contents and kind of information contained on the disk (somewhat akin to the partition table of MS-DOS).

A volume descriptor is broken up into two parts; one specific to the standard itself (the type of volume descriptor), and the other detailing the characteristics of the descriptor. The volume descriptor is constructed in this manner so that if a program reading the disk does not understand a particular descriptor, it can skip over it until it finds one it recognizes, thus allowing the use of many different types of information on one CD-ROM. However, it must have a primary descriptor describing the ISO-9660 filesystem, and it must have an ending descriptor (a variable-length table which contains information on how many other descriptors are present).

It is possible to have many kinds of filesystems and information arrangements on a single CD-ROM. However, while many other kinds of descriptors can be used to optionally record non-ISO defined information contents, the primary volume descriptor is always present.

In order to accommodate the two common byte orders, Big Endian (680x0) and Little Endian (80x86), ISO-9660 has data types which allow either and consequently are twice as big. The "least significant" half holds the Little Endian and the "most significant" half the Big Endian. Thus, the 32-bit integer (0x11223344) is represented as the byte sequence (0x44, 0x33, 0x22, 0x11, 0x22, 0x22, 0x33, 0x44)--essentially a binary palindrome.

ISO-9660 Primary Volume Descriptor

The ISO-9660 primary volume descriptor describes the characteristics of the ISO-standard filesystem information present on a given CD-ROM (refer to Figure 3 ). It acts much like the superblock of the UNIX filesystem, providing details on the ISO-9660 compliant portions of the disk. While we can have many kinds of filesystems on a single ISO-9660 CD-ROM, we can have only one ISO-9660 file structure (found as the primary volume-descriptor type).

Figure 3: File structure of an ISO-9660 and High Sierra primary volume descriptor.

  /* volume descriptor types -- type field of each descriptor */
  #define VD_PRIMARY 1
  #define VD_END 255

  /* ISO 9660 primary descriptor */
  #define ISODCL(from, to) (to - from + 1)

  #define ISO_STANDARD_ID "CD001"

  struct iso_primary_descriptor {
       char type                      [ISODCL (  1,     1)];
       char id                        [ISODCL (  2,     6)];
       char version                   [ISODCL (  7,     7)];
       char reserved1                 [ISODCL (  8,     8)];
       char system_id                 [ISODCL (  9,    40)]; /* achars */
       char volume_id                 [ISODCL ( 41,    72)]; /* dchars */
       char reserved2                 [ISODCL ( 73,    80)];
       char volume_space_size         [ISODCL ( 81,    88)];
       char reserved3                 [ISODCL ( 89,   120)];
       char volume_set_size           [ISODCL (121,   124)];
       char volume_sequence_number    [ISODCL (125,   128)];
       char logical_block_size        [ISODCL (129,   132)];
       char path_table_size           [ISODCL (133,   140)];
       char type_1_path_table         [ISODCL (141,   144)];
       char opt_type_1_path_table     [ISODCL (145,   148)];
       char type_m_path_table         [ISODCL (149,   152)];
       char opt_type_m_path_table     [ISODCL (153,   156)];
       char root_directory_record     [ISODCL (157,   190)];
       char volume_set_id             [ISODCL (191,   318)]; /* dchars */
       char publisher_id              [ISODCL (319,   446)]; /* achars */
       char preparer_id               [ISODCL (447,   574)]; /* achars */
       char application_id            [ISODCL (575,   702)]; /* achars */
       char copyright_file_id         [ISODCL (703,   739)]; /* dchars */
       char abstract_file_id          [ISODCL (740,   776)]; /* dchars */
       char bibliographic_file_id     [ISODCL (777,   813)]; /* dchars */
       char creation_date             [ISODCL (814,   830)];
       char modification_date         [ISODCL (831,   847)];
       char expiration_date           [ISODCL (848,   864)];
       char effective_date            [ISODCL (865,   881)];
       char file_structure_version    [ISODCL (882,   882)];
       char reserved4                 [ISODCL (883,   883)];
       char application_data          [ISODCL (884,  1395)];
       char reserved5                 [ISODCL (1396,  2048)];

  }:

  /* High Sierra format primary descriptor */
  #define HSFDCL (from, to) (to - from + 1)

  #define HSF_STANDARD_ID "CDROM"

  struct hsf_primary_descriptor {
       char volume_lbn                [HSFDCL (  1,     8)];
       char type                      [HSFDCL (  9,     9)];
       char id                        [HSFDCL ( 10,    14)];
       char version                   [HSFDCL ( 15,    15)];
       char reserved1                 [HSFDCL ( 16,    16)];
       char system_id                 [HSFDCL ( 17,    48)]; /* achars */
       char volume_id                 [HSFDCL ( 49,    80)]; /* dchars */
       char reserved2                 [HSFDCL ( 81,    88)];
       char volume_space_size         [HSFDCL ( 89,    96)];
       char reserved3                 [HSFDCL ( 97,   128)];
       char volume_set_size           [HSFDCL (129,   132)];
       char volume_sequence_number    [HSFDCL (133,   136)];
       char logical_block_size        [HSFDCL (137,   140)];
       char path_table_size           [HSFDCL (141,   148)];
       char manditory_path_table_lsb  [HSFDCL (149,   152)];
       char opt_path_table_lsb_1      [HSFDCL (153,   156)];
       char opt_path_table_lsb_2      [HSFDCL (157,   160)];
       char opt_path_table_lsb_3      [HSFDCL (161,   164)];
       char manditory_path_table_msb  [HSFDCL (165,   168)];
       char opt_path_table_msb_1      [HSFDCL (169,   172)];
       char opt_path_table_msb_2      [HSFDCL (173,   176)];
       char opt_path_table_msb_3      [HSFDCL (177,   180)];
       char root_directory_record     [HSFDCL (181,   214)];
       char volume_set_id             [HSFDCL (215,   342)]; /* dchars */
       char publisher_id              [HSFDCL (343,   470)]; /* achars */
       char preparer_id               [HSFDCL (471,   598)]; /* achars */
       char application_id            [HSFDCL (599,   726)]; /* achars */
       char copyright_file_id         [HSFDCL (727,   758)]; /* dchars */
       char abstract_file_id          [HSFDCL (759,   790)]; /* dchars */
       char creation_date             [HSFDCL (791,   806)];
       char modification_date         [HSFDCL (807,   822)];
       char expiration_date           [HSFDCL (823,   838)];
       char effective_date            [HSFDCL (839,   854)];
       char file_structure_version    [HSFDCL (855,   855)];
       char reserved4                 [HSFDCL (856,   856)];
       char application_data          [HSFDCL (857,  1368)];
       char reserved5                 [HSFDCL (1369,  2048)];

Contained within the primary volume descriptor is the root-directory record describing the location of the contiguous root directory. (As in UNIX, directories appear as files for the operating system's special use.) Within this region, directory entries are successively stored. The evaluation of the ISO-9660 filenames is begun at this location. The root directory is stored as is any other file--as an extent or sequential series of sectors that contains each of the directory entries appearing in the root. In addition, since ISO-9660 works by segmenting the CD-ROM into logical blocks, the size of these blocks is found in the primary volume descriptor as well.

Directory-entry Format

Each directory entry begins with a length octet describing the size of the entry. Entries themselves are of variable length, up to 255 octets in size. Attributes for the file described by the directory entry are stored in the directory entry itself (unlike UNIX).

In Figure 4, the root-directory entry is a variable-length object, so that the name can be of variable length. (No other part in the directory entry is of variable length.)

Figure 4: Data structure of a CD-ROM filesystem directory entry.

  /* CDROM file system directory entries */

  /* file flags: */
  #define CD_VISABLE  0x01 /* file name is hidden or visable to user */
  #define CD_DIRECTORY  0x02 /* file is a directory and contains entries */
  #define CD_ASSOCIATED  0x04/* file is opaque to filesystem, visable
                                to system implementation */
  #define CD_EAHSFRECORD 0x04 /* file has HSF extended attribute record
                                 fmt */
  #define CD_PROTECTION  0x04 /* used extended attributes for protection */
  #define CD_ANOTHEREXTNT 0x80 /* file has at least one more extent */

  struct iso_directory_record {
       char length         [ISODCL  (1, 1)];
       char ext_attr_length         [ISODCL (2, 2)];
       char extent         [ISODCL  (3, 10)];
       char size           [ISODCL  (11, 18)];
       char date           [ISODCL  (19, 25)];
       char flags          [ISODCL  (26, 26)];
       char file_unit_size      [ISODCL (27, 27)];
       char interleave          [ISODCL (28, 28)];
       char volume_sequence_number  [ISODCL (29, 32)];
       char name_len           [ISODCL  (33, 33)];
       char name           [0];

  }:

  struct hsf_directory_record {

       char length         [HSFDCL (1, 1)];
       char ext_attr_length        [HSFDCL (2, 2)];
       char extent         [HSFDCL (3, 10)];
       char size           [HSFDCL (11, 18)];
       char date           [HSFDCL (19, 24)];
       char flags          [HSFDCL (25, 25)];
       char reserved1          [HSFDCL (26, 26)];
       char interleave_size        [HSFDCL (27, 27)];
       char interleave         [HSFDCL (28, 28)];
       char volume_sequence_number [HSFDCL (29, 32)];
       char name_len           [HSFDCL (33, 33)];
       char name           [0];
  }:

File Attributes

File attributes are very simple in ISO-9660. The most important file attribute is determining whether the file is a directory or an ordinary file. Additional attributes which make the file "invisible" to various programs, but present nonetheless. These are used by some systems to store information adjacent to "visible" files without letting the user note their presence. Data and time stamps also exist for each file.

The ISO-9660 directory entries and attributes do not provide enough information for UNIX file attributes to be reconstructed, and they require the use of extensions (such as the Rock Ridge extensions) to be complete.

Filenames

Filenames in ISO-9660 correspond to a DOS-like representation, with an uppercase, fixed-size base name, a delimiter (a period) to separate filenames from the extension, and a three-letter extension name (also uppercase). Following the extension, you can optionally add on a delimiter (a semicolon) and a revision number of the file. (For example, a typical filename would be FOO.BAR;1.) There are additional restrictions on the type of allowed characters beyond that of alpha characters.

The choice of filename is thus restricted to allow for the vast number of different systems that existed at the time the standard was determined. While the directory entries allow much larger names than this, the characteristics and size of the filename were developed to achieve "level-one" compliance with the original High Sierra format.

Unfortunately, many systems with ISO-9660 capability are not compatible with these naming conventions. (For example, on a UNIX system, a semicolon is used as a command delimiter in the shell, among other things.) Therefore, systems programmers place code within the ISO filesystem to translate the name into something more acceptable. (Again, for UNIX systems, uppercase letters are translated to lower case, semicolons and trailing version numbers are removed, modes are translated, and the ownership of the file is replicated from the UNIX directory on which the ISO-9660 filesystem is mounted.)

File Pathname Traversal

There are two ways to locate a file on an ISO-9660 filesystem. One way is to successively interpret the directory names and look through each directory file structure to find the file (much the way MS-DOS and UNIX work to find a file). The other way is through the use of a precompiled table of paths, where all the entries are enumerated in the successive contents of a file with the corresponding entries. (Since some systems do not have a mechanism for wandering through directories, they obtain a match by consulting the table.)

While a large linear table seems a bit arcane, it can be of great value, as you can quickly search without wandering across the disk (thus reducing seek time).

More Details.

File Contents

The ISO-9660 standard says practically nothing about the contents of files themselves--they can contain any kind of data one wishes to store. (The one exception is that of "extended attributes," which will be discussed in a future article.)

cdromcheck: Decoding a CD-ROM

Listing One (page 114) is cdromcheck, a simple C program that checks the CD-ROM to determine if it is arranged in High Sierra or ISO-9660 filesystem format and lists the volume descriptors present; see Listing Two, page 114. One important difference between High Sierra and ISO-9660 formats is outlined in a listing of the volume descriptor. The first CDROM examined, Rich Morin's "Prime Time Freeware" vol 1.1 CDROM, lists a single ISO-9660 volume descriptor. Discovery System's "CDROM Sampler," on the other hand, lists two High Sierra volume descriptors, varying only in their volume logical-block number. The High Sierra format listed replicated volume descriptors (though not the data itself) in case a volume descriptor was damaged. This practice has since been deemed unnecessary and is absent in the ISO-9660 standard.

There are three areas of difference between ISO-9660 format and High Sierra: the format of the volume descriptors, the constant that signifies the kind of volume descriptor, and a minor change to accommodate a time zone. It's a tribute to the designers of the High Sierra filesystem that so few changes were required to make it a standard.

cdromcat: Viewing a CD-ROM File

Listing Four (cdromcat.c), page 115, is a simple program to interpret the CD-ROM filesystem and return the contents of a file. It consists of three major sections:

  • 1. A user-application section which allows the user to interact with the remainder of cdromcat.
  • 2. The filesystem primitives for the CD-ROM filesystem.
  • 3. The object output routines which format and print the output (in this example, the contents of the PTF CD-ROM examined with cdromcheck). Object output routines are never used within the operating system itself in this manner, but they are a mandatory component of any good user applications program.
The filesystem primitives for the CD-ROM filesystem are the heart of the program. First we check for the presence of a CD-ROM filesystem, then obtain the blocks for a directory entry on the CD-ROM, search the contents of this directory entry for a file, and then lookup the pathname filename (by translating it), locate it, and return as found directory entry.

Throughout cdromcat.c, we use as a handle a machine-independent structure which refers to the named-associated file, with macros allowing for object translation into either ISO-9660 or High Sierra filesystem format. Note that filesystems are excellent vehicles for object-oriented programming (such as coding in C++).

While both ISO-9660 and High Sierra filesystems allow for different byte ordering, the trivial macros used in the header file definitions for our CD-ROM filesystem (cdromfs.h, Listing Three, page 114) assume that cdromcat.c is being run on a Little Endian system, in our case a 386/486 PC. We have left it to the reader to enhance this program to run on a Big Endian (68000 or SPARC) system.

386BSD isofs: ISO-line 9660 Filesystem

A complete implementation of the ISO-9660 filesystem can be found in 386BSD Release 0.1 and all future releases. Initially done by Pace Willison, this implementation allows CD-ROMs to be mounted as if they were an ordinary 386BSD filesystem. After mounting, one can use the shell to browse directories (through the use of the ls and cd commands).

Upon examination of the 386BSD version, it should become quite apparent that writing a filesystem for inclusion in the 386BSD kernel is significantly different from the little applications programs discussed earlier. The code in the ISO-9660 filesystem implementation in 386BSD is really a series of methods to interpret virtual filesystem node (vnode) operations given the underlying virtual filesystem (VFS). These operations are specific kernel interfaces used during all filename translations and operations within the kernel.

The VFS interface itself does not refer in any way to the actual physical device, nor does the code within the ISO-9660 filesystem. This intimacy is exported to the lower I/O filesystem via the block interface, beneath which the decision is made to obtain the information off a device. Since the VFS and the ISO-9660 filesystem code are not device specific (only filesystem specific), you could actually substitute an ordinary disk drive for the CD-ROM drive and they would work just the same.

The level of abstraction is simple and hierarchically arranged. At the top, the kernel system calls deal with the files. Below this lies the VFS, its virtual operations (vops) determining what specific filesystem type is present. Below the VFS lies the filesystem itself, followed by the block I/O interface to the device driver, which actually performs the operations on the device that has the ISO-9660 filesystem recorded. This arrangement allows for a more elegant and flexible mechanism than specific user applications.

386BSD Release 0.1 for the 386/486 AT PC (binary, source, and additional packages) is currently available via anonymous ftp at agate.berkeley.edu (IP address 128.32.136.1) or any of its mirror sites. You can obtain the Tiny 386BSD installation floppy to qualify, partition, download, and install/extract the rest of the distribution as part of the DDJ Careware Program. Simply send a formatted, error-free, high-density (either 3.5- or 5.25-inch) floppy and a SASE mailer to: Tiny 386BSD, Dr. Dobb's Journal, 411 Borel Avenue, San Mateo, CA 94402, and we will make a copy and send it back to you. There is no charge for the service, but if you would like to slip in a dollar or more for the Children's Support League of the East Bay (as part of DDJ's Careware Program), we'll make sure it gets to the disadvantaged children helped by this great charity!

CD-ROM Extensions

In a future article, we'll discuss extensions to CD-ROM, including the Rock Ridge extensions (soon to become an IEEE standard), CDI, and CDROM-XA. With extensions, you can allow a CD-ROM to appear like a given target operating system (such as UNIX filesystem). By encoding these extensions (sharing use protocols), you can also have separate sets of attributes for the same filesystem, thus allowing organization of extended information for different systems (such as VMS and UNIX) in a nonconflicting way. We will also revisit our cdromcat.c program (Listing Four) and revise it to read ISO-9660 CD-ROMs with Rock Ridge extensions.

An Overview of CD-ROM Hardware

CD-ROM drives, like other write-protected disk drives, use a moving head pasing over a rotating media to index and obtain a desired sector of information. One significant difference between a CD-ROM and other drives, however, lies in the access (or seek) time of the drive itself. Originally, CD-ROM drives were built from the same drive mechanisms used in audio products, and it took several seconds to scan across the disk to find a track (still incredibly fast compared to an audio cassette).

Newer, high-end CD-ROM drives allow fractional-second (200-millisecond) seek times--more appropriate for the pace of software applications. These drives are slower than existing Winchester disk drives because the head assemblies weigh more (lens system, laser, and photo diode) and require fine positioning. Another limitation on access time is that data is recorded in a constant bit-rate spiral, which hinders random indexing without additional rotational searching.

Low-end drives accept naked CD-ROMs, just like an ordinary CD player. High-end drives, in contrast, use a plastic carrier to prevent wear from CD-ROM insertions.

Almost all CD-ROM drives offer a stereo audio jack that allows the drive to play ordinary CDs. However, this does not necessarily mean that a CD-ROM drive can be used to read a CD's digitally encoded audio information, although CD-ROMs can contain voice tracks that can be played.

CD-ROM drives are usually inter-faced one of two ways: Early CD-ROMs used a custom interface board sold with the unit; later drives used the Small Computer Systems Interface (SCSI) to access the drive. While custom interfaces (very similar to floppy-disk interfaces) are present to this day in low-end units, SCSI units are becoming more popular as the demands for bandwidth and interconnection displace the cost of a more elaborate interface.

--L.G.J. & W.F.J.

_INSIDE THE ISO-9660 FILESYSTEM FORMAT_
by William Frederick Jolitz and Lynne Greer Jolitz


[LISTING ONE]
<a name="02d5_0016">

/* cdromcheck: A simple program to check what kind of CDROM we have, and to
 * list the volume descriptors that are present, if any. */

#include <stdio.h>
#include "primary_descriptor"
#include "directory_entry"

#define VD_LSN       16   /* first logical sector of volume descriptor table */
#define CDROM_LSECSZ 2048 /* initial logical sector size of a CDROM */

char buffer[CDROM_LBS];
static hsffmt, isofmt;
void doiso(struct iso_primary_descriptor *, int);
void dohsf(struct hsf_primary_descriptor *, int);

#define HSF 1
#define ISO 2
int cdromfmt;

char *cdromfmtnames[] = {
    "unknown format",
    "High Sierra",
    "ISO - 9660"
};
char *voltypenames[] = {
    "Boot Record",
    "Standard File Structure",
    "Coded Character Set File Structure",
    "Unspecified File Structure",
};
#define NVOLTYPENAMES   (sizeof(voltypenames)/sizeof(char *))
int
main(int argc, char *argv[])
{
    struct iso_primary_descriptor *ipd;
    struct hsf_primary_descriptor *hpd;
    int cdfd;

    cdfd = open("/dev/ras2d", 0);
    /* locate at the beginning of the descriptor table */
    lseek(cdfd, VD_LSN*CDROM_LSECSZ, SEEK_SET);
    ipd = (struct iso_primary_descriptor *) buffer;
    hpd = (struct hsf_primary_descriptor *) buffer;
    /* walk descriptor table */
    for(;;) {
        unsigned char type;
        read(cdfd, buffer, sizeof(buffer));
        /* determine ISO or HSF format of CDROM */
        if (cdromfmt == 0) {
          if (strncmp (ipd->id, ISO_STANDARD_ID, sizeof(ipd->id)) == 0)
                cdromfmt = ISO;
          if (strncmp (hpd->id, HSF_STANDARD_ID, sizeof(hpd->id)) == 0)
                cdromfmt = HSF;
          if (cdromfmt)
           printf("%s Volume Descriptors:\n", cdromfmtnames[cdromfmt]);
            else {
                printf("%s\n", cdromfmtnames[0]);
                exit(0);
            }
        }
        /* type of descriptor */
        if (cdromfmt == ISO)
            type = (unsigned char)ipd->type[0];
        else
            type = (unsigned char)hpd->type[0];

        /* type of volume */
        if (type < NVOLTYPENAMES)
            printf("\t%s\n", voltypenames[type]);
        else if (type != VD_END)
            printf("\t Reserved - %d\n", type);

        /* terminating volume */
        if (type == VD_END)
            break;
        /* ISO 9660 filestructure */
        if (cdromfmt == ISO && type == VD_PRIMARY
         && strncmp (ipd->id, ISO_STANDARD_ID, sizeof(ipd->id)) == 0) {
            doiso(ipd, cdfd);
            isofmt++;
            continue;
        }
        /* (obselete) High Sierra filestructure */
        if (cdromfmt == HSF && type == VD_PRIMARY
         && strncmp (hpd->id, HSF_STANDARD_ID, sizeof(hpd->id)) == 0) {
            dohsf(hpd, cdfd);
            hsffmt++;
            continue;
        }
        printf("\n");
    }
    return (0);
}
char *iso_astring(char *, int len);
/* rude translation routines for interpreting strings, words, halfwords */
#define ISO_AS(s)   (iso_astring(s, sizeof(s)))
#define ISO_WD(s)   (*(unsigned *)(s))
#define ISO_HWD(s)  (*(unsigned short *)(s))
/* dig out the details of a ISO - 9660 descriptor */
void
doiso(struct iso_primary_descriptor *ipd, int fd) {
   printf(" Volume ID:\t\t%s\n", ISO_AS(ipd->volume_id));
   printf(" Logical Block Size:\t%d\n", ISO_HWD(ipd->logical_block_size));
   printf(" Volume Set ID:\t\t%s\n", ISO_AS(ipd->volume_set_id));
   printf(" Publisher ID:\t\t%s\n", ISO_AS(ipd->publisher_id));
   printf(" Preparer ID:\t\t%s\n", ISO_AS(ipd->preparer_id));
   printf(" Application ID:\t\t%s\n", ISO_AS(ipd->application_id));
   printf(" Copyright File ID:\t%s\n", ISO_AS(ipd->copyright_file_id));
   printf(" Abstract File ID:\t%s\n", ISO_AS(ipd->abstract_file_id));
   printf(" Bibliographic File ID:\t%s\n", ISO_AS(ipd->bibliographic_file_id));
   printf(" Creation Date:\t\t%s\n", ISO_AS(ipd->creation_date));
   printf(" Modification Date:\t%s\n", ISO_AS(ipd->modification_date));
   printf(" Expiration Date:\t%s\n", ISO_AS(ipd->expiration_date));
   printf(" Effective Date:\t%s\n", ISO_AS(ipd->effective_date));
}
/* dig out the details of a High Sierra Descriptor */
void
dohsf(struct hsf_primary_descriptor *hpd, int fd) {
    printf(" Volume Logical Block Number:\t%d\n", ISO_WD(hpd->volume_lbn));
    printf(" Volume ID:\t\t%s\n", ISO_AS(hpd->volume_id));
    printf(" Logical Block Size:\t%d\n", ISO_HWD(hpd->logical_block_size));
    printf(" Volume Set ID:\t\t%s\n", ISO_AS(hpd->volume_set_id));
    printf(" Publisher ID:\t\t%s\n", ISO_AS(hpd->publisher_id));
    printf(" Preparer ID:\t\t%s\n", ISO_AS(hpd->preparer_id));
    printf(" Application ID:\t\t%s\n", ISO_AS(hpd->application_id));
    printf(" Copyright File ID:\t%s\n", ISO_AS(hpd->copyright_file_id));
    printf(" Abstract File ID:\t%s\n", ISO_AS(hpd->abstract_file_id));
    printf(" Creation Date:\t\t%s\n", ISO_AS(hpd->creation_date));
    printf(" Modification Date:\t%s\n", ISO_AS(hpd->modification_date));
    printf(" Expiration Date:\t%s\n", ISO_AS(hpd->expiration_date));
    printf(" Effective Date:\t%s\n", ISO_AS(hpd->effective_date));
}
static char __strbuf[200];
/* turn a blank padded character feild into the null terminated strings
   that POSIX/UNIX/WHATSIX likes so much */
char *iso_astring(char *sp, int len) {
    bcopy(sp, __strbuf, len);
    __strbuf[len] = 0;
    for (sp = __strbuf + len - 1; sp > __strbuf ; sp--)
        if (*sp == ' ')
            *sp = 0;
    return(__strbuf);
}





<a name="02d5_0017">
<a name="02d5_0018">
[LISTING TWO]
<a name="02d5_0018">

From Rich Morin's "Prime Time Freeware", Vol 1.1 CDROM:

ISO - 9660 Volume Descriptors: Standard File Structure
 Volume ID:     PTF_1_1
 Logical Block Size:    2048
 Volume Set ID:
 Publisher ID:
 Preparer ID:       MERIDIAN_DATA_CD_PUBLISHER
 Application ID:
 Copyright File ID:
 Abstract File ID:
 Bibliographic File ID:
 Creation Date:     1992011101452700
 Modification Date: 1992011101452700
 Expiration Date:   0000000000000000
 Effective Date:    1992011101452700


From Discovery System's CD-ROM Sampler:

High Sierra Volume Descriptors:

    Standard File Structure
 Volume Logical Block Number:   16
 Volume ID:     CDROM_SAMP1
 Logical Block Size:    2048
 Volume Set ID:     CDROM_SAMP1
 Publisher ID:      DISCOVERY
 Preparer ID:       DISCOVERY
 Application ID:
 Copyright File ID:
 Abstract File ID:
 Creation Date:     1987111215553400
 Modification Date: 1987111215553400
 Expiration Date:   0000000000000000
 Effective Date:    0000000000000000

    Standard File Structure
 Volume Logical Block Number:   17
 Volume ID:     CDROM_SAMP1
 Logical Block Size:    2048
 Volume Set ID:     CDROM_SAMP1
 Publisher ID:      DISCOVERY
 Preparer ID:       DISCOVERY
 Application ID:
 Copyright File ID:
 Abstract File ID:
 Creation Date:     1987111215553400
 Modification Date: 1987111215553400
 Expiration Date:   0000000000000000
 Effective Date:    0000000000000000





<a name="02d5_0019">
<a name="02d5_001a">
[LISTING THREE]
<a name="02d5_001a">

/* cdromfs.h: various definitions for CDROM filesystems. */

#define VD_LSN        16  /* first logical sector of volume descriptor table */
#define CDROM_LSECSZ  2048 /* initial logical sector size of a CDROM */

#define HSF 1
#define ISO 2

char *cdromfmtnames[] = {
    "unknown format",
    "High Sierra",
    "ISO - 9660"
};
char *voltypenames[] = {
    "Boot Record",
    "Standard File Structure",
    "Coded Character Set File Structure",
    "Unspecified File Structure",
};
/* rude translation routines for interpreting strings, words, halfwords */
#define ISO_AS(s)   (iso_astring(s, sizeof(s)))
#define ISO_WD(s)   (*(unsigned *)(s))
#define ISO_HWD(s)  (*(unsigned short *)(s))
#define ISO_BY(s)   (*(unsigned char *)(s))

#define NVOLTYPENAMES   (sizeof(voltypenames)/sizeof(char *))
struct cdromtime {
    unsigned char years;    /* number of years since 1900 */
    unsigned char month;    /* month of the year */
    unsigned char day;  /* day of month */
    unsigned char hour; /* hour of day */
    unsigned char min;  /* minute of the hour */
    unsigned char sec;  /* second of the minute */
    unsigned char tz;   /* timezones, in quarter hour increments */
                /* or, longitude in 3.75 of a degree */
};
#define CD_FLAGBITS "vdaEp  m"  /* file flag bits */
/* Handy macro's for block calculation */
#define lbntob(fs, n)   ((fs)->lbs * (n))
#define btolbn(fs, n)   ((fs)->lbs * (n))
#define trunc_lbn(fs, n)    ((n) - ((n) % (fs)->lbs)
#define roundup(n, d)   ((((n) + (d)) / (d)) * (d))






<a name="02d5_001b">
<a name="02d5_001c">
[LISTING FOUR]
<a name="02d5_001c">

/* cdromcat -- A simple program to interpret the CDROM filesystem, and return.
 * the contents of the file (directories are formatted and printed, files are
 * returned untranslated).  */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "primary_descriptor"
#include "directory_entry"
#include "cdromfs.h"

/* per filesystem information */
struct fs {
    char *name; /* kind of cdrom filesystem */
    int fd;     /* open file descriptor */
    int lbs;    /* logical block size */
    int type;   /* which flavor */
} fsd;
/* filesystem directory entry */
struct directent {
    union fsdir {
        struct iso_directory_record iso_dir;
        struct hsf_directory_record hsf_dir;
    } fsd;
    /* actually, name contains name, reserved field, and extensions area */
    char name[255 - sizeof(union fsdir)];
}   rootent;
/* filesystem volume descriptors */
union voldesc {
    struct iso_primary_descriptor  iso_desc;
    struct hsf_primary_descriptor  hsf_desc;
};
char *iso_astring(char *, int len);
char *cdrom_time(struct cdromtime *, int);
void printdirent(struct directent *, struct fs *);
void printdirents(struct directent *, struct fs *);
void printdirentheader(char *p);
int searchdirent(struct directent *, struct directent *, struct directent *,
    struct fs *);
void extractdirent(struct directent *, struct fs *);
int lookup(struct directent *, struct directent *, char *, struct fs *);
/* "fetch directory value" */
#define FDV(b, f, t)    (((t) == ISO) ? (b)->fsd.iso_dir.##f \
                : (b)->fsd.hsf_dir.##f)
/* "fetch primary descriptor value" */
#define FPDV(b, f, t)   (((t) == ISO) ? (b)->iso_desc.##f \
                : (b)->hsf_desc.##f)
/* user "application" program */
int
main(int argc, char *argv[])
{
    struct directent openfile;
    char pathname[80];
    /* open the CDROM device */
    if ((fsd.fd = open("/dev/ras2d", 0)) < 0) {
        perror("cdromcat");
        exit(1);
    }
    /* is there a filesystem we can understand here? */
    if (iscdromfs(&rootent, &fsd) == 0) {
        fprintf(stderr, "cdromcat: %s\n", fsd.name);
        exit(1);
    }
    /* print the contents of the root directory to give user a start */
    printf("Root Directory Listing:\n");
    printdirentheader("/");
    printdirents(&rootent, &fsd);
    /* print files on demand from user */
    for(;;){
        /* prompt user for name to locate */
        printf("Pathname to open? : ");
        fflush(stdout);
        /* obtain, if none, exit, else trim newline off */
        if (fgets(pathname, sizeof(pathname), stdin) == NULL)
            exit(0);
        pathname[strlen(pathname) - 1] = '\0';
        if (strlen(pathname) == 0)
            exit(0);
        /* lookup filename on CDROM */
        if (lookup(&rootent, &openfile, pathname, &fsd)){
            /* if a directory, format and list it */
            if (ISO_BY(FDV(&openfile, flags, fsd.type))
                & CD_DIRECTORY) {
                printdirentheader(pathname);
                printdirents(&openfile, &fsd);
            }
            /* if a file, print it on standard output */
            else
                extractdirent(&openfile, &fsd);
        } else
            printf("Not found.\n");
    }
    /* NOTREACHED */
}
/* ----------- Filesystem primatives ------------------- */
/* Check for the presence of a cdrom filesystem. If present, pass back
 * parameters for initialization, otherwise, pass back error. */
int
iscdromfs(struct directent *dp, struct fs *fs) {
    char buffer[CDROM_LSECSZ];
    union voldesc *vdp = (union voldesc *) buffer;
    /* locate at the beginning of the descriptor table */
    lseek(fs->fd, VD_LSN*CDROM_LSECSZ, SEEK_SET);
    /* walk descriptor table */
    for(;;) {
        unsigned char type;
        /* obtain a descriptor */
        read(fs->fd, buffer, sizeof(buffer));
        /* determine ISO or HSF format of CDROM */
        if (fs->type == 0) {
            if (strncmp (vdp->iso_desc.id, ISO_STANDARD_ID,
                sizeof(vdp->iso_desc.id)) == 0)
                fs->type = ISO;
            if (strncmp (vdp->hsf_desc.id, HSF_STANDARD_ID,
                sizeof(vdp->hsf_desc.id)) == 0)
                fs->type = HSF;
        }
        /* if determined, obtain root directory entry */
        if (fs->type) {
            type = ISO_BY(FPDV(vdp, type, fs->type));
            if (type == VD_PRIMARY) {
                bcopy (
            (caddr_t) FPDV(vdp, root_directory_record, fs->type),
                (caddr_t)dp, sizeof (union fsdir));
            fs->lbs =
                  ISO_HWD(FPDV(vdp, logical_block_size, fs->type));
            }
        }
        /* terminating volume */
        if (type == VD_END)
            break;
    }
    fs->name = cdromfmtnames[fs->type];
    return (fs->type);
}
/* Obtain a "logical", i.e. relative to the directory entries beginning
 * (or extent), block from the CDROM. */
int
getblkdirent(struct directent *dp, char *contents, long lbn, struct fs *fs) {
    long    filesize = ISO_WD(FDV(dp, size, fs->type)),
        extent = ISO_WD(FDV(dp, extent, fs->type));
    if (lbntob(fs, lbn) > roundup(filesize, fs->lbs))
        return (0);
    /* perform logical to physical translation */
    (void) lseek(fs->fd, lbntob(fs, extent + lbn), SEEK_SET);
    /* obtain block */
    return (read(fs->fd, contents, fs->lbs) == fs->lbs);
}
/* Search the contents of this directory entry, known to be a directory itself,
 * looking for a component. If found, return directory entry associated with
 * the component. */
int
searchdirent(struct directent *dp, struct directent *fdp,
    struct directent *compdp, struct fs *fs) {
    struct directent *ldp;
    long    filesize =  ISO_WD(FDV(dp, size, fs->type)),
        comp_namelen =  ISO_BY(FDV(compdp, name_len, fs->type)),
        lbn = 0, cnt;
    char    *buffer = (char *) malloc(fs->lbs);
    while (getblkdirent(dp, buffer, lbn, fs)) {
        cnt = filesize > fs->lbs ? fs->lbs : filesize;
        filesize -= cnt;
        ldp = (struct directent *) buffer;
        /* have we a record to match? */
        while (cnt > sizeof (union fsdir)) {
            long    entlen, namelen;
            /* match against component's name and name length */
            entlen = ISO_BY(FDV(ldp, length, fs->type));
            namelen = ISO_BY(FDV(ldp, name_len, fs->type));
    if (entlen >= comp_namelen + sizeof(union fsdir) && namelen == comp_namelen
                && strncmp(FDV(ldp,name,fs->type),
                FDV(compdp,name,fs->type), namelen) == 0) {
                bcopy ((caddr_t)ldp, (caddr_t)fdp, entlen);
                bcopy ((caddr_t)ldp, (caddr_t)compdp, entlen);
                free(buffer);
                return 1;
            } else {
                cnt -= entlen;
                ldp = (struct directent *)
                    (((char *) ldp) + entlen);
            }
        }
        if (filesize == 0) break;
        lbn++;
    }
    free(buffer);
    return 0;
}
/* Lookup the pathname by interpreting the directory structure of the CDROM
 * element by element, returning a directory entry if found. Name translation
 * occurs here, out of the null terminated path name string. This routine
 * works by recursion. */
int
lookup(struct directent *dp, struct directent *fdp, char *pathname,
    struct fs *fs) {
    struct directent *ldp;
    struct directent thiscomp;
    char *nextcomp;
    unsigned len;
    /* break off the next component of the pathname */
    if ((nextcomp = strrchr(pathname, '/'))  == NULL)
        nextcomp = strrchr(pathname, '\0');
    /* construct an entry for this component to match */
    ISO_BY(FDV(&thiscomp, name_len, fs->type)) = len = nextcomp - pathname;
    bcopy(pathname, thiscomp.name, len);
    /* attempt a match, returning component if found */
    if (searchdirent(dp, fdp, &thiscomp, fs)){
        /* if no more components, return found value */
        if (*nextcomp == '\0')
            return 1;
        /* otherwise, if this component is a directory,
         * recursively satisfy lookup */
        else if (ISO_BY(FDV(dp, flags, fs->type)) & CD_DIRECTORY)
            return (lookup(&thiscomp, fdp, nextcomp + 1, fs));
    }
    /* if no match return fail */
    else
        return(0);
}
/* --------------- object output routines for application ------------ */
/* Extract the entire contents of a directory entry and write this on
 * standard output. */
void
extractdirent(struct directent *dp, struct fs *fs) {
    long    filesize = ISO_WD(FDV(dp, size, fs->type)),
        lbn = 0, cnt;
    char    *buffer = (char *) malloc(fs->lbs);
    /* iterate over all contents of the directory entry */
    while (getblkdirent(dp, buffer, lbn, fs)) {
        /* write out the valid portion of this logical block */
        cnt = filesize > fs->lbs ? fs->lbs : filesize;
        (void) write (1, buffer,  cnt);
        /* next one? */
        lbn++;
        filesize -= cnt;
        if (filesize == 0) break;
    }
    free(buffer);
}
/* Print directory header */
void
printdirentheader(char *path) {
    printf("Directory(%s):\n", path);
    printf("Flags:\tsize date sysa name\n");
}
/* Print all entries in the directory. */
void
printdirents(struct directent *dp, struct fs *fs) {
    struct directent *ldp;
    long    filesize = ISO_WD(FDV(dp, size, fs->type)),
        lbn = 0, cnt;
    char    *buffer = (char *) malloc(fs->lbs);
    while (getblkdirent(dp, buffer, lbn, fs)) {
        long    entlen, namelen;
        cnt = filesize > fs->lbs ? fs->lbs : filesize;
        filesize -= cnt;
        ldp = (struct directent *) buffer;
        entlen = ISO_BY(FDV(ldp, length, fs->type));
        namelen = ISO_BY(FDV(ldp, name_len, fs->type));
        /* have we a record to match? */
        while (cnt > sizeof (union fsdir) && entlen && namelen) {
            printdirent(ldp, fs);
            /* next entry? */
            cnt -= entlen;
            ldp = (struct directent *) (((char *) ldp) + entlen);
            entlen = ISO_BY(FDV(ldp, length, fs->type));
            namelen = ISO_BY(FDV(ldp, name_len, fs->type));
        }
        if (filesize == 0) break;
        lbn++;
    }
    free(buffer);
}
/* Print a directent on output, formatted. */
void
printdirent(struct directent *dp, struct fs *fs) {
    unsigned extattlen;
    unsigned fbname, name_len, entlen, enttaken;
    /* mode flags */
    prmodes(ISO_BY(FDV(dp, flags, fs->type)));
/* Note: this feature of HSF is not used because of lack of semantic def. */
#ifdef whybother
    extattlen = ISO_BY(FDV(dp, ext_attr_length, fs->type));
    if (extattlen)
        printf(" E%3d", extattlen);
    else
        printf("     ");
#endif
    /* size */
    printf("\t%6d", ISO_WD(FDV(dp, size, fs->type)));
    /* time */
    printf(" %s",
       cdrom_time((struct cdromtime *) FDV(dp, date, fs->type),fs->type));
    /* compensate for reserved field used to word align directory entry */
    entlen = ISO_BY(FDV(dp, length, fs->type));
    name_len = ISO_BY(FDV(dp, name_len, fs->type));
    enttaken = sizeof(union fsdir) + name_len;
    if (enttaken & 1)
        enttaken++;
    fbname = ISO_BY(FDV(dp, name, fs->type));
    entlen -= enttaken;
    /* print size of CDROM Extensions field if present */
    if (entlen)
        printf(" %3d", entlen);
    else
        printf("    ");
    /* finally print name. compensate for unprintable names */
    if (name_len == 1 && fbname <= 1) {
        printf("\t%s\n", (fbname == 0) ? "." : "..");
    } else
        printf("\t%s\n",
            iso_astring(FDV(dp, name, fs->type), name_len));
};
/* print CDROM file modes */
prmodes(f) {
    int i;
    for(i=0; i < 8; i++) {
        if(CD_FLAGBITS[i] == ' ')
            continue;
        if(f & (1<<i))
            putchar(CD_FLAGBITS[i]);
        else
            putchar('-');
    }
    putchar(' ');
}
/* attempt to print a CDROM file's creation time */
char *
cdrom_time(struct cdromtime *crt, int type) {
    struct tm tm;
    static char buf[32];
    char *fmt;
    /* step 1. convert into a ANSI C time description */
    tm.tm_sec = crt->sec;
    tm.tm_min = crt->min;
    tm.tm_hour = crt->hour;
    tm.tm_mday = crt->day;
    /* month starts with 1 */
    tm.tm_mon = crt->month - 1;
    tm.tm_year = crt->years;
    tm.tm_isdst = 0;
/* Note: not all ISO-9660 disks have correct timezone field */
#ifdef whybother
    /* ISO has time zone as 7th octet, HSF does not */
    if (type == ISO) {
        tm.tm_gmtoff = crt->tz*15*60;
        tm.tm_zone = timezone(crt->tz*15, 0);
        fmt = "%b %e %H:%M:%S %Z %Y";
    } else
#endif
        fmt = "%b %e %H:%M:%S %Y";
    /* step 2. use ANSI C standard function to format time properly */
    (void)strftime(buf, sizeof(buf), fmt, &tm);
    return (buf);
}
static char __strbuf[200];
/* turn a blank padded character field into the null terminated strings
   that POSIX/UNIX/WHATSIX likes so much */
char *iso_astring(char *sp, int len) {
    bcopy(sp, __strbuf, len);
    __strbuf[len] = 0;
    for (sp = __strbuf + len - 1; sp > __strbuf ; sp--)
        if (*sp == ' ')
            *sp = 0;
    return(__strbuf);
}








<a name="02d5_001d">
<a name="02d5_001e">
[LISTING FIVE]
<a name="02d5_001e">

bill 1 % cdromcat
Root Directory Listing:
Directory(/):
Flags:   size date sysa name
-d----      2048 Jul 27 12:49:16 1992       .
-d----      2048 Jul 27 12:49:16 1992       ..
------    394127 Jul 24 01:21:06 1992       0.ALL;1
------    289565 Jul 24 01:21:06 1992       0.ASK;1
------      2454 Jul 23 23:57:56 1992       0.DOC;1
-d----      2048 Jul 27 12:50:06 1992       A2Z
-d----      2048 Jul 27 12:50:07 1992       AI
-d----      2048 Jul 27 12:50:07 1992       ARCHIVE
-d----      2048 Jul 27 12:50:08 1992       CAD
-d----      2048 Jul 27 12:50:08 1992       DATABASE
-d----      2048 Jul 27 12:50:08 1992       DATACOMM
-d----      2048 Jul 27 12:50:10 1992       DESKTOP
-d----      2048 Jul 27 12:50:10 1992       DOCPREP
-d----      2048 Jul 27 12:50:13 1992       GAME
-d----      2048 Jul 27 12:50:13 1992       GRAPHICS
-d----      2048 Jul 27 12:50:16 1992       LANGUAGE
-d----      2048 Jul 27 12:50:27 1992       MATH
-d----      2048 Jul 27 12:50:27 1992       MISC
-d----      2048 Jul 27 12:50:28 1992       MUSIC
-d----      2048 Jul 27 12:50:29 1992       OS
-d----      2048 Jul 27 12:50:29 1992       PGM_TOOL
-d----      2048 Jul 27 12:50:30 1992       SCIENCE
Pathname to open? : 0.DOC;1
Topic:      (/)

Description:   This is the top level directory of the PTF disc.

Notes:
<... contents of file ... >

Pathname to open? : OS
Directory(OS):
Flags:   size date sysa name
-d----      2048 Jul 27 12:50:29 1992       .
-d----      2048 Jul 27 12:49:16 1992       ..
------       593 Jul 23 23:53:40 1992       0.DOC;1
-d----      2048 Jul 27 12:50:29 1992       CONDOR
-d----      2048 Jul 27 12:50:29 1992       MACH
-d----      2048 Jul 27 12:50:29 1992       MDQS
-d----      2048 Jul 27 12:50:29 1992       PLAN_9
Pathname to open? : OS/PLAN_9
Directory(OS/PLAN_9):
Flags:   size date sysa name
-d----      2048 Jul 27 12:50:29 1992       .
-d----      2048 Jul 27 12:50:29 1992       ..
------       732 Jul 23 23:41:38 1992       0.DOC;1
------        31 Jul 23 23:03:30 1992       0.LST;1
------    230093 Jul 23 23:03:30 1992       PAPERS.ATZ;1
------       472 Jul 23 23:03:30 1992       PAPERS.LTV;1
Pathname to open? :
bill 2 %














Copyright © 1992, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.