iso9660: use generic get_le32() accessor in SUSP/Rock Ridge code
[syslinux/sherbszt.git] / core / fs / iso9660 / susp_rr.c
blobbbeae975c9122992fb17778fe7942a8eb38f435b
1 /* Reader for SUSP and Rock Ridge information.
3 Copyright (c) 2013 Thomas Schmitt <scdbackup@gmx.net>
4 Provided under GNU General Public License version 2 or later.
6 Based on:
7 SUSP 1.12 (entries CE , PD , SP , ST , ER , ES)
8 ftp://ftp.ymi.com/pub/rockridge/susp112.ps
9 RRIP 1.12 (entries PX , PN , SL , NM , CL , PL , RE , TF , SF)
10 ftp://ftp.ymi.com/pub/rockridge/rrip112.ps
11 ECMA-119 aka ISO 9660
12 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
14 Shortcommings / Future improvements:
15 (XXX): Avoid memcpy() with Continuation Areas wich span over more than one
16 block ? (Will then need memcpy() with entries which are hit by a
17 block boundary.) (Questionable whether the effort is worth it.)
18 (XXX): Take into respect ES entries ? (Hardly anybody does this.)
22 #ifndef Isolinux_rockridge_in_libisofS
24 /* Mindlessly copied from core/fs/iso9660/iso9660.c */
25 #include <dprintf.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/dirent.h>
29 #include <core.h>
30 #include <cache.h>
31 #include <disk.h>
32 #include <fs.h>
33 #include <byteswap.h>
34 #include "iso9660_fs.h"
36 #else /* ! Isolinux_rockridge_in_libisofS */
38 /* ====== Test mock-up of definitions which should come from syslinux ====== */
40 /* With defined Isolinux_rockridge_in_libisofS this source file can be included
41 into libisofs/fs_image.c and the outcome of its public functions can be
42 compared with the perception of libisofs when loading an ISO image.
44 Test results look ok with 50 ISO images when read by xorriso underneath
45 valgrind.
48 typedef uint32_t block_t;
50 #define dprintf printf
52 struct device {
53 IsoDataSource *src;
57 struct susp_rr_dir_rec_wrap {
58 char data[256];
61 struct iso_sb_info {
62 struct susp_rr_dir_rec_wrap root;
64 int do_rr; /* 1 = try to process Rock Ridge info , 0 = do not */
65 int susp_skip; /* Skip length from SUSP enntry SP */
68 struct fs_info {
69 struct device *fs_dev;
70 struct iso_sb_info *fs_info;
73 #define get_cache dummy_get_cache
75 static char *dummy_get_cache(struct device *fs_dev, block_t lba)
77 static uint8_t buf[2048];
78 int ret;
80 ret = fs_dev->src->read_block(fs_dev->src, lba, buf);
81 if (ret < 0)
82 return NULL;
83 return (char *) buf;
86 /* =========================== End of test mock-up ========================= */
88 #endif /* ! Isolinux_rockridge_for_reaL */
91 static int susp_rr_is_out_of_mem(void *pt)
93 if (pt != NULL)
94 return 0;
95 dprintf("susp_rr.c: Out of memory !\n");
97 /* XXX : Should one abort on global level ? */
99 return 1;
103 static uint32_t susp_rr_read_lsb32(const void *buf)
105 return get_le32(buf);
109 /* State of iteration over SUSP entries.
111 This would be quite trivial if there was not the possibility of Continuation
112 Areas announced by the CE entry. In general they are quite rare, because
113 often all Rock Ridge entries fit into the ISO 9660 directory record.
114 So it seems unwise to invest much complexity into optimization of
115 Continuation Areas.
116 (I found 35 CE in a backup of mine which contains 63000 files, 2 CE in
117 a Debian netinst ISO, 2 CE in a Fedora live CD.)
119 struct susp_rr_iter {
120 struct fs_info *fs; /* From where to read Continuation Area data */
121 char *dir_rec; /* ISO 9660 directory record */
122 int in_ce; /* 0= still reading dir_rec, 1= reading ce_data */
123 char *ce_data; /* Loaded Continuation Area data */
124 int ce_allocated; /* 0= do not free ce_data, 1= do free */
125 size_t read_pos; /* Current read offset in dir_rec or ce_data */
126 size_t read_end; /* Current first invalid read_pos */
128 block_t next_lba; /* Block number of start of next Continuation Area */
129 size_t next_offset; /* Byte offset within the next_lba block */
130 size_t next_length; /* Number of valid bytes in next Cont. Area */
134 static int susp_rr_iter_new(struct susp_rr_iter **iter,
135 struct fs_info *fs, char *dir_rec)
137 struct iso_sb_info *sbi = fs->fs_info;
138 struct susp_rr_iter *o;
139 uint8_t len_fi;
140 int read_pos, read_end;
142 len_fi = ((uint8_t *) dir_rec)[32];
143 read_pos = 33 + len_fi + !(len_fi % 2) + sbi->susp_skip;
144 read_end = ((uint8_t *) dir_rec)[0];
145 if (read_pos + 4 > read_end)
146 return 0; /* Not enough System Use data present for SUSP */
147 if (dir_rec[read_pos + 3] != 1)
148 return 0; /* Not SUSP version 1 */
150 o= *iter= malloc(sizeof(struct susp_rr_iter));
151 if (susp_rr_is_out_of_mem(o))
152 return -1;
153 o->fs = fs;
154 o->dir_rec= dir_rec;
155 o->in_ce= 0;
156 o->read_pos = read_pos;
157 o->read_end = read_end;
158 o->next_lba = 0;
159 o->next_offset = o->next_length = 0;
160 o->ce_data = NULL;
161 o->ce_allocated = 0;
162 return 1;
166 static int susp_rr_iter_destroy(struct susp_rr_iter **iter)
168 struct susp_rr_iter *o;
170 o = *iter;
171 if (o == NULL)
172 return 0;
173 if (o->ce_data != NULL && o->ce_allocated)
174 free(o->ce_data);
175 free(o);
176 *iter = NULL;
177 return 1;
181 /* Switch to next Continuation Area.
183 static int susp_rr_switch_to_ca(struct susp_rr_iter *iter)
185 block_t num_blocks, i;
186 const char *data = NULL;
188 num_blocks = (iter->next_offset + iter->next_length + 2047) / 2048;
190 if (iter->ce_data != NULL && iter->ce_allocated)
191 free(iter->ce_data);
192 iter->ce_data = NULL;
193 iter->ce_allocated = 0;
194 if (num_blocks > 1) {
195 /* The blocks are expected contiguously. Need to consolidate them. */
196 if (num_blocks > 50) {
197 dprintf("susp_rr.c: More than 100 KB claimed by a CE entry.\n");
198 return -1;
200 iter->ce_data = malloc(num_blocks * 2048);
201 if (susp_rr_is_out_of_mem(iter->ce_data))
202 return -1;
203 iter->ce_allocated = 1;
204 for (i = 0; i < num_blocks; i++) {
205 data = get_cache(iter->fs->fs_dev, iter->next_lba + i);
206 if (data == NULL) {
207 dprintf("susp_rr.c: Failure to read block %lu\n",
208 (unsigned long) iter->next_lba + i);
209 return -1;
211 memcpy(iter->ce_data + i * 2048, data, 2048);
213 } else {
214 /* Avoiding malloc() and memcpy() in the single block case */
215 data = get_cache(iter->fs->fs_dev, iter->next_lba);
216 if (data == NULL) {
217 dprintf("susp_rr.c: Failure to read block %lu\n",
218 (unsigned long) iter->next_lba);
219 return -1;
221 iter->ce_data = (char *) data;
224 iter->in_ce = 1;
225 iter->read_pos = iter->next_offset;
226 iter->read_end = iter->next_offset + iter->next_length;
227 iter->next_lba = 0;
228 iter->next_offset = iter->next_length = 0;
229 return 1;
233 /* Obtain the next SUSP entry.
235 static int susp_rr_iterate(struct susp_rr_iter *iter, char **pos_pt)
237 char *entries;
238 uint8_t susp_len, *u_entry;
239 int ret;
241 if (iter->in_ce) {
242 entries = iter->ce_data + iter->read_pos;
243 } else {
244 entries = iter->dir_rec + iter->read_pos;
246 if (iter->read_pos + 4 <= iter->read_end)
247 if (entries[3] != 1) {
248 /* Not SUSP version 1 */
249 dprintf("susp_rr.c: Chain of SUSP entries broken\n");
250 return -1;
252 if (iter->read_pos + 4 > iter->read_end ||
253 (entries[0] == 'S' && entries[1] == 'T')) {
254 /* This part of the SU area is done */
255 if (iter->next_length == 0) {
256 /* No further CE entry was encountered. Iteration ends now. */
257 return 0;
259 ret = susp_rr_switch_to_ca(iter);
260 if (ret <= 0)
261 return ret;
262 entries = iter->ce_data + iter->read_pos;
265 if (entries[0] == 'C' && entries[1] == 'E') {
266 if (iter->next_length > 0) {
267 dprintf("susp_rr.c: Surplus CE entry detected\n");
268 return -1;
270 /* Register address data of next Continuation Area */
271 u_entry = (uint8_t *) entries;
272 iter->next_lba = susp_rr_read_lsb32(u_entry + 4);
273 iter->next_offset = susp_rr_read_lsb32(u_entry + 12);
274 iter->next_length = susp_rr_read_lsb32(u_entry + 20);
277 *pos_pt = entries;
278 susp_len = ((uint8_t *) entries)[2];
279 iter->read_pos += susp_len;
280 return 1;
284 /* Check for SP entry at position try_skip in the System Use area.
286 static int susp_rr_check_sp(struct fs_info *fs, char *dir_rec, int try_skip)
288 struct iso_sb_info *sbi = fs->fs_info;
289 int read_pos, read_end, len_fi;
290 uint8_t *sua;
292 len_fi = ((uint8_t *) dir_rec)[32];
293 read_pos = 33 + len_fi + !(len_fi % 2) + try_skip;
294 read_end = ((uint8_t *) dir_rec)[0];
295 if (read_end - read_pos < 7)
296 return 0;
297 sua = (uint8_t *) (dir_rec + read_pos);
298 if (sua[0] != 'S' || sua[1] != 'P' || sua[2] != 7 || sua[3] != 1 ||
299 sua[4] != 0xbe || sua[5] != 0xef)
300 return 0;
301 dprintf("susp_rr.c: SUSP signature detected\n");
302 sbi->susp_skip = ((uint8_t *) dir_rec)[6];
303 if (sbi->susp_skip > 0 && sbi->susp_skip != try_skip)
304 dprintf("susp_rr.c: Unusual: Non-zero skip length in SP entry\n");
305 return 1;
309 /* Public function. See susp_rr.h
311 Rock Ridge specific knowledge about NM and SL has been integrated here,
312 because this saves one malloc and memcpy for the file name.
314 int susp_rr_get_entries(struct fs_info *fs, char *dir_rec, char *sig,
315 char **data, int *len_data, int flag)
317 int count = 0, ret = 0, head_skip = 4, nmsp_flags = -1, is_done = 0;
318 char *pos_pt, *new_data;
319 uint8_t pay_len;
320 struct susp_rr_iter *iter = NULL;
321 struct iso_sb_info *sbi = fs->fs_info;
323 *data = NULL;
324 *len_data = 0;
326 if (!sbi->do_rr)
327 return 0; /* Rock Ridge is not enabled */
329 if (flag & 1)
330 head_skip = 5;
332 ret = susp_rr_iter_new(&iter, fs, dir_rec);
333 if (ret <= 0)
334 goto ex;
335 while (!is_done) {
336 ret = susp_rr_iterate(iter, &pos_pt);
337 if (ret < 0)
338 goto ex;
339 if (ret == 0)
340 break; /* End SUSP iteration */
341 if (sig[0] != pos_pt[0] || sig[1] != pos_pt[1])
342 continue; /* Next SUSP iteration */
344 pay_len = ((uint8_t *) pos_pt)[2];
345 if (pay_len < head_skip) {
346 dprintf("susp_rr.c: Short NM entry encountered.\n");
347 ret = -1;
348 goto ex;
350 pay_len -= head_skip;
351 if ((flag & 1)) {
352 if (nmsp_flags < 0)
353 nmsp_flags = ((uint8_t *) pos_pt)[4];
354 if (!(pos_pt[4] & 1)) /* No CONTINUE bit */
355 is_done = 1; /* This is the last iteration cycle */
357 count += pay_len;
358 if (count > 102400) {
359 dprintf("susp_rr.c: More than 100 KB in '%c%c' entries.\n",
360 sig[0], sig[1]);
361 ret = -1;
362 goto ex;
364 new_data = malloc(count + 1);
365 if (susp_rr_is_out_of_mem(new_data)) {
366 ret = -1;
367 goto ex;
369 if (*data != NULL) {
370 /* This case should be rare. An extra iteration pass to predict
371 the needed data size would hardly pay off.
373 memcpy(new_data, *data, *len_data);
374 free(*data);
376 new_data[count] = 0;
377 *data = new_data;
378 memcpy(*data + *len_data, pos_pt + head_skip, pay_len);
379 *len_data += pay_len;
381 if (*data == NULL) {
382 ret = 0;
383 } else if (flag & 1) {
384 ret = 0x100 | nmsp_flags;
385 } else {
386 ret = 1;
388 ex:;
389 susp_rr_iter_destroy(&iter);
390 if (ret <= 0 && *data != NULL) {
391 free(*data);
392 *data = NULL;
394 return ret;
398 /* Public function. See susp_rr.h
400 int susp_rr_get_nm(struct fs_info *fs, char *dir_rec,
401 char **name, int *len_name)
403 int ret;
405 ret = susp_rr_get_entries(fs, dir_rec, "NM", name, len_name, 1);
406 if (ret <= 0)
407 return ret;
409 /* Interpret flags */
410 if (ret & 0x6) {
411 if (*name != NULL)
412 free(*name);
413 *len_name = 0;
414 *name = strdup(ret & 0x2 ? "." : "..");
415 if (susp_rr_is_out_of_mem(*name)) {
416 return -1;
418 *len_name = strlen(*name);
420 if (*len_name >= 256) {
421 dprintf("susp_rr.c: Rock Ridge name longer than 255 characters.\n");
422 free(*name);
423 *name = NULL;
424 *len_name = 0;
425 return -1;
427 return 1;
431 /* Public function. See susp_rr.h
433 int susp_rr_check_signatures(struct fs_info *fs, int flag)
435 struct iso_sb_info *sbi = fs->fs_info;
436 char *dir_rec;
437 char *data = NULL;
438 uint8_t *u_data;
439 block_t lba;
440 int len_data, i, len_er = 0, len_id, ret;
441 int rrip_112 = 0;
443 sbi->do_rr = 1; /* provisory for the time of examination */
444 sbi->susp_skip = 0;
446 #ifndef Isolinux_rockridge_in_libisofS
447 /* (There is a name collision with libisofs BLOCK_SIZE. On the other hand,
448 libisofs has hardcoded blocksize 2048.) */
450 /* For now this works only with 2 KB blocks */
451 if (BLOCK_SIZE(fs) != 2048) {
452 dprintf("susp_rr.c: Block size is not 2048. Rock Ridge disabled.\n");
453 goto no_susp;
456 #endif /* Isolinux_rockridge_in_libisofS */
458 /* Obtain first dir_rec of root directory */
459 lba = susp_rr_read_lsb32(((uint8_t *) &(sbi->root)) + 2);
460 dir_rec = (char *) get_cache(fs->fs_dev, lba);
461 if (dir_rec == NULL)
462 goto no_susp;
464 /* First System Use entry must be SP */
465 ret = susp_rr_check_sp(fs, dir_rec, 0);
466 if (ret == 0) {
467 /* SUSP 1.12 prescribes that on CD-ROM XA discs the SP entry is at
468 offset 14 of the System Use area.
469 How to detect a CD-ROM XA disc here ?
470 (libisofs ignores this prescription and lives well with that.
471 /usr/src/linux/fs/isofs/ makes a blind try with 14.)
473 ret = susp_rr_check_sp(fs, dir_rec, 14);
475 if (ret <= 0)
476 goto no_susp;
478 if (!(flag & 1)) {
479 ret = 1;
480 goto ex;
483 /* Look for ER entries */
484 ret = susp_rr_get_entries(fs, dir_rec, "ER", &data, &len_data, 0);
485 if (ret <= 0 || len_data < 8)
486 goto no_rr;
487 u_data = (uint8_t *) data;
488 for (i = 0; i < len_data; i += len_er) {
489 len_id = u_data[0];
490 len_er = 4 + len_id + u_data[1] + u_data[2];
491 if (i + len_er > len_data) {
492 dprintf("susp_rr.c: Error with field lengths in ER entry\n");
493 goto no_rr;
495 if (len_id == 10 && strncmp(data + 4, "RRIP_1991A", len_id) == 0) {
496 dprintf("susp_rr.c: Signature of Rock Ridge 1.10 detected\n");
497 break;
498 } else if ((len_id == 10 &&
499 strncmp(data + 4, "IEEE_P1282", len_id) == 0) ||
500 (len_id == 9 &&
501 strncmp(data + 4, "IEEE_1282", len_id) == 0)) {
502 dprintf("susp_rr.c: Signature of Rock Ridge 1.12 detected\n");
503 rrip_112 = 1;
504 break;
507 if (i >= len_data)
508 goto no_rr;
510 sbi->do_rr = 1 + rrip_112;
511 ret = 2 + rrip_112;
512 goto ex;
514 no_susp:;
515 dprintf("susp_rr.c: No SUSP signature detected\n");
516 ret = 0;
517 goto ex;
519 no_rr:;
520 dprintf("susp_rr.c: No Rock Ridge signature detected\n");
521 ret = 0;
523 ex:;
524 if (ret <= 0)
525 sbi->do_rr = 0;
526 if (data != NULL)
527 free(data);
528 return ret;