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.
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
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 */
28 #include <sys/dirent.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
48 typedef uint32_t block_t
;
50 #define dprintf printf
57 struct susp_rr_dir_rec_wrap
{
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 */
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];
80 ret
= fs_dev
->src
->read_block(fs_dev
->src
, lba
, buf
);
86 /* =========================== End of test mock-up ========================= */
88 #endif /* ! Isolinux_rockridge_for_reaL */
91 static int susp_rr_is_out_of_mem(void *pt
)
95 dprintf("susp_rr.c: Out of memory !\n");
97 /* XXX : Should one abort on global level ? */
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
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
;
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
))
156 o
->read_pos
= read_pos
;
157 o
->read_end
= read_end
;
159 o
->next_offset
= o
->next_length
= 0;
166 static int susp_rr_iter_destroy(struct susp_rr_iter
**iter
)
168 struct susp_rr_iter
*o
;
173 if (o
->ce_data
!= NULL
&& o
->ce_allocated
)
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
)
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");
200 iter
->ce_data
= malloc(num_blocks
* 2048);
201 if (susp_rr_is_out_of_mem(iter
->ce_data
))
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
);
207 dprintf("susp_rr.c: Failure to read block %lu\n",
208 (unsigned long) iter
->next_lba
+ i
);
211 memcpy(iter
->ce_data
+ i
* 2048, data
, 2048);
214 /* Avoiding malloc() and memcpy() in the single block case */
215 data
= get_cache(iter
->fs
->fs_dev
, iter
->next_lba
);
217 dprintf("susp_rr.c: Failure to read block %lu\n",
218 (unsigned long) iter
->next_lba
);
221 iter
->ce_data
= (char *) data
;
225 iter
->read_pos
= iter
->next_offset
;
226 iter
->read_end
= iter
->next_offset
+ iter
->next_length
;
228 iter
->next_offset
= iter
->next_length
= 0;
233 /* Obtain the next SUSP entry.
235 static int susp_rr_iterate(struct susp_rr_iter
*iter
, char **pos_pt
)
238 uint8_t susp_len
, *u_entry
;
242 entries
= iter
->ce_data
+ iter
->read_pos
;
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");
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. */
259 ret
= susp_rr_switch_to_ca(iter
);
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");
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);
278 susp_len
= ((uint8_t *) entries
)[2];
279 iter
->read_pos
+= susp_len
;
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
;
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)
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)
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");
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
;
320 struct susp_rr_iter
*iter
= NULL
;
321 struct iso_sb_info
*sbi
= fs
->fs_info
;
327 return 0; /* Rock Ridge is not enabled */
332 ret
= susp_rr_iter_new(&iter
, fs
, dir_rec
);
336 ret
= susp_rr_iterate(iter
, &pos_pt
);
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");
350 pay_len
-= head_skip
;
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 */
358 if (count
> 102400) {
359 dprintf("susp_rr.c: More than 100 KB in '%c%c' entries.\n",
364 new_data
= malloc(count
+ 1);
365 if (susp_rr_is_out_of_mem(new_data
)) {
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
);
378 memcpy(*data
+ *len_data
, pos_pt
+ head_skip
, pay_len
);
379 *len_data
+= pay_len
;
383 } else if (flag
& 1) {
384 ret
= 0x100 | nmsp_flags
;
389 susp_rr_iter_destroy(&iter
);
390 if (ret
<= 0 && *data
!= NULL
) {
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
)
405 ret
= susp_rr_get_entries(fs
, dir_rec
, "NM", name
, len_name
, 1);
409 /* Interpret flags */
414 *name
= strdup(ret
& 0x2 ? "." : "..");
415 if (susp_rr_is_out_of_mem(*name
)) {
418 *len_name
= strlen(*name
);
420 if (*len_name
>= 256) {
421 dprintf("susp_rr.c: Rock Ridge name longer than 255 characters.\n");
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
;
440 int len_data
, i
, len_er
= 0, len_id
, ret
;
443 sbi
->do_rr
= 1; /* provisory for the time of examination */
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");
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
);
464 /* First System Use entry must be SP */
465 ret
= susp_rr_check_sp(fs
, dir_rec
, 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);
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)
487 u_data
= (uint8_t *) data
;
488 for (i
= 0; i
< len_data
; i
+= len_er
) {
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");
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");
498 } else if ((len_id
== 10 &&
499 strncmp(data
+ 4, "IEEE_P1282", len_id
) == 0) ||
501 strncmp(data
+ 4, "IEEE_1282", len_id
) == 0)) {
502 dprintf("susp_rr.c: Signature of Rock Ridge 1.12 detected\n");
510 sbi
->do_rr
= 1 + rrip_112
;
515 dprintf("susp_rr.c: No SUSP signature detected\n");
520 dprintf("susp_rr.c: No Rock Ridge signature detected\n");