• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Reader for SUSP and Rock Ridge information.
2 
3    Copyright (c) 2013 Thomas Schmitt <scdbackup@gmx.net>
4    Provided under GNU General Public License version 2 or later.
5 
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
13 
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.)
19 
20 */
21 
22 #ifndef Isolinux_rockridge_in_libisofS
23 
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"
35 
36 #else /* ! Isolinux_rockridge_in_libisofS */
37 
38 /* ====== Test mock-up of definitions which should come from syslinux ====== */
39 
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.
43 
44    Test results look ok with 50 ISO images when read by xorriso underneath
45    valgrind.
46 */
47 
48 typedef uint32_t block_t;
49 
50 #define dprintf printf
51 
52 struct device {
53     IsoDataSource *src;
54 };
55 
56 
57 struct susp_rr_dir_rec_wrap {
58     char data[256];
59 };
60 
61 struct iso_sb_info {
62     struct susp_rr_dir_rec_wrap root;
63 
64     int do_rr;       /* 1 = try to process Rock Ridge info , 0 = do not */
65     int susp_skip;   /* Skip length from SUSP enntry SP */
66 };
67 
68 struct fs_info {
69     struct device *fs_dev;
70     struct iso_sb_info *fs_info;
71 };
72 
73 #define get_cache dummy_get_cache
74 
dummy_get_cache(struct device * fs_dev,block_t lba)75 static char *dummy_get_cache(struct device *fs_dev, block_t lba)
76 {
77     static uint8_t buf[2048];
78     int ret;
79 
80     ret = fs_dev->src->read_block(fs_dev->src, lba, buf);
81     if (ret < 0)
82 	return NULL;
83     return (char *) buf;
84 }
85 
86 /* =========================== End of test mock-up ========================= */
87 
88 #endif /* ! Isolinux_rockridge_for_reaL */
89 
90 
susp_rr_is_out_of_mem(void * pt)91 static int susp_rr_is_out_of_mem(void *pt)
92 {
93     if (pt != NULL)
94 	return 0;
95     dprintf("susp_rr.c: Out of memory !\n");
96 
97     /* XXX : Should one abort on global level ? */
98 
99     return 1;
100 }
101 
102 
susp_rr_read_lsb32(const void * buf)103 static uint32_t susp_rr_read_lsb32(const void *buf)
104 {
105     return get_le32(buf);
106 }
107 
108 
109 /* State of iteration over SUSP entries.
110 
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.)
118 */
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 */
127 
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 */
131 };
132 
133 
susp_rr_iter_new(struct susp_rr_iter ** iter,struct fs_info * fs,char * dir_rec)134 static int susp_rr_iter_new(struct susp_rr_iter **iter,
135 			    struct fs_info *fs, char *dir_rec)
136 {
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;
141 
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 */
149 
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;
163 }
164 
165 
susp_rr_iter_destroy(struct susp_rr_iter ** iter)166 static int susp_rr_iter_destroy(struct susp_rr_iter **iter)
167 {
168     struct susp_rr_iter *o;
169 
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;
178 }
179 
180 
181 /* Switch to next Continuation Area.
182 */
susp_rr_switch_to_ca(struct susp_rr_iter * iter)183 static int susp_rr_switch_to_ca(struct susp_rr_iter *iter)
184 {
185     block_t num_blocks, i;
186     const char *data = NULL;
187 
188     num_blocks = (iter->next_offset + iter->next_length + 2047) / 2048;
189 
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;
199 	}
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;
210 	    }
211 	    memcpy(iter->ce_data + i * 2048, data, 2048);
212 	}
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;
220 	}
221 	iter->ce_data = (char *) data;
222     }
223 
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;
230 }
231 
232 
233 /* Obtain the next SUSP entry.
234 */
susp_rr_iterate(struct susp_rr_iter * iter,char ** pos_pt)235 static int susp_rr_iterate(struct susp_rr_iter *iter, char **pos_pt)
236 {
237     char *entries;
238     uint8_t susp_len, *u_entry;
239     int ret;
240 
241     if (iter->in_ce) {
242 	entries = iter->ce_data + iter->read_pos;
243     } else {
244 	entries = iter->dir_rec + iter->read_pos;
245     }
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;
251 	}
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;
258 	}
259 	ret = susp_rr_switch_to_ca(iter);
260 	if (ret <= 0)
261 	    return ret;
262 	entries = iter->ce_data + iter->read_pos;
263     }
264 
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;
269 	}
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);
275     }
276 
277     *pos_pt = entries;
278     susp_len = ((uint8_t *) entries)[2];
279     iter->read_pos += susp_len;
280     return 1;
281 }
282 
283 
284 /* Check for SP entry at position try_skip in the System Use area.
285 */
susp_rr_check_sp(struct fs_info * fs,char * dir_rec,int try_skip)286 static int susp_rr_check_sp(struct fs_info *fs, char *dir_rec, int try_skip)
287 {
288     struct iso_sb_info *sbi = fs->fs_info;
289     int read_pos, read_end, len_fi;
290     uint8_t *sua;
291 
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;
306 }
307 
308 
309 /* Public function. See susp_rr.h
310 
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.
313 */
susp_rr_get_entries(struct fs_info * fs,char * dir_rec,char * sig,char ** data,int * len_data,int flag)314 int susp_rr_get_entries(struct fs_info *fs, char *dir_rec, char *sig,
315 			char **data, int *len_data, int flag)
316 {
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;
322 
323     *data = NULL;
324     *len_data = 0;
325 
326     if (!sbi->do_rr)
327 	return 0; /* Rock Ridge is not enabled */
328 
329     if (flag & 1)
330 	head_skip = 5;
331 
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 */
343 
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;
349 	}
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 */
356 	}
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;
363 	}
364 	new_data = malloc(count + 1);
365 	if (susp_rr_is_out_of_mem(new_data)) {
366 	    ret = -1;
367 	    goto ex;
368 	}
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.
372 	    */
373 	    memcpy(new_data, *data, *len_data);
374 	    free(*data);
375 	}
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;
380     }
381     if (*data == NULL) {
382 	ret = 0;
383     } else if (flag & 1) {
384 	ret = 0x100 | nmsp_flags;
385     } else {
386 	ret = 1;
387     }
388 ex:;
389     susp_rr_iter_destroy(&iter);
390     if (ret <= 0 && *data != NULL) {
391 	free(*data);
392 	*data = NULL;
393     }
394     return ret;
395 }
396 
397 
398 /* Public function. See susp_rr.h
399 */
susp_rr_get_nm(struct fs_info * fs,char * dir_rec,char ** name,int * len_name)400 int susp_rr_get_nm(struct fs_info *fs, char *dir_rec,
401 		   char **name, int *len_name)
402 {
403     int ret;
404 
405     ret = susp_rr_get_entries(fs, dir_rec, "NM", name, len_name, 1);
406     if (ret <= 0)
407 	return ret;
408 
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;
417 	}
418 	*len_name = strlen(*name);
419     }
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;
426     }
427     return 1;
428 }
429 
430 
431 /* Public function. See susp_rr.h
432 */
susp_rr_check_signatures(struct fs_info * fs,int flag)433 int susp_rr_check_signatures(struct fs_info *fs, int flag)
434 {
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;
442 
443     sbi->do_rr = 1;      /* provisory for the time of examination */
444     sbi->susp_skip = 0;
445 
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.) */
449 
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;
454     }
455 
456 #endif /* Isolinux_rockridge_in_libisofS */
457 
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;
463 
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.)
472 	*/
473 	ret = susp_rr_check_sp(fs, dir_rec, 14);
474     }
475     if (ret <= 0)
476 	goto no_susp;
477 
478     if (!(flag & 1)) {
479 	ret = 1;
480 	goto ex;
481     }
482 
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;
494 	}
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;
505 	}
506     }
507     if (i >= len_data)
508 	goto no_rr;
509 
510     sbi->do_rr = 1 + rrip_112;
511     ret = 2 + rrip_112;
512     goto ex;
513 
514 no_susp:;
515     dprintf("susp_rr.c: No SUSP signature detected\n");
516     ret = 0;
517     goto ex;
518 
519 no_rr:;
520     dprintf("susp_rr.c: No Rock Ridge signature detected\n");
521     ret = 0;
522 
523 ex:;
524     if (ret <= 0)
525 	sbi->do_rr = 0;
526     if (data != NULL)
527 	free(data);
528     return ret;
529 }
530