aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--com32/include/byteswap.h142
-rw-r--r--com32/include/netinet/in.h12
-rw-r--r--core/fs/iso9660/iso9660.c41
-rw-r--r--core/fs/iso9660/iso9660_fs.h5
-rw-r--r--core/fs/iso9660/susp_rr.c529
-rw-r--r--core/fs/iso9660/susp_rr.h84
6 files changed, 793 insertions, 20 deletions
diff --git a/com32/include/byteswap.h b/com32/include/byteswap.h
index b7eeeb40..5ff2cd69 100644
--- a/com32/include/byteswap.h
+++ b/com32/include/byteswap.h
@@ -1,11 +1,12 @@
#ifndef _BYTESWAP_H
#define _BYTESWAP_H
-/* COM32 will be running on an i386 platform */
-
#include <stdint.h>
+#include <klibc/endian.h>
#include <klibc/compiler.h>
+/* This assumes an i386 platform */
+
#define __bswap_16_macro(v) ((uint16_t) \
(((uint16_t)(v) << 8) | \
((uint16_t)(v) >> 8)))
@@ -26,15 +27,13 @@ static inline __constfunc uint16_t __bswap_16(uint16_t v)
static inline __constfunc uint32_t __bswap_32(uint32_t v)
{
-#if __SIZEOF_POINTER__ == 4
+#if defined(__x86_64__)
+ asm("bswap %0" : "+r" (v));
+#elif defined(__i386__)
asm("xchgb %h0,%b0 ; roll $16,%0 ; xchgb %h0,%b0"
: "+q" (v));
-#elif __SIZEOF_POINTER__ == 8
- asm("bswap %0"
- : "=r" (v)
- : "0" (v));
#else
-#error "unable to build for architecture"
+ v = __bswap_32_macro(v);
#endif
return v;
}
@@ -49,10 +48,133 @@ static inline __constfunc uint32_t __bswap_32(uint32_t v)
static inline __constfunc uint64_t __bswap_64(uint64_t v)
{
- return ((uint64_t)__bswap_32(v) << 32) | __bswap_32(v >> 32);
+#if defined(__x86_64__)
+ asm("bswap %0" : "+r" (v));
+#else
+ v = ((uint64_t)__bswap_32(v) << 32) | __bswap_32(v >> 32);
+#endif
+ return v;
}
#define bswap_64(x) (__builtin_constant_p(x) ? \
__bswap_64_macro(x) : __bswap_64(x))
-#endif /* byteswap.h */
+/* This is generic */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+#define be16_to_cpu(x) bswap_16(x)
+#define cpu_to_be16(x) bswap_16(x)
+#define be32_to_cpu(x) bswap_32(x)
+#define cpu_to_be32(x) bswap_32(x)
+#define be64_to_cpu(x) bswap_64(x)
+#define cpu_to_be64(x) bswap_64(x)
+
+#define le16_to_cpu(x) (x)
+#define cpu_to_le16(x) (x)
+#define le32_to_cpu(x) (x)
+#define cpu_to_le32(x) (x)
+#define le64_to_cpu(x) (x)
+#define cpu_to_le64(x) (x)
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+#define le16_to_cpu(x) bswap_16(x)
+#define cpu_to_le16(x) bswap_16(x)
+#define le32_to_cpu(x) bswap_32(x)
+#define cpu_to_le32(x) bswap_32(x)
+#define le64_to_cpu(x) bswap_64(x)
+#define cpu_to_le64(x) bswap_64(x)
+
+#define be16_to_cpu(x) (x)
+#define cpu_to_be16(x) (x)
+#define be32_to_cpu(x) (x)
+#define cpu_to_be32(x) (x)
+#define be64_to_cpu(x) (x)
+#define cpu_to_be64(x) (x)
+
+#else
+
+#error "Unknown byte order!"
+
+#endif
+
+typedef struct { uint16_t x; } __attribute__((packed)) __ua_uint16_t;
+typedef struct { uint32_t x; } __attribute__((packed)) __ua_uint32_t;
+typedef struct { uint64_t x; } __attribute__((packed)) __ua_uint64_t;
+
+/* These are guaranteed to support unaligned accesses */
+static inline uint16_t get_le16(const uint16_t *p)
+{
+ const __ua_uint16_t *up = (const __ua_uint16_t *)p;
+ return le16_to_cpu(up->x);
+}
+
+static inline uint32_t get_le32(const uint32_t *p)
+{
+ const __ua_uint32_t *up = (const __ua_uint32_t *)p;
+ return le32_to_cpu(up->x);
+}
+
+static inline uint64_t get_le64(const uint64_t *p)
+{
+ const __ua_uint64_t *up = (const __ua_uint64_t *)p;
+ return le64_to_cpu(up->x);
+}
+
+static inline uint16_t get_be16(const uint16_t *p)
+{
+ const __ua_uint16_t *up = (const __ua_uint16_t *)p;
+ return be16_to_cpu(up->x);
+}
+
+static inline uint32_t get_be32(const uint32_t *p)
+{
+ const __ua_uint32_t *up = (const __ua_uint32_t *)p;
+ return be32_to_cpu(up->x);
+}
+
+static inline uint64_t get_be64(const uint64_t *p)
+{
+ const __ua_uint64_t *up = (const __ua_uint64_t *)p;
+ return be64_to_cpu(up->x);
+}
+
+static inline void put_le16(uint16_t v, uint16_t *p)
+{
+ __ua_uint16_t *up = (__ua_uint16_t *)p;
+ up->x = cpu_to_le16(v);
+}
+
+static inline void put_le32(uint32_t v, uint32_t *p)
+{
+ __ua_uint32_t *up = (__ua_uint32_t *)p;
+ up->x = cpu_to_le32(v);
+}
+
+static inline void put_le64(uint64_t v, uint64_t *p)
+{
+ __ua_uint64_t *up = (__ua_uint64_t *)p;
+ up->x = cpu_to_le64(v);
+}
+
+static inline void put_be16(uint16_t v, uint16_t *p)
+{
+ __ua_uint16_t *up = (__ua_uint16_t *)p;
+ up->x = cpu_to_be16(v);
+}
+
+static inline void put_be32(uint32_t v, uint32_t *p)
+{
+ __ua_uint32_t *up = (__ua_uint32_t *)p;
+ up->x = cpu_to_be32(v);
+}
+
+static inline void put_be64(uint64_t v, uint64_t *p)
+{
+ __ua_uint64_t *up = (__ua_uint64_t *)p;
+ up->x = cpu_to_be64(v);
+}
+
+#endif /* _BYTESWAP_H */
+
diff --git a/com32/include/netinet/in.h b/com32/include/netinet/in.h
index b24f8046..0a0049fd 100644
--- a/com32/include/netinet/in.h
+++ b/com32/include/netinet/in.h
@@ -8,12 +8,12 @@
#include <stdint.h>
#include <byteswap.h>
-#define htons(x) bswap_16(x)
-#define ntohs(x) bswap_16(x)
-#define htonl(x) bswap_32(x)
-#define ntohl(x) bswap_32(x)
-#define htonq(x) bswap_64(x)
-#define ntohq(x) bswap_64(x)
+#define htons(x) cpu_to_be16(x)
+#define ntohs(x) be16_to_cpu(x)
+#define htonl(x) cpu_to_be32(x)
+#define ntohl(x) be32_to_cpu(x)
+#define htonq(x) cpu_to_be64(x)
+#define ntohq(x) be64_to_cpu(x)
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;
diff --git a/core/fs/iso9660/iso9660.c b/core/fs/iso9660/iso9660.c
index fe58a5b3..492adc65 100644
--- a/core/fs/iso9660/iso9660.c
+++ b/core/fs/iso9660/iso9660.c
@@ -8,6 +8,7 @@
#include <fs.h>
#include <stdlib.h>
#include "iso9660_fs.h"
+#include "susp_rr.h"
/* Convert to lower case string */
static inline char iso_tolower(char c)
@@ -100,9 +101,10 @@ iso_find_entry(const char *dname, struct inode *inode)
block_t dir_block = PVT(inode)->lba;
int i = 0, offset = 0;
const char *de_name;
- int de_name_len, de_len;
+ int de_name_len, de_len, rr_name_len, ret;
const struct iso_dir_entry *de;
const char *data = NULL;
+ char *rr_name = NULL;
dprintf("iso_find_entry: \"%s\"\n", dname);
@@ -131,10 +133,24 @@ iso_find_entry(const char *dname, struct inode *inode)
continue;
}
+ /* Try to get Rock Ridge name */
+ ret = susp_rr_get_nm(fs, (char *) de, &rr_name, &rr_name_len);
+ if (ret > 0) {
+ if (strcmp(rr_name, dname) == 0) {
+ dprintf("Found (by RR name).\n");
+ free(rr_name);
+ return de;
+ }
+ free(rr_name);
+ rr_name = NULL;
+ continue; /* Rock Ridge was valid and did not match */
+ }
+
+ /* Fall back to ISO name */
de_name_len = de->name_len;
de_name = de->name;
if (iso_compare_name(de_name, de_name_len, dname)) {
- dprintf("Found.\n");
+ dprintf("Found (by ISO name).\n");
return de;
}
}
@@ -194,6 +210,8 @@ static int iso_readdir(struct file *file, struct dirent *dirent)
struct inode *inode = file->inode;
const struct iso_dir_entry *de;
const char *data = NULL;
+ char *rr_name = NULL;
+ int name_len, ret;
while (1) {
size_t offset = file->offset & (BLOCK_SIZE(fs) - 1);
@@ -218,8 +236,18 @@ static int iso_readdir(struct file *file, struct dirent *dirent)
dirent->d_ino = 0; /* Inode number is invalid to ISO fs */
dirent->d_off = file->offset;
dirent->d_type = get_inode_mode(de->flags);
- dirent->d_reclen = offsetof(struct dirent, d_name) + 1 +
- iso_convert_name(dirent->d_name, de->name, de->name_len);
+
+ /* Try to get Rock Ridge name */
+ ret = susp_rr_get_nm(fs, (char *) de, &rr_name, &name_len);
+ if (ret > 0) {
+ memcpy(dirent->d_name, rr_name, name_len + 1);
+ free(rr_name);
+ rr_name = NULL;
+ } else {
+ name_len = iso_convert_name(dirent->d_name, de->name, de->name_len);
+ }
+
+ dirent->d_reclen = offsetof(struct dirent, d_name) + 1 + name_len;
file->offset += de->length; /* Update for next reading */
@@ -282,6 +310,11 @@ static int iso_fs_init(struct fs_info *fs)
/* Initialize the cache */
cache_init(fs->fs_dev, fs->block_shift);
+ /* Check for SP and ER in the first directory record of the root directory.
+ Set sbi->susp_skip and enable sbi->do_rr as appropriate.
+ */
+ susp_rr_check_signatures(fs, 1);
+
return fs->block_shift;
}
diff --git a/core/fs/iso9660/iso9660_fs.h b/core/fs/iso9660/iso9660_fs.h
index a365fa1a..40265642 100644
--- a/core/fs/iso9660/iso9660_fs.h
+++ b/core/fs/iso9660/iso9660_fs.h
@@ -37,6 +37,11 @@ struct iso_dir_entry {
struct iso_sb_info {
struct iso_dir_entry root;
+
+ int do_rr; /* 1 , 2 = try to process Rock Ridge info , 0 = do not.
+ 2 indicates that the id of RRIP 1.12 was found.
+ */
+ int susp_skip; /* Skip length from SUSP entry SP */
};
/*
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;
+}
diff --git a/core/fs/iso9660/susp_rr.h b/core/fs/iso9660/susp_rr.h
new file mode 100644
index 00000000..16732df3
--- /dev/null
+++ b/core/fs/iso9660/susp_rr.h
@@ -0,0 +1,84 @@
+#ifndef ISO9660_SUSP_H
+#define ISO9660_SUSP_H 1
+
+/* Public functions of susp_rr.c, a reader for SUSP and Rock Ridge information.
+*/
+
+/* Inspect the ISO 9660 filesystem whether it bears the signatures of
+ SUSP and Rock Ridge.
+ Set the parameters fs->fs_info->do_rr and fs->fs_info->susp_skip.
+ To be called at the end of iso_fs_init().
+
+ SUSP demands an SP entry as first entry in the System Use area of
+ the first directory record in the root directory.
+ Rock Ridge prescribes at the same directory record an ER entry with
+ id field content "RRIP_1991A", or "IEEE_P1282", or "IEEE_1282".
+
+ @param fs The filesystem to inspect
+ @param flag Bitfield for control purposes:
+ bit0= Demand a Rock Ridge ER entry
+ @return 0 No valid SUSP signature found.
+ 1 Yes, signature of SUSP found. No ER was demanded.
+ 2 ER of RRIP 1.10 found.
+ 3 ER of RRIP 1.12 found.
+*/
+int susp_rr_check_signatures(struct fs_info *fs, int flag);
+
+
+/* Obtain the payload bytes of all SUSP entries with the given signature.
+
+ @param fs The filesystem from which to read CE blocks.
+ fs->fs_info->do_rr must be non-zero or else this function
+ will always return 0 (i.e. no payload found).
+ @param dir_rec Memory containing the whole ISO 9660 directory record.
+ @param sig Two characters of SUSP signature. E.g. "NM", "ER", ...
+ @param data Returns allocated memory with the payload.
+ A trailing 0-byte is added for convenience with strings.
+ If data is returned != NULL, then it has to be disposed
+ by free() when it is no longer needed.
+ @param len_data Returns the number of valid bytes in *data.
+ Not included in this count is the convenience 0-byte.
+ @param flag Bitfield for control purposes:
+ bit0= NM/SL mode:
+ Skip 5 header bytes rather than 4.
+ End after first matching entry without CONTINUE bit.
+ Return 0x100 | byte[4] (FLAGS) of first entry.
+ @return >0 Success.
+ *data and *len_data are valid.
+ Only in this case, *data is returned != NULL.
+ 0 Desired signature not found.
+ -1 Error.
+ Something is wrong with the ISO 9660 or SUSP data in
+ the image.
+*/
+int susp_rr_get_entries(struct fs_info *fs, char *dir_rec, char *sig,
+ char **data, int *len_data, int flag);
+
+
+/* Obtain the Rock Ridge name of a directory record.
+ If the found content of NM entries is longer than 255 characters,
+ then this function will not return it, but rather indicate an error.
+
+ @param fs The filesystem from which to read CE blocks.
+ fs->fs_info->do_rr must be non-zero or else this function
+ will always return 0 (i.e. no Rock Ridge name found).
+ @param dir_rec Memory containing the whole ISO 9660 directory record.
+ @param name Returns allocated memory with the name and a trailing
+ 0-byte. name might contain any byte values.
+ If name is returned != NULL, then it has to be disposed
+ by free() when it is no longer needed.
+ @param len_name Returns the number of valid bytes in *name.
+ Not included in this count is the 0-byte after the name.
+ @return >0 Success.
+ *name and *len_name are valid.
+ Only in this case, *data is returned != NULL.
+ 0 No NM entry found. No Rock Ridge name defined.
+ -1 Error.
+ Something is wrong with the ISO 9660 or SUSP data in
+ the image.
+*/
+int susp_rr_get_nm(struct fs_info *fs, char *dir_rec,
+ char **name, int *len_name);
+
+
+#endif /* ! ISO9660_SUSP_H */