/* Checkcd.c: * * Performs consistency checks on a ISO 9660 CD. * * ---------------------------------------------------------------------- * This code is (C) Copyright 1994 by Frank Munkert. * All rights reserved. * This software may be freely distributed and redistributed for * non-commercial purposes, provided this notice is included. * ---------------------------------------------------------------------- * History: * */ #include #include #include #include #include #include #include #include "cdrom.h" #include "iso9660.h" #include "rock.h" #ifdef LATTICE #include #include #include extern struct Library *SysBase, *DOSBase; #endif #define STD_BUFFERS 20 #define FILE_BUFFERS 2 CDROM *g_cd = NULL; char g_the_device[80]; int g_the_unit; t_ulong g_memory_type = MEMF_CHIP; t_ulong g_check_sector; char *g_check_name; prim_vol_desc g_pvd; int g_path_table_records = 0; struct Library *UtilityBase; #define TU(x,o) (t_ulong)(((unsigned char *)(x))[o]) #define GET721(x) (TU(x,0) + (TU(x,1) << 8)) #define GET722(x) (TU(x,1) + (TU(x,0) << 8)) #define GET731(x) (TU(x,0) + (TU(x,1) << 8) + (TU(x,2) << 16) + (TU(x,3) << 24)) #define GET732(x) (TU(x,3) + (TU(x,2) << 8) + (TU(x,1) << 16) + (TU(x,0) << 24)) /* Check consistency of a 7.2.3 field: */ void Check_723 (void *p_buf, int p_offset) { t_uchar *buf = (t_uchar *) (p_buf) + (p_offset - 1); t_ulong l = buf[0] + (buf[1] << 8); t_ulong m = buf[3] + (buf[2] << 8); if (l != m) { printf ("ERROR: %s, sector %lu, offset %d - not recorded according to 7.2.3\n", g_check_name, g_check_sector, p_offset); } } /* Check consistency of a 7.3.3 field: */ void Check_733 (void *p_buf, int p_offset) { t_uchar *buf = (t_uchar *) p_buf + (p_offset - 1); t_ulong l = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24); t_ulong m = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24); if (l != m) { printf ("ERROR: %s, sector %lu, offset %d - not recorded according to 7.3.3\n", g_check_name, g_check_sector, p_offset); } } /* Check optional path tables: */ void Check_Optional_Path_Tables (void) { t_ulong loc1, loc2; t_uchar *buf1; t_uchar *buf2; int i; for (i=0; i<=1; i++) { int remain = g_pvd.path_size_m; if (i == 0) loc1 = GET731((char*) &g_pvd.l_table), loc2 = GET731((char *) &g_pvd.opt_l_table); else loc1 = g_pvd.m_table, loc2 = g_pvd.opt_m_table; if (!loc2) continue; for (;;) { if (!Read_Sector (g_cd, loc1)) { printf ("ERROR: illegal sector %lu\n", loc1); exit (1); } buf1 = g_cd->buffer; if (!Read_Sector (g_cd, loc2)) { printf ("ERROR: illegal sector %lu\n", loc2); exit (1); } buf2 = g_cd->buffer; if (memcmp (buf1, buf2, remain > 2048 ? 2048 : remain) != 0) { printf ("ERROR: %c path table and optional %c path table differ" " in sectors %lu and %lu\n", i ? 'M' : 'L', i ? 'M' : 'L', loc1, loc2); } if (remain > 2048) remain -= 2048; else break; loc1++, loc2++; } } } /* Get the path table record in sector p_loc with offset *p_offset. */ void Get_Path_Table_Record (t_uchar *p_buf, t_ulong p_loc, t_ulong *p_offset) { t_ulong sector = p_loc + (*p_offset >> 11); int len; if (!Read_Sector (g_cd, sector)) { printf ("ERROR: illegal sector %lu\n", sector); exit (1); } len = g_cd->buffer[*p_offset & 2047] + 8; if (len & 1) len++; if (len + (*p_offset & 2047) > 2048) { int part1_len = 2048 - (*p_offset & 2047); memcpy (p_buf, g_cd->buffer + (*p_offset & 2047), part1_len); if (!Read_Sector (g_cd, sector+1)) { printf ("ERROR: illegal sector %lu\n", sector+1); exit (1); } memcpy (p_buf + part1_len, g_cd->buffer, len - part1_len); } else memcpy (p_buf, g_cd->buffer + (*p_offset & 2047), len); *p_offset += len; } /* Test whether the L and the M path tables contain the same information: */ void Compare_L_And_M_Path_Table (void) { t_ulong loc1 = GET731((char*) &g_pvd.l_table); t_ulong loc2 = g_pvd.m_table; t_ulong offset1 = 0; t_ulong offset2 = 0; static t_uchar buf1[256]; static t_uchar buf2[256]; while (offset1 < g_pvd.path_size_m) { t_ulong tmp = offset1; Get_Path_Table_Record (buf1, loc1, &offset1); Get_Path_Table_Record (buf2, loc2, &offset2); if (offset1 != offset2) { printf ("ERROR: Length mismatch in L and M path table at offset %lu\n", tmp); return; } if (buf1[1] != buf2[1]) printf ("ERROR: Extended attribute record lengths differ in L and M" " path table at offset %lu\n", offset1); if (memcmp (buf1+8, buf2+8, buf1[0]) != 0) printf ("ERROR: directory identifiers differ in L and M path table " "at offset %lu\n", offset1); if (GET731 (buf1+2) != GET732 (buf2+2)) printf ("ERROR: extent locations differ in L and M path table at" " offset %lu\n", offset1); if (GET721 (buf1+6) != GET722 (buf2+6)) printf ("ERROR: parent directory numbers differ in L and M path table at" " offset %lu\n", offset1); g_path_table_records++; } } /* Check consistency of path table and directory records: */ void Compare_Path_Table_With_Directory_Records (void) { t_ulong loc = g_pvd.m_table; t_ulong offset = 0; static t_uchar buf[256]; int rec = 1; VOLUME *vol; CDROM_OBJ *obj; CDROM_INFO info; t_ulong pos; t_bool warn_case = 0; vol = Open_Volume (g_cd, 0); if (!vol) { printf ("ERROR: cannot open volume\n"); exit (10); } for (; offset < g_pvd.path_size_m; rec++) { t_ulong tmp = offset; if ((rec & 7) == 1) { printf ("\r (%d of %d)", rec, g_path_table_records); fflush (stdout); } Get_Path_Table_Record (buf, loc, &offset); /* skip root record: */ if (rec == 1) continue; pos = GET732 (buf+2); obj = Iso_Create_Directory_Obj (vol, pos); if (!obj || !CDROM_Info (obj, &info)) { printf ("\nERROR: cannot get information for directory at location %lu\n" "(Path table offset = %lu)\n", pos, tmp); exit (10); } if (info.name_length != buf[0] || Strnicmp ((UBYTE*) info.name, (UBYTE*) buf+8, info.name_length) != 0) { printf ("\nERROR: names in path table and directory record differ for " "directory at location %lu\n", pos); printf ("Name in path table = "); fwrite (buf+8, 1, info.name_length, stdout); printf ("\nName in directory record = "); fwrite (info.name, 1, info.name_length, stdout); putchar ('\n'); } else if (!warn_case && memcmp (info.name, buf+8, info.name_length) != 0) { printf ("\nWARNING: Directory names in path table and directory records " "differ in case.\n"); warn_case = 1; } Close_Object (obj); } printf ("\r%75s\r", ""); Close_Volume (vol); } /* Check optional path tables: */ void Check_Path_Tables (void) { Check_Optional_Path_Tables (); Compare_L_And_M_Path_Table (); } /* Check primary volume descriptor: */ void Check_PVD (void) { int loc = 16; int pvd_pos = 0; prim_vol_desc *pvd; do { if (!Read_Sector (g_cd, loc)) { printf ("ERROR: illegal sector %d\n", loc); exit (1); } pvd = (prim_vol_desc *) g_cd->buffer; if (memcmp (pvd->id, "CD001", 5) != 0) { printf ("ERROR: missing standard identifier at sector %d\n", loc); exit (10); } if (pvd->type > 4 && pvd->type < 255) printf ("WARNING: unknown volume descriptor type at sector %d\n", loc); if (pvd->version != 1) printf ("WARNING: unknown volume descriptor version at sector %d\n", loc); if (pvd->type == 1 && !pvd_pos) pvd_pos = loc; loc++; } while (pvd->type != 255); if (!Read_Sector (g_cd, pvd_pos)) { printf ("ERROR: illegal sector %d\n", loc); exit (1); } pvd = (prim_vol_desc *) g_cd->buffer; g_check_name = "primary volume descriptor"; g_check_sector = pvd_pos; Check_733 (pvd, 81); Check_723 (pvd, 121); Check_723 (pvd, 125); Check_723 (pvd, 129); Check_733 (pvd, 133); memcpy (&g_pvd, pvd, sizeof (g_pvd)); } void Check_Subdirectory (CDROM_OBJ *p_home, char *p_name) { CDROM_OBJ *obj; CDROM_INFO info; /* VOLUME *vol = p_home->volume; */ printf (" %s\r", p_name); fflush (stdout); if (obj = Open_Object (p_home, p_name)) { unsigned long offset = 0; while (Examine_Next (obj, &info, &offset)) { directory_record *dir = info.suppl_info; g_check_name = "directory record"; g_check_sector = offset; Check_733 (dir, 3); Check_733 (dir, 11); Check_723 (dir, 29); } Close_Object (obj); } else { printf ("ERROR: Object '%s': iso_errno = %d\n", p_name, iso_errno); return; } if (obj = Open_Object (p_home, p_name)) { unsigned long offset = 0; while (Examine_Next (obj, &info, &offset)) { if (info.directory_f) { char *name = malloc (strlen (p_name) + info.name_length + 2); int len; if (!name) { fprintf (stderr, "out of memory\n"); exit (10); } if (Is_Top_Level_Object (obj)) name[0] = 0; else sprintf (name, "%s/", p_name); len = strlen (name) + info.name_length; memcpy (name + strlen (name), info.name, info.name_length); name[len] = 0; Check_Subdirectory (p_home, name); free (name); } } Close_Object (obj); } else { printf ("ERROR: Object '%s': iso_errno = %d\n", p_name, iso_errno); } printf (" %*s\r", strlen (p_name), ""); } void Check_Directories (void) { VOLUME *vol; CDROM_OBJ *home; if (!(vol = Open_Volume (g_cd, 1))) { printf ("ERROR: cannot open volume; iso_errno = %d\n", iso_errno); exit (10); } if (!(home = Open_Top_Level_Directory (vol))) { printf ("ERROR: cannot open top level directory; iso_errno = %d\n", iso_errno); Close_Volume (vol); exit (1); } Check_Subdirectory (home, ":"); Close_Object (home); Close_Volume (vol); } void Cleanup (void) { if (g_cd) Cleanup_CDROM (g_cd); if (UtilityBase) CloseLibrary (UtilityBase); } int Get_Device_And_Unit (void) { int len; char buf[10]; len = GetVar ((UBYTE *) "CDROM_DEVICE", (UBYTE *) g_the_device, sizeof (g_the_device), 0); if (len < 0) return 0; if (len >= sizeof (g_the_device)) { fprintf (stderr, "CDROM_DEVICE too long\n"); exit (1); } g_the_device[len] = 0; len = GetVar ((UBYTE *) "CDROM_UNIT", (UBYTE *) buf, sizeof (buf), 0); if (len < 0) return 0; if (len >= sizeof (buf)) { fprintf (stderr, "CDROM_UNIT too long\n"); exit (1); } buf[len] = 0; g_the_unit = atoi (buf); if (GetVar ((UBYTE *) "CDROM_FASTMEM", (UBYTE *) buf, sizeof (buf), 0) > 0) { fprintf (stderr, "using fastmem\n"); g_memory_type = MEMF_FAST; } return 1; } void main (int argc, char *argv[]) { atexit (Cleanup); if (!(UtilityBase = (struct Library *) OpenLibrary ((UBYTE *) "utility.library", 37))) { fprintf (stderr, "cannot open utility.library\n"); exit (1); } if (!Get_Device_And_Unit ()) { fprintf (stderr, "Please set the following environment variables:\n" " CDROM_DEVICE name of SCSI device\n" " CDROM_UNIT unit number of CDROM drive\n" "e.g.\n" " setenv CDROM_DEVICE scsi.device\n" " setenv CDROM_UNIT 2\n" "Set the variable CDROM_FASTMEM to any value if you\n" "want to use fast memory for SCSI buffers (does not work\n" "with all SCSI devices!)\n" ); exit (1); } g_ignore_blocklength = TRUE; g_cd = Open_CDROM (g_the_device, g_the_unit, 1, g_memory_type, STD_BUFFERS, FILE_BUFFERS); if (!g_cd) { fprintf (stderr, "cannot open CDROM, error code = %d\n", g_cdrom_errno); exit (1); } printf ("Checking primary volume descriptor...\n"); Check_PVD (); printf ("Checking path tables...\n"); Check_Path_Tables (); printf ("Comparing path table with directory records...\n"); Compare_Path_Table_With_Directory_Records (); printf ("Checking directories...\n"); Check_Directories (); printf ("All checks completed.\n"); exit (0); }