aboutsummaryrefslogtreecommitdiffstats
path: root/core/fs/iso9660/susp_rr.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2014-02-12 09:56:15 -0800
committerH. Peter Anvin <hpa@zytor.com>2014-02-12 09:56:15 -0800
commit83730db928b303bc3e9f7bd35c2e64cd85ab0267 (patch)
treeb383a3263acc13360ab4e6f9c3ac5d5f85888d2b /core/fs/iso9660/susp_rr.c
parent9376eaf8582bcec4aebd697d33836f0d180aee13 (diff)
parent5de463f724da515fd6c5ea49ded6dde178362181 (diff)
downloadsyslinux-83730db928b303bc3e9f7bd35c2e64cd85ab0267.tar.gz
syslinux-83730db928b303bc3e9f7bd35c2e64cd85ab0267.tar.xz
syslinux-83730db928b303bc3e9f7bd35c2e64cd85ab0267.zip
Merge remote-tracking branch 'origin/rockridge'
Resolved Conflicts: com32/include/byteswap.h Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'core/fs/iso9660/susp_rr.c')
-rw-r--r--core/fs/iso9660/susp_rr.c529
1 files changed, 529 insertions, 0 deletions
diff --git a/core/fs/iso9660/susp_rr.c b/core/fs/iso9660/susp_rr.c
new file mode 100644
index 00000000..bbeae975
--- /dev/null
+++ b/core/fs/iso9660/susp_rr.c
@@ -0,0 +1,529 @@
+/* Reader for SUSP and Rock Ridge information.
+
+ Copyright (c) 2013 Thomas Schmitt <scdbackup@gmx.net>
+ Provided under GNU General Public License version 2 or later.
+
+ Based on:
+ SUSP 1.12 (entries CE , PD , SP , ST , ER , ES)
+ ftp://ftp.ymi.com/pub/rockridge/susp112.ps
+ RRIP 1.12 (entries PX , PN , SL , NM , CL , PL , RE , TF , SF)
+ ftp://ftp.ymi.com/pub/rockridge/rrip112.ps
+ ECMA-119 aka ISO 9660
+ http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
+
+ Shortcommings / Future improvements:
+ (XXX): Avoid memcpy() with Continuation Areas wich span over more than one
+ block ? (Will then need memcpy() with entries which are hit by a
+ block boundary.) (Questionable whether the effort is worth it.)
+ (XXX): Take into respect ES entries ? (Hardly anybody does this.)
+
+*/
+
+#ifndef Isolinux_rockridge_in_libisofS
+
+/* Mindlessly copied from core/fs/iso9660/iso9660.c */
+#include <dprintf.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/dirent.h>
+#include <core.h>
+#include <cache.h>
+#include <disk.h>
+#include <fs.h>
+#include <byteswap.h>
+#include "iso9660_fs.h"
+
+#else /* ! Isolinux_rockridge_in_libisofS */
+
+/* ====== Test mock-up of definitions which should come from syslinux ====== */
+
+/* With defined Isolinux_rockridge_in_libisofS this source file can be included
+ into libisofs/fs_image.c and the outcome of its public functions can be
+ compared with the perception of libisofs when loading an ISO image.
+
+ Test results look ok with 50 ISO images when read by xorriso underneath
+ valgrind.
+*/
+
+typedef uint32_t block_t;
+
+#define dprintf printf
+
+struct device {
+ IsoDataSource *src;
+};
+
+
+struct susp_rr_dir_rec_wrap {
+ char data[256];
+};
+
+struct iso_sb_info {
+ struct susp_rr_dir_rec_wrap root;
+
+ int do_rr; /* 1 = try to process Rock Ridge info , 0 = do not */
+ int susp_skip; /* Skip length from SUSP enntry SP */
+};
+
+struct fs_info {
+ struct device *fs_dev;
+ struct iso_sb_info *fs_info;
+};
+
+#define get_cache dummy_get_cache
+
+static char *dummy_get_cache(struct device *fs_dev, block_t lba)
+{
+ static uint8_t buf[2048];
+ int ret;
+
+ ret = fs_dev->src->read_block(fs_dev->src, lba, buf);
+ if (ret < 0)
+ return NULL;
+ return (char *) buf;
+}
+
+/* =========================== End of test mock-up ========================= */
+
+#endif /* ! Isolinux_rockridge_for_reaL */
+
+
+static int susp_rr_is_out_of_mem(void *pt)
+{
+ if (pt != NULL)
+ return 0;
+ dprintf("susp_rr.c: Out of memory !\n");
+
+ /* XXX : Should one abort on global level ? */
+
+ return 1;
+}
+
+
+static uint32_t susp_rr_read_lsb32(const void *buf)
+{
+ return get_le32(buf);
+}
+
+
+/* State of iteration over SUSP entries.
+
+ This would be quite trivial if there was not the possibility of Continuation
+ Areas announced by the CE entry. In general they are quite rare, because
+ often all Rock Ridge entries fit into the ISO 9660 directory record.
+ So it seems unwise to invest much complexity into optimization of
+ Continuation Areas.
+ (I found 35 CE in a backup of mine which contains 63000 files, 2 CE in
+ a Debian netinst ISO, 2 CE in a Fedora live CD.)
+*/
+struct susp_rr_iter {
+ struct fs_info *fs; /* From where to read Continuation Area data */
+ char *dir_rec; /* ISO 9660 directory record */
+ int in_ce; /* 0= still reading dir_rec, 1= reading ce_data */
+ char *ce_data; /* Loaded Continuation Area data */
+ int ce_allocated; /* 0= do not free ce_data, 1= do free */
+ size_t read_pos; /* Current read offset in dir_rec or ce_data */
+ size_t read_end; /* Current first invalid read_pos */
+
+ block_t next_lba; /* Block number of start of next Continuation Area */
+ size_t next_offset; /* Byte offset within the next_lba block */
+ size_t next_length; /* Number of valid bytes in next Cont. Area */
+};
+
+
+static int susp_rr_iter_new(struct susp_rr_iter **iter,
+ struct fs_info *fs, char *dir_rec)
+{
+ struct iso_sb_info *sbi = fs->fs_info;
+ struct susp_rr_iter *o;
+ uint8_t len_fi;
+ int read_pos, read_end;
+
+ len_fi = ((uint8_t *) dir_rec)[32];
+ read_pos = 33 + len_fi + !(len_fi % 2) + sbi->susp_skip;
+ read_end = ((uint8_t *) dir_rec)[0];
+ if (read_pos + 4 > read_end)
+ return 0; /* Not enough System Use data present for SUSP */
+ if (dir_rec[read_pos + 3] != 1)
+ return 0; /* Not SUSP version 1 */
+
+ o= *iter= malloc(sizeof(struct susp_rr_iter));
+ if (susp_rr_is_out_of_mem(o))
+ return -1;
+ o->fs = fs;
+ o->dir_rec= dir_rec;
+ o->in_ce= 0;
+ o->read_pos = read_pos;
+ o->read_end = read_end;
+ o->next_lba = 0;
+ o->next_offset = o->next_length = 0;
+ o->ce_data = NULL;
+ o->ce_allocated = 0;
+ return 1;
+}
+
+
+static int susp_rr_iter_destroy(struct susp_rr_iter **iter)
+{
+ struct susp_rr_iter *o;
+
+ o = *iter;
+ if (o == NULL)
+ return 0;
+ if (o->ce_data != NULL && o->ce_allocated)
+ free(o->ce_data);
+ free(o);
+ *iter = NULL;
+ return 1;
+}
+
+
+/* Switch to next Continuation Area.
+*/
+static int susp_rr_switch_to_ca(struct susp_rr_iter *iter)
+{
+ block_t num_blocks, i;
+ const char *data = NULL;
+
+ num_blocks = (iter->next_offset + iter->next_length + 2047) / 2048;
+
+ if (iter->ce_data != NULL && iter->ce_allocated)
+ free(iter->ce_data);
+ iter->ce_data = NULL;
+ iter->ce_allocated = 0;
+ if (num_blocks > 1) {
+ /* The blocks are expected contiguously. Need to consolidate them. */
+ if (num_blocks > 50) {
+ dprintf("susp_rr.c: More than 100 KB claimed by a CE entry.\n");
+ return -1;
+ }
+ iter->ce_data = malloc(num_blocks * 2048);
+ if (susp_rr_is_out_of_mem(iter->ce_data))
+ return -1;
+ iter->ce_allocated = 1;
+ for (i = 0; i < num_blocks; i++) {
+ data = get_cache(iter->fs->fs_dev, iter->next_lba + i);
+ if (data == NULL) {
+ dprintf("susp_rr.c: Failure to read block %lu\n",
+ (unsigned long) iter->next_lba + i);
+ return -1;
+ }
+ memcpy(iter->ce_data + i * 2048, data, 2048);
+ }
+ } else {
+ /* Avoiding malloc() and memcpy() in the single block case */
+ data = get_cache(iter->fs->fs_dev, iter->next_lba);
+ if (data == NULL) {
+ dprintf("susp_rr.c: Failure to read block %lu\n",
+ (unsigned long) iter->next_lba);
+ return -1;
+ }
+ iter->ce_data = (char *) data;
+ }
+
+ iter->in_ce = 1;
+ iter->read_pos = iter->next_offset;
+ iter->read_end = iter->next_offset + iter->next_length;
+ iter->next_lba = 0;
+ iter->next_offset = iter->next_length = 0;
+ return 1;
+}
+
+
+/* Obtain the next SUSP entry.
+*/
+static int susp_rr_iterate(struct susp_rr_iter *iter, char **pos_pt)
+{
+ char *entries;
+ uint8_t susp_len, *u_entry;
+ int ret;
+
+ if (iter->in_ce) {
+ entries = iter->ce_data + iter->read_pos;
+ } else {
+ entries = iter->dir_rec + iter->read_pos;
+ }
+ if (iter->read_pos + 4 <= iter->read_end)
+ if (entries[3] != 1) {
+ /* Not SUSP version 1 */
+ dprintf("susp_rr.c: Chain of SUSP entries broken\n");
+ return -1;
+ }
+ if (iter->read_pos + 4 > iter->read_end ||
+ (entries[0] == 'S' && entries[1] == 'T')) {
+ /* This part of the SU area is done */
+ if (iter->next_length == 0) {
+ /* No further CE entry was encountered. Iteration ends now. */
+ return 0;
+ }
+ ret = susp_rr_switch_to_ca(iter);
+ if (ret <= 0)
+ return ret;
+ entries = iter->ce_data + iter->read_pos;
+ }
+
+ if (entries[0] == 'C' && entries[1] == 'E') {
+ if (iter->next_length > 0) {
+ dprintf("susp_rr.c: Surplus CE entry detected\n");
+ return -1;
+ }
+ /* Register address data of next Continuation Area */
+ u_entry = (uint8_t *) entries;
+ iter->next_lba = susp_rr_read_lsb32(u_entry + 4);
+ iter->next_offset = susp_rr_read_lsb32(u_entry + 12);
+ iter->next_length = susp_rr_read_lsb32(u_entry + 20);
+ }
+
+ *pos_pt = entries;
+ susp_len = ((uint8_t *) entries)[2];
+ iter->read_pos += susp_len;
+ return 1;
+}
+
+
+/* Check for SP entry at position try_skip in the System Use area.
+*/
+static int susp_rr_check_sp(struct fs_info *fs, char *dir_rec, int try_skip)
+{
+ struct iso_sb_info *sbi = fs->fs_info;
+ int read_pos, read_end, len_fi;
+ uint8_t *sua;
+
+ len_fi = ((uint8_t *) dir_rec)[32];
+ read_pos = 33 + len_fi + !(len_fi % 2) + try_skip;
+ read_end = ((uint8_t *) dir_rec)[0];
+ if (read_end - read_pos < 7)
+ return 0;
+ sua = (uint8_t *) (dir_rec + read_pos);
+ if (sua[0] != 'S' || sua[1] != 'P' || sua[2] != 7 || sua[3] != 1 ||
+ sua[4] != 0xbe || sua[5] != 0xef)
+ return 0;
+ dprintf("susp_rr.c: SUSP signature detected\n");
+ sbi->susp_skip = ((uint8_t *) dir_rec)[6];
+ if (sbi->susp_skip > 0 && sbi->susp_skip != try_skip)
+ dprintf("susp_rr.c: Unusual: Non-zero skip length in SP entry\n");
+ return 1;
+}
+
+
+/* Public function. See susp_rr.h
+
+ Rock Ridge specific knowledge about NM and SL has been integrated here,
+ because this saves one malloc and memcpy for the file name.
+*/
+int susp_rr_get_entries(struct fs_info *fs, char *dir_rec, char *sig,
+ char **data, int *len_data, int flag)
+{
+ int count = 0, ret = 0, head_skip = 4, nmsp_flags = -1, is_done = 0;
+ char *pos_pt, *new_data;
+ uint8_t pay_len;
+ struct susp_rr_iter *iter = NULL;
+ struct iso_sb_info *sbi = fs->fs_info;
+
+ *data = NULL;
+ *len_data = 0;
+
+ if (!sbi->do_rr)
+ return 0; /* Rock Ridge is not enabled */
+
+ if (flag & 1)
+ head_skip = 5;
+
+ ret = susp_rr_iter_new(&iter, fs, dir_rec);
+ if (ret <= 0)
+ goto ex;
+ while (!is_done) {
+ ret = susp_rr_iterate(iter, &pos_pt);
+ if (ret < 0)
+ goto ex;
+ if (ret == 0)
+ break; /* End SUSP iteration */
+ if (sig[0] != pos_pt[0] || sig[1] != pos_pt[1])
+ continue; /* Next SUSP iteration */
+
+ pay_len = ((uint8_t *) pos_pt)[2];
+ if (pay_len < head_skip) {
+ dprintf("susp_rr.c: Short NM entry encountered.\n");
+ ret = -1;
+ goto ex;
+ }
+ pay_len -= head_skip;
+ if ((flag & 1)) {
+ if (nmsp_flags < 0)
+ nmsp_flags = ((uint8_t *) pos_pt)[4];
+ if (!(pos_pt[4] & 1)) /* No CONTINUE bit */
+ is_done = 1; /* This is the last iteration cycle */
+ }
+ count += pay_len;
+ if (count > 102400) {
+ dprintf("susp_rr.c: More than 100 KB in '%c%c' entries.\n",
+ sig[0], sig[1]);
+ ret = -1;
+ goto ex;
+ }
+ new_data = malloc(count + 1);
+ if (susp_rr_is_out_of_mem(new_data)) {
+ ret = -1;
+ goto ex;
+ }
+ if (*data != NULL) {
+ /* This case should be rare. An extra iteration pass to predict
+ the needed data size would hardly pay off.
+ */
+ memcpy(new_data, *data, *len_data);
+ free(*data);
+ }
+ new_data[count] = 0;
+ *data = new_data;
+ memcpy(*data + *len_data, pos_pt + head_skip, pay_len);
+ *len_data += pay_len;
+ }
+ if (*data == NULL) {
+ ret = 0;
+ } else if (flag & 1) {
+ ret = 0x100 | nmsp_flags;
+ } else {
+ ret = 1;
+ }
+ex:;
+ susp_rr_iter_destroy(&iter);
+ if (ret <= 0 && *data != NULL) {
+ free(*data);
+ *data = NULL;
+ }
+ return ret;
+}
+
+
+/* Public function. See susp_rr.h
+*/
+int susp_rr_get_nm(struct fs_info *fs, char *dir_rec,
+ char **name, int *len_name)
+{
+ int ret;
+
+ ret = susp_rr_get_entries(fs, dir_rec, "NM", name, len_name, 1);
+ if (ret <= 0)
+ return ret;
+
+ /* Interpret flags */
+ if (ret & 0x6) {
+ if (*name != NULL)
+ free(*name);
+ *len_name = 0;
+ *name = strdup(ret & 0x2 ? "." : "..");
+ if (susp_rr_is_out_of_mem(*name)) {
+ return -1;
+ }
+ *len_name = strlen(*name);
+ }
+ if (*len_name >= 256) {
+ dprintf("susp_rr.c: Rock Ridge name longer than 255 characters.\n");
+ free(*name);
+ *name = NULL;
+ *len_name = 0;
+ return -1;
+ }
+ return 1;
+}
+
+
+/* Public function. See susp_rr.h
+*/
+int susp_rr_check_signatures(struct fs_info *fs, int flag)
+{
+ struct iso_sb_info *sbi = fs->fs_info;
+ char *dir_rec;
+ char *data = NULL;
+ uint8_t *u_data;
+ block_t lba;
+ int len_data, i, len_er = 0, len_id, ret;
+ int rrip_112 = 0;
+
+ sbi->do_rr = 1; /* provisory for the time of examination */
+ sbi->susp_skip = 0;
+
+#ifndef Isolinux_rockridge_in_libisofS
+/* (There is a name collision with libisofs BLOCK_SIZE. On the other hand,
+ libisofs has hardcoded blocksize 2048.) */
+
+ /* For now this works only with 2 KB blocks */
+ if (BLOCK_SIZE(fs) != 2048) {
+ dprintf("susp_rr.c: Block size is not 2048. Rock Ridge disabled.\n");
+ goto no_susp;
+ }
+
+#endif /* Isolinux_rockridge_in_libisofS */
+
+ /* Obtain first dir_rec of root directory */
+ lba = susp_rr_read_lsb32(((uint8_t *) &(sbi->root)) + 2);
+ dir_rec = (char *) get_cache(fs->fs_dev, lba);
+ if (dir_rec == NULL)
+ goto no_susp;
+
+ /* First System Use entry must be SP */
+ ret = susp_rr_check_sp(fs, dir_rec, 0);
+ if (ret == 0) {
+ /* SUSP 1.12 prescribes that on CD-ROM XA discs the SP entry is at
+ offset 14 of the System Use area.
+ How to detect a CD-ROM XA disc here ?
+ (libisofs ignores this prescription and lives well with that.
+ /usr/src/linux/fs/isofs/ makes a blind try with 14.)
+ */
+ ret = susp_rr_check_sp(fs, dir_rec, 14);
+ }
+ if (ret <= 0)
+ goto no_susp;
+
+ if (!(flag & 1)) {
+ ret = 1;
+ goto ex;
+ }
+
+ /* Look for ER entries */
+ ret = susp_rr_get_entries(fs, dir_rec, "ER", &data, &len_data, 0);
+ if (ret <= 0 || len_data < 8)
+ goto no_rr;
+ u_data = (uint8_t *) data;
+ for (i = 0; i < len_data; i += len_er) {
+ len_id = u_data[0];
+ len_er = 4 + len_id + u_data[1] + u_data[2];
+ if (i + len_er > len_data) {
+ dprintf("susp_rr.c: Error with field lengths in ER entry\n");
+ goto no_rr;
+ }
+ if (len_id == 10 && strncmp(data + 4, "RRIP_1991A", len_id) == 0) {
+ dprintf("susp_rr.c: Signature of Rock Ridge 1.10 detected\n");
+ break;
+ } else if ((len_id == 10 &&
+ strncmp(data + 4, "IEEE_P1282", len_id) == 0) ||
+ (len_id == 9 &&
+ strncmp(data + 4, "IEEE_1282", len_id) == 0)) {
+ dprintf("susp_rr.c: Signature of Rock Ridge 1.12 detected\n");
+ rrip_112 = 1;
+ break;
+ }
+ }
+ if (i >= len_data)
+ goto no_rr;
+
+ sbi->do_rr = 1 + rrip_112;
+ ret = 2 + rrip_112;
+ goto ex;
+
+no_susp:;
+ dprintf("susp_rr.c: No SUSP signature detected\n");
+ ret = 0;
+ goto ex;
+
+no_rr:;
+ dprintf("susp_rr.c: No Rock Ridge signature detected\n");
+ ret = 0;
+
+ex:;
+ if (ret <= 0)
+ sbi->do_rr = 0;
+ if (data != NULL)
+ free(data);
+ return ret;
+}