3 * Performs consistency checks on a ISO 9660 CD.
5 * ----------------------------------------------------------------------
6 * This code is (C) Copyright 1994 by Frank Munkert.
7 * (C) Copyright 2007 by Pavel Fedin.
9 * This software may be freely distributed and redistributed for
10 * non-commercial purposes, provided this notice is included.
11 * ----------------------------------------------------------------------
14 * 06-Mar-09 error - Removed madness, fixed insanity. Cleanup started
15 * 18-Aug-07 sonic - Now builds on AROS.
16 * 08-Apr-07 sonic - Removed "TRACKDISK" option.
17 * - Removed dealing with block length.
18 * 31-Mar-07 sonic Added support for Joliet and character set translation
27 #include <proto/dos.h>
28 #include <proto/exec.h>
29 #include <proto/utility.h>
38 #include <pragmas/dos_pragmas.h>
39 #include <pragmas/exec_pragmas.h>
40 #include <pragmas/utility_pragmas.h>
41 extern struct Library
*SysBase
, *DOSBase
;
44 #define STD_BUFFERS 20
45 #define FILE_BUFFERS 2
47 static struct CDVDBase
*global
;
48 t_ulong g_check_sector
;
51 int g_path_table_records
= 0;
53 #define TU(x,o) (t_ulong)(((unsigned char *)(x))[o])
55 #define GET721(x) (TU(x,0) + (TU(x,1) << 8))
56 #define GET722(x) (TU(x,1) + (TU(x,0) << 8))
57 #define GET731(x) (TU(x,0) + (TU(x,1) << 8) + (TU(x,2) << 16) + (TU(x,3) << 24))
58 #define GET732(x) (TU(x,3) + (TU(x,2) << 8) + (TU(x,1) << 16) + (TU(x,0) << 24))
60 #define GET721(x) (TU(x,1) + (TU(x,0) << 8))
61 #define GET722(x) (TU(x,0) + (TU(x,1) << 8))
62 #define GET731(x) (TU(x,3) + (TU(x,2) << 8) + (TU(x,1) << 16) + (TU(x,0) << 24))
63 #define GET732(x) (TU(x,0) + (TU(x,1) << 8) + (TU(x,2) << 16) + (TU(x,3) << 24))
66 /* Check consistency of a 7.2.3 field: */
68 void Check_723 (void *p_buf
, int p_offset
)
70 t_uchar
*buf
= (t_uchar
*) (p_buf
) + (p_offset
- 1);
71 t_ulong l
= buf
[0] + (buf
[1] << 8);
72 t_ulong m
= buf
[3] + (buf
[2] << 8);
74 printf ("ERROR: %s, sector %lu, offset %d - not recorded according to 7.2.3\n",
75 g_check_name
, (unsigned long)g_check_sector
, p_offset
);
79 /* Check consistency of a 7.3.3 field: */
81 void Check_733 (void *p_buf
, int p_offset
)
83 t_uchar
*buf
= (t_uchar
*) p_buf
+ (p_offset
- 1);
84 t_ulong l
= buf
[0] + (buf
[1] << 8) + (buf
[2] << 16) + (buf
[3] << 24);
85 t_ulong m
= buf
[7] + (buf
[6] << 8) + (buf
[5] << 16) + (buf
[4] << 24);
87 printf ("ERROR: %s, sector %lu, offset %d - not recorded according to 7.3.3\n",
88 g_check_name
, (unsigned long)g_check_sector
, p_offset
);
92 /* Check optional path tables: */
94 void Check_Optional_Path_Tables (void)
101 for (i
=0; i
<=1; i
++) {
103 int remain
= g_pvd
.path_size
;
106 loc1
= GET731((char*) &g_pvd
.l_table
),
107 loc2
= GET731((char *) &g_pvd
.opt_l_table
);
109 loc1
= g_pvd
.table
, loc2
= g_pvd
.opt_table
;
115 if (!Read_Chunk (global
->g_cd
, loc1
)) {
116 printf ("ERROR: illegal sector %lu\n", (unsigned long)loc1
);
119 buf1
= global
->g_cd
->buffer
;
121 if (!Read_Chunk (global
->g_cd
, loc2
)) {
122 printf ("ERROR: illegal sector %lu\n", (unsigned long)loc2
);
125 buf2
= global
->g_cd
->buffer
;
127 if (memcmp (buf1
, buf2
, remain
> 2048 ? 2048 : remain
) != 0) {
128 printf ("ERROR: %c path table and optional %c path table differ"
129 " in sectors %lu and %lu\n",
130 i
? 'M' : 'L', i
? 'M' : 'L', (unsigned long)loc1
, (unsigned long)loc2
);
143 /* Get the path table record in sector p_loc with offset *p_offset. */
145 void Get_Path_Table_Record (t_uchar
*p_buf
, t_ulong p_loc
, t_ulong
*p_offset
)
147 t_ulong sector
= p_loc
+ (*p_offset
>> 11);
150 if (!Read_Chunk (global
->g_cd
, sector
)) {
151 printf ("ERROR: illegal sector %lu\n", (unsigned long)sector
);
154 len
= global
->g_cd
->buffer
[*p_offset
& 2047] + 8;
158 if (len
+ (*p_offset
& 2047) > 2048) {
159 int part1_len
= 2048 - (*p_offset
& 2047);
160 memcpy (p_buf
, global
->g_cd
->buffer
+ (*p_offset
& 2047), part1_len
);
161 if (!Read_Chunk (global
->g_cd
, sector
+1)) {
162 printf ("ERROR: illegal sector %lu\n", (unsigned long)(sector
+1));
165 memcpy (p_buf
+ part1_len
, global
->g_cd
->buffer
, len
- part1_len
);
167 memcpy (p_buf
, global
->g_cd
->buffer
+ (*p_offset
& 2047), len
);
172 /* Test whether the L and the M path tables contain the same information: */
174 void Compare_L_And_M_Path_Table (void)
176 t_ulong loc1
= GET731((char*) &g_pvd
.l_table
);
177 t_ulong loc2
= g_pvd
.table
;
180 static t_uchar buf1
[256];
181 static t_uchar buf2
[256];
183 while (offset1
< g_pvd
.path_size
) {
185 t_ulong tmp
= offset1
;
187 Get_Path_Table_Record (buf1
, loc1
, &offset1
);
188 Get_Path_Table_Record (buf2
, loc2
, &offset2
);
190 if (offset1
!= offset2
) {
191 printf ("ERROR: Length mismatch in L and M path table at offset %lu\n",
196 if (buf1
[1] != buf2
[1])
197 printf ("ERROR: Extended attribute record lengths differ in L and M"
198 " path table at offset %lu\n", (unsigned long)offset1
);
200 if (memcmp (buf1
+8, buf2
+8, buf1
[0]) != 0)
201 printf ("ERROR: directory identifiers differ in L and M path table "
202 "at offset %lu\n", (unsigned long)offset1
);
204 if (GET731 (buf1
+2) != GET732 (buf2
+2))
205 printf ("ERROR: extent locations differ in L and M path table at"
206 " offset %lu\n", (unsigned long)offset1
);
208 if (GET721 (buf1
+6) != GET722 (buf2
+6))
209 printf ("ERROR: parent directory numbers differ in L and M path table at"
210 " offset %lu\n", (unsigned long)offset1
);
212 g_path_table_records
++;
216 /* Check consistency of path table and directory records: */
218 void Compare_Path_Table_With_Directory_Records (void)
220 t_ulong loc
= g_pvd
.table
;
222 static t_uchar buf
[256];
228 t_bool warn_case
= 0;
230 vol
= Open_Volume (global
->g_cd
, 0, 0);
232 printf ("ERROR: cannot open volume\n");
236 for (; offset
< g_pvd
.path_size
; rec
++) {
237 t_ulong tmp
= offset
;
239 if ((rec
& 7) == 1) {
240 printf ("\r (%d of %d)",
241 rec
, g_path_table_records
);
244 Get_Path_Table_Record (buf
, loc
, &offset
);
245 /* skip root record: */
248 pos
= GET732 (buf
+2);
249 obj
= Iso_Create_Directory_Obj (vol
, pos
);
250 if (!obj
|| !CDROM_Info (obj
, &info
)) {
251 printf ("\nERROR: cannot get information for directory at location %lu\n"
252 "(Path table offset = %lu)\n", (unsigned long)pos
, (unsigned long)tmp
);
255 if (info
.name_length
!= buf
[0] ||
256 Strnicmp ((UBYTE
*) info
.name
, (UBYTE
*) buf
+8, info
.name_length
) != 0) {
257 printf ("\nERROR: names in path table and directory record differ for "
258 "directory at location %lu\n", (unsigned long)pos
);
259 printf ("Name in path table = ");
260 fwrite (buf
+8, 1, info
.name_length
, stdout
);
261 printf ("\nName in directory record = ");
262 fwrite (info
.name
, 1, info
.name_length
, stdout
);
264 } else if (!warn_case
&& memcmp (info
.name
, buf
+8, info
.name_length
) != 0) {
265 printf ("\nWARNING: Directory names in path table and directory records "
266 "differ in case.\n");
271 printf ("\r%75s\r", "");
275 /* Check optional path tables: */
277 void Check_Path_Tables (void)
279 Check_Optional_Path_Tables ();
280 Compare_L_And_M_Path_Table ();
283 /* Check primary volume descriptor: */
285 void Check_PVD (void)
292 if (!Read_Chunk (global
->g_cd
, loc
)) {
293 printf ("ERROR: illegal sector %d\n", loc
);
296 pvd
= (prim_vol_desc
*) global
->g_cd
->buffer
;
297 if (memcmp (pvd
->id
, "CD001", 5) != 0) {
298 printf ("ERROR: missing standard identifier at sector %d\n", loc
);
301 if (pvd
->type
> 4 && pvd
->type
< 255)
302 printf ("WARNING: unknown volume descriptor type at sector %d\n", loc
);
303 if (pvd
->version
!= 1)
304 printf ("WARNING: unknown volume descriptor version at sector %d\n", loc
);
305 if (pvd
->type
== 1 && !pvd_pos
)
308 } while (pvd
->type
!= 255);
310 if (!Read_Chunk (global
->g_cd
, pvd_pos
)) {
311 printf ("ERROR: illegal sector %d\n", loc
);
314 pvd
= (prim_vol_desc
*) global
->g_cd
->buffer
;
315 g_check_name
= "primary volume descriptor";
316 g_check_sector
= pvd_pos
;
318 Check_723 (pvd
, 121);
319 Check_723 (pvd
, 125);
320 Check_723 (pvd
, 129);
321 Check_733 (pvd
, 133);
322 memcpy (&g_pvd
, pvd
, sizeof (g_pvd
));
325 void Check_Subdirectory (CDROM_OBJ
*p_home
, char *p_name
)
329 /* VOLUME *vol = p_home->volume; */
331 printf (" %s\r", p_name
);
333 obj
= Open_Object (p_home
, p_name
);
337 while (Examine_Next (obj
, &info
, &offset
)) {
338 directory_record
*dir
= info
.suppl_info
;
339 g_check_name
= "directory record";
340 g_check_sector
= offset
;
348 printf ("ERROR: Object '%s': iso_errno = %d\n", p_name
, global
->iso_errno
);
351 obj
= Open_Object (p_home
, p_name
);
355 while (Examine_Next (obj
, &info
, &offset
)) {
356 if (info
.directory_f
) {
357 char *name
= malloc (strlen (p_name
) + info
.name_length
+ 2);
360 fprintf (stderr
, "out of memory\n");
363 if (Is_Top_Level_Object (obj
))
366 sprintf (name
, "%s/", p_name
);
367 len
= strlen (name
) + info
.name_length
;
368 memcpy (name
+ strlen (name
), info
.name
, info
.name_length
);
370 Check_Subdirectory (p_home
, name
);
376 printf ("ERROR: Object '%s': iso_errno = %d\n", p_name
, global
->iso_errno
);
379 printf (" %*s\r", (int)strlen (p_name
), "");
382 void Check_Directories (void)
387 if (!(vol
= Open_Volume (global
->g_cd
, 1, 1))) {
388 printf ("ERROR: cannot open volume; iso_errno = %d\n", global
->iso_errno
);
392 if (!(home
= Open_Top_Level_Directory (vol
))) {
393 printf ("ERROR: cannot open top level directory; iso_errno = %d\n", global
->iso_errno
);
398 Check_Subdirectory (home
, ":");
407 Cleanup_CDROM (global
->g_cd
);
410 CloseLibrary(CodesetsBase
);
412 CloseLibrary ((APTR
)UtilityBase
);
415 int Get_Device_And_Unit (void)
420 len
= GetVar ((UBYTE
*) "CDROM_DEVICE", (UBYTE
*) global
->g_device
,
421 sizeof (global
->g_device
), 0);
424 if (len
>= sizeof (global
->g_device
)) {
425 fprintf (stderr
, "CDROM_DEVICE too long\n");
428 global
->g_device
[len
] = 0;
430 len
= GetVar ((UBYTE
*) "CDROM_UNIT", (UBYTE
*) buf
,
434 if (len
>= sizeof (buf
)) {
435 fprintf (stderr
, "CDROM_UNIT too long\n");
439 global
->g_unit
= atoi (buf
);
441 if (GetVar ((UBYTE
*) "CDROM_FASTMEM", (UBYTE
*) buf
,
442 sizeof (buf
), 0) > 0) {
443 fprintf (stderr
, "using fastmem\n");
444 global
->g_memory_type
= MEMF_FAST
;
450 int main (int argc
, char *argv
[])
452 struct CDVDBase
*global
;
454 global
= AllocMem(sizeof(*global
), MEMF_CLEAR
| MEMF_PUBLIC
);
456 return ERROR_NO_FREE_STORE
;
459 global
->g_memory_type
= MEMF_CHIP
;
462 if (!(UtilityBase
= (APTR
)OpenLibrary ("utility.library", 37))) {
463 fprintf (stderr
, "cannot open utility.library\n");
467 if (!Get_Device_And_Unit ()) {
469 "Please set the following environment variables:\n"
470 " CDROM_DEVICE name of SCSI device\n"
471 " CDROM_UNIT unit number of CDROM drive\n"
473 " setenv CDROM_DEVICE scsi.device\n"
474 " setenv CDROM_UNIT 2\n"
475 "Set the variable CDROM_FASTMEM to any value if you\n"
476 "want to use fast memory for SCSI buffers (does not work\n"
477 "with all SCSI devices!)\n"
478 "Set the variable CDROM_UNICODETABLE to file name of external\n"
479 "Unicode conversion table file if you want to see file names\n"
480 "with national characters on Joliet volumes\n"
488 global
->g_cd
= Open_CDROM (global
, global
->g_device
, global
->g_unit
, global
->g_memory_type
,
489 STD_BUFFERS
, FILE_BUFFERS
);
491 fprintf (stderr
, "cannot open CDROM, error code = %d\n", global
->g_cdrom_errno
);
495 printf ("Checking primary volume descriptor...\n");
497 printf ("Checking path tables...\n");
498 Check_Path_Tables ();
499 printf ("Comparing path table with directory records...\n");
500 Compare_Path_Table_With_Directory_Records ();
502 printf ("Checking directories...\n");
503 Check_Directories ();
505 printf ("All checks completed.\n");