grub2: bring back build of aros-side grub2 tools
[AROS.git] / rom / filesys / CDVDFS / src / checkcd.c
blobffd43503bd4ce3e4e5b5dedb56b091082d27c3ab
1 /* Checkcd.c:
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.
8 * All rights reserved.
9 * This software may be freely distributed and redistributed for
10 * non-commercial purposes, provided this notice is included.
11 * ----------------------------------------------------------------------
12 * History:
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
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
25 #include <dos/var.h>
27 #include <proto/dos.h>
28 #include <proto/exec.h>
29 #include <proto/utility.h>
31 #include "cdrom.h"
32 #include "iso9660.h"
33 #include "rock.h"
34 #include "charset.h"
35 #include "globals.h"
37 #ifdef LATTICE
38 #include <pragmas/dos_pragmas.h>
39 #include <pragmas/exec_pragmas.h>
40 #include <pragmas/utility_pragmas.h>
41 extern struct Library *SysBase, *DOSBase;
42 #endif
44 #define STD_BUFFERS 20
45 #define FILE_BUFFERS 2
47 static struct CDVDBase *global;
48 t_ulong g_check_sector;
49 char *g_check_name;
50 prim_vol_desc g_pvd;
51 int g_path_table_records = 0;
53 #define TU(x,o) (t_ulong)(((unsigned char *)(x))[o])
54 #if AROS_BIG_ENDIAN
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))
59 #else
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))
64 #endif
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);
73 if (l != m) {
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);
86 if (l != m) {
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)
96 t_ulong loc1, loc2;
97 t_uchar *buf1;
98 t_uchar *buf2;
99 int i;
101 for (i=0; i<=1; i++) {
103 int remain = g_pvd.path_size;
105 if (i == 0)
106 loc1 = GET731((char*) &g_pvd.l_table),
107 loc2 = GET731((char *) &g_pvd.opt_l_table);
108 else
109 loc1 = g_pvd.table, loc2 = g_pvd.opt_table;
110 if (!loc2)
111 continue;
113 for (;;) {
115 if (!Read_Chunk (global->g_cd, loc1)) {
116 printf ("ERROR: illegal sector %lu\n", (unsigned long)loc1);
117 exit (1);
119 buf1 = global->g_cd->buffer;
121 if (!Read_Chunk (global->g_cd, loc2)) {
122 printf ("ERROR: illegal sector %lu\n", (unsigned long)loc2);
123 exit (1);
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);
133 if (remain > 2048)
134 remain -= 2048;
135 else
136 break;
138 loc1++, 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);
148 int len;
150 if (!Read_Chunk (global->g_cd, sector)) {
151 printf ("ERROR: illegal sector %lu\n", (unsigned long)sector);
152 exit (1);
154 len = global->g_cd->buffer[*p_offset & 2047] + 8;
155 if (len & 1)
156 len++;
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));
163 exit (1);
165 memcpy (p_buf + part1_len, global->g_cd->buffer, len - part1_len);
166 } else
167 memcpy (p_buf, global->g_cd->buffer + (*p_offset & 2047), len);
169 *p_offset += 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;
178 t_ulong offset1 = 0;
179 t_ulong offset2 = 0;
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",
192 (unsigned long)tmp);
193 return;
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;
221 t_ulong offset = 0;
222 static t_uchar buf[256];
223 int rec = 1;
224 VOLUME *vol;
225 CDROM_OBJ *obj;
226 CDROM_INFO info;
227 t_ulong pos;
228 t_bool warn_case = 0;
230 vol = Open_Volume (global->g_cd, 0, 0);
231 if (!vol) {
232 printf ("ERROR: cannot open volume\n");
233 exit (10);
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);
242 fflush (stdout);
244 Get_Path_Table_Record (buf, loc, &offset);
245 /* skip root record: */
246 if (rec == 1)
247 continue;
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);
253 exit (10);
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);
263 putchar ('\n');
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");
267 warn_case = 1;
269 Close_Object (obj);
271 printf ("\r%75s\r", "");
272 Close_Volume (vol);
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)
287 int loc = 16;
288 int pvd_pos = 0;
289 prim_vol_desc *pvd;
291 do {
292 if (!Read_Chunk (global->g_cd, loc)) {
293 printf ("ERROR: illegal sector %d\n", loc);
294 exit (1);
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);
299 exit (10);
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)
306 pvd_pos = loc;
307 loc++;
308 } while (pvd->type != 255);
310 if (!Read_Chunk (global->g_cd, pvd_pos)) {
311 printf ("ERROR: illegal sector %d\n", loc);
312 exit (1);
314 pvd = (prim_vol_desc *) global->g_cd->buffer;
315 g_check_name = "primary volume descriptor";
316 g_check_sector = pvd_pos;
317 Check_733 (pvd, 81);
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)
327 CDROM_OBJ *obj;
328 CDROM_INFO info;
329 /* VOLUME *vol = p_home->volume; */
331 printf (" %s\r", p_name);
332 fflush (stdout);
333 obj = Open_Object (p_home, p_name);
334 if (obj) {
335 uint32_t offset = 0;
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;
341 Check_733 (dir, 3);
342 Check_733 (dir, 11);
343 Check_723 (dir, 29);
346 Close_Object (obj);
347 } else {
348 printf ("ERROR: Object '%s': iso_errno = %d\n", p_name, global->iso_errno);
349 return;
351 obj = Open_Object (p_home, p_name);
352 if (obj) {
353 uint32_t offset = 0;
355 while (Examine_Next (obj, &info, &offset)) {
356 if (info.directory_f) {
357 char *name = malloc (strlen (p_name) + info.name_length + 2);
358 int len;
359 if (!name) {
360 fprintf (stderr, "out of memory\n");
361 exit (10);
363 if (Is_Top_Level_Object (obj))
364 name[0] = 0;
365 else
366 sprintf (name, "%s/", p_name);
367 len = strlen (name) + info.name_length;
368 memcpy (name + strlen (name), info.name, info.name_length);
369 name[len] = 0;
370 Check_Subdirectory (p_home, name);
371 free (name);
374 Close_Object (obj);
375 } else {
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)
384 VOLUME *vol;
385 CDROM_OBJ *home;
387 if (!(vol = Open_Volume (global->g_cd, 1, 1))) {
388 printf ("ERROR: cannot open volume; iso_errno = %d\n", global->iso_errno);
389 exit (10);
392 if (!(home = Open_Top_Level_Directory (vol))) {
393 printf ("ERROR: cannot open top level directory; iso_errno = %d\n", global->iso_errno);
394 Close_Volume (vol);
395 exit (1);
398 Check_Subdirectory (home, ":");
400 Close_Object (home);
401 Close_Volume (vol);
404 void Cleanup (void)
406 if (global->g_cd)
407 Cleanup_CDROM (global->g_cd);
409 if (CodesetsBase)
410 CloseLibrary(CodesetsBase);
411 if (UtilityBase)
412 CloseLibrary ((APTR)UtilityBase);
415 int Get_Device_And_Unit (void)
417 int len;
418 char buf[10];
420 len = GetVar ((UBYTE *) "CDROM_DEVICE", (UBYTE *) global->g_device,
421 sizeof (global->g_device), 0);
422 if (len < 0)
423 return 0;
424 if (len >= sizeof (global->g_device)) {
425 fprintf (stderr, "CDROM_DEVICE too long\n");
426 exit (1);
428 global->g_device[len] = 0;
430 len = GetVar ((UBYTE *) "CDROM_UNIT", (UBYTE *) buf,
431 sizeof (buf), 0);
432 if (len < 0)
433 return 0;
434 if (len >= sizeof (buf)) {
435 fprintf (stderr, "CDROM_UNIT too long\n");
436 exit (1);
438 buf[len] = 0;
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;
447 return 1;
450 int main (int argc, char *argv[])
452 struct CDVDBase *global;
454 global = AllocMem(sizeof(*global), MEMF_CLEAR | MEMF_PUBLIC);
455 if (global)
456 return ERROR_NO_FREE_STORE;
458 global->g_cd = NULL;
459 global->g_memory_type = MEMF_CHIP;
460 atexit (Cleanup);
462 if (!(UtilityBase = (APTR)OpenLibrary ("utility.library", 37))) {
463 fprintf (stderr, "cannot open utility.library\n");
464 exit (1);
467 if (!Get_Device_And_Unit ()) {
468 fprintf (stderr,
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"
472 "e.g.\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"
482 exit (1);
485 CodesetsBase = NULL;
486 InitCharset(global);
488 global->g_cd = Open_CDROM (global, global->g_device, global->g_unit, global->g_memory_type,
489 STD_BUFFERS, FILE_BUFFERS);
490 if (!global->g_cd) {
491 fprintf (stderr, "cannot open CDROM, error code = %d\n", global->g_cdrom_errno);
492 exit (1);
495 printf ("Checking primary volume descriptor...\n");
496 Check_PVD ();
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");
507 exit (0);