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
48typedef uint32_t block_t;
49
50#define dprintf printf
51
52struct device {
53    IsoDataSource *src;
54};
55
56
57struct susp_rr_dir_rec_wrap {
58    char data[256];
59};
60
61struct 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
68struct fs_info {
69    struct device *fs_dev;
70    struct iso_sb_info *fs_info;
71};
72
73#define get_cache dummy_get_cache
74
75static 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
91static 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
103static 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*/
119struct 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
134static 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
166static 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*/
183static 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*/
235static 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*/
286static 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*/
314int 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    }
388ex:;
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*/
400int 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*/
433int 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
514no_susp:;
515    dprintf("susp_rr.c: No SUSP signature detected\n");
516    ret = 0;
517    goto ex;
518
519no_rr:;
520    dprintf("susp_rr.c: No Rock Ridge signature detected\n");
521    ret = 0;
522
523ex:;
524    if (ret <= 0)
525	sbi->do_rr = 0;
526    if (data != NULL)
527	free(data);
528    return ret;
529}
530