_INSIDE THE ISO-966 FILESYSTEM FORMAT_ by William Frederick Jolitz and Lynne Greer Jolitz [LISTING ONE] /* cdromcheck: A simple program to check what kind of CDROM we have, and to * list the volume descriptors that are present, if any. */ #include #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); } [LISTING TWO] 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 [LISTING THREE] /* 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)) [LISTING FOUR] /* 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 #include #include #include #include #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<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); } [LISTING FIVE] 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 %