Working around a Linux kernel bug, which hides files of which the

Rock Ridge CE entry points to a range that crosses a block boundary,
or of which the byte offset is larger than the block size of 2048.
Thanks to Joerg Meyer.
This commit is contained in:
Thomas Schmitt 2015-02-26 17:56:34 +01:00
parent 527b613607
commit 8e55195edc
2 changed files with 269 additions and 36 deletions

View File

@ -1,7 +1,7 @@
/* /*
* Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2007 Vreixo Formoso
* Copyright (c) 2007 Mario Danic * Copyright (c) 2007 Mario Danic
* Copyright (c) 2009 - 2012 Thomas Schmitt * Copyright (c) 2009 - 2015 Thomas Schmitt
* *
* This file is part of the libisofs project; you can redistribute it and/or * This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 * modify it under the terms of the GNU General Public License version 2
@ -32,10 +32,16 @@
#define ISO_ROCKRIDGE_IN_DIR_REC 124 #define ISO_ROCKRIDGE_IN_DIR_REC 124
#endif #endif
#define ISO_CE_ENTRY_SIZE 28
static static
int susp_add_ES(Ecma119Image *t, struct susp_info *susp, int to_ce, int seqno); int susp_add_ES(Ecma119Image *t, struct susp_info *susp, int to_ce, int seqno);
static
int susp_make_CE(Ecma119Image *t, uint8_t **CE,
uint32_t block_offset, uint32_t byte_offset, uint32_t size);
static static
int susp_append(Ecma119Image *t, struct susp_info *susp, uint8_t *data) int susp_append(Ecma119Image *t, struct susp_info *susp, uint8_t *data)
@ -54,14 +60,66 @@ int susp_append(Ecma119Image *t, struct susp_info *susp, uint8_t *data)
static static
int susp_append_ce(Ecma119Image *t, struct susp_info *susp, uint8_t *data) int susp_append_ce(Ecma119Image *t, struct susp_info *susp, uint8_t *data)
{ {
susp->n_ce_susp_fields++; int to_alloc = 1, ret;
susp->ce_susp_fields = realloc(susp->ce_susp_fields, sizeof(void*) unsigned char *pad;
* susp->n_ce_susp_fields); uint8_t *CE;
if (susp->ce_susp_fields == NULL) { size_t next_alloc;
return ISO_OUT_OF_MEM;
if (data[0] &&
(susp->ce_len + data[2] + ISO_CE_ENTRY_SIZE - 1) / BLOCK_SIZE !=
susp->ce_len / BLOCK_SIZE) {
/* Linux fs/isofs wants byte_offset + ce_len <= BLOCK_SIZE.
So this Continuation Area needs to end by a CE which points
to the start of the next block.
*/
to_alloc = 2;
if ((susp->ce_len + ISO_CE_ENTRY_SIZE) % BLOCK_SIZE)
to_alloc = 3; /* need a PAD pseudo entry */
}
if (susp->ce_susp_fields == NULL)
susp->alloc_ce_susp_fields = 0;
if (susp->n_ce_susp_fields + to_alloc > susp->alloc_ce_susp_fields) {
next_alloc = susp->alloc_ce_susp_fields;
while (susp->n_ce_susp_fields + to_alloc > next_alloc)
next_alloc += ISO_SUSP_CE_ALLOC_STEP;
susp->ce_susp_fields = realloc(susp->ce_susp_fields,
sizeof(uint8_t *) * next_alloc);
if (susp->ce_susp_fields == NULL)
return ISO_OUT_OF_MEM;
susp->alloc_ce_susp_fields = next_alloc;
}
if (to_alloc >= 2) {
/* Insert CE entry (actual CE size later by susp_update_CE_sizes) */
ret = susp_make_CE(t, &CE, (uint32_t) (susp->ce_block +
susp->ce_len / BLOCK_SIZE + 1),
(uint32_t) 0, (uint32_t) 2048);
if (ret < 0)
return ret;
susp->ce_susp_fields[susp->n_ce_susp_fields] = CE;
susp->ce_len += ISO_CE_ENTRY_SIZE;
susp->n_ce_susp_fields++;
}
if (to_alloc >= 3) {
pad = malloc(1);
if (pad == NULL)
return ISO_OUT_OF_MEM;
pad[0] = 0;
susp->ce_susp_fields[susp->n_ce_susp_fields] = pad;
if (susp->ce_len % BLOCK_SIZE)
susp->ce_len += BLOCK_SIZE - (susp->ce_len % BLOCK_SIZE);
susp->n_ce_susp_fields++;
}
susp->ce_susp_fields[susp->n_ce_susp_fields] = data;
susp->n_ce_susp_fields++;
if (data[0] == 0) {
if (susp->ce_len % BLOCK_SIZE)
susp->ce_len += BLOCK_SIZE - (susp->ce_len % BLOCK_SIZE);
} else {
susp->ce_len += data[2];
} }
susp->ce_susp_fields[susp->n_ce_susp_fields - 1] = data;
susp->ce_len += data[2];
return ISO_SUCCESS; return ISO_SUCCESS;
} }
@ -547,9 +605,24 @@ int rrip_add_SL(Ecma119Image *t, struct susp_info *susp, uint8_t **comp,
} }
static
int susp_calc_add_to_ce(size_t *ce, int add, int flag)
{
/* Account for inserted CE before size exceeds block size */
if ((*ce + add + ISO_CE_ENTRY_SIZE - 1) / BLOCK_SIZE != *ce / BLOCK_SIZE) {
/* Insert CE and padding */
*ce += ISO_CE_ENTRY_SIZE;
if (*ce % BLOCK_SIZE)
*ce += BLOCK_SIZE - (*ce % BLOCK_SIZE);
}
*ce += add;
return ISO_SUCCESS;
}
/* /*
@param flag bit0= only account sizes in sua_free resp. ce_len @param flag bit0= only account sizes in sua_free resp. ce_len.
parameters susp and data may be NULL in this case Parameter susp may be NULL in this case
*/ */
static static
int aaip_add_AL(Ecma119Image *t, struct susp_info *susp, int aaip_add_AL(Ecma119Image *t, struct susp_info *susp,
@ -558,11 +631,20 @@ int aaip_add_AL(Ecma119Image *t, struct susp_info *susp,
{ {
int ret, done = 0, len, es_extra = 0; int ret, done = 0, len, es_extra = 0;
uint8_t *aapt, *cpt; uint8_t *aapt, *cpt;
size_t count = 0;
if (!t->opts->aaip_susp_1_10) if (!t->opts->aaip_susp_1_10)
es_extra = 5; es_extra = 5;
if (*sua_free < num_data + es_extra || *ce_len > 0) { if (*sua_free < num_data + es_extra || *ce_len > 0) {
*ce_len += num_data + es_extra; if (es_extra > 0)
susp_calc_add_to_ce(ce_len, es_extra, 0);
done = 0;
for (aapt = *data; !done; aapt += aapt[2]) {
done = !(aapt[4] & 1);
len = aapt[2];
susp_calc_add_to_ce(ce_len, len, 0);
count += len;
}
} else { } else {
*sua_free -= num_data + es_extra; *sua_free -= num_data + es_extra;
} }
@ -588,6 +670,7 @@ int aaip_add_AL(Ecma119Image *t, struct susp_info *susp,
} }
/* Multiple fields have to be handed over as single field copies */ /* Multiple fields have to be handed over as single field copies */
done = 0;
for (aapt = *data; !done; aapt += aapt[2]) { for (aapt = *data; !done; aapt += aapt[2]) {
done = !(aapt[4] & 1); done = !(aapt[4] & 1);
len = aapt[2]; len = aapt[2];
@ -722,6 +805,35 @@ int aaip_add_ER(Ecma119Image *t, struct susp_info *susp, int flag)
} }
/**
* Create the byte representation of a CE entry.
* (SUSP, 5.1).
*/
static
int susp_make_CE(Ecma119Image *t, uint8_t **CE,
uint32_t block_offset, uint32_t byte_offset, uint32_t size)
{
uint8_t *data;
*CE = NULL;
data = calloc(1, 28);
if (data == NULL)
return ISO_OUT_OF_MEM;
*CE = data;
data[0] = 'C';
data[1] = 'E';
data[2] = 28;
data[3] = 1;
iso_bb(&data[4], block_offset - t->eff_partition_offset, 4);
iso_bb(&data[12], byte_offset, 4);
iso_bb(&data[20], size, 4);
return ISO_SUCCESS;
}
/** /**
* Add a CE System Use Entry to the given tree node. A "CE" is used to add * Add a CE System Use Entry to the given tree node. A "CE" is used to add
* a continuation area, where additional System Use Entry can be written. * a continuation area, where additional System Use Entry can be written.
@ -730,20 +842,18 @@ int aaip_add_ER(Ecma119Image *t, struct susp_info *susp, int flag)
static static
int susp_add_CE(Ecma119Image *t, size_t ce_len, struct susp_info *susp) int susp_add_CE(Ecma119Image *t, size_t ce_len, struct susp_info *susp)
{ {
uint8_t *CE = malloc(28); uint32_t block_offset, byte_offset;
if (CE == NULL) { uint8_t *CE;
return ISO_OUT_OF_MEM; int ret;
}
CE[0] = 'C';
CE[1] = 'E';
CE[2] = 28;
CE[3] = 1;
iso_bb(&CE[4], susp->ce_block - t->eff_partition_offset, 4);
iso_bb(&CE[12], susp->ce_len, 4);
iso_bb(&CE[20], (uint32_t) ce_len, 4);
/* Linux fs/isofs wants byte_offset + ce_len <= BLOCK_SIZE
* Here the byte_offset is reduced to the minimum.
*/
block_offset = susp->ce_block + susp->ce_len / BLOCK_SIZE;
byte_offset = susp->ce_len % BLOCK_SIZE;
ret = susp_make_CE(t, &CE, block_offset, byte_offset, (uint32_t) ce_len);
if (ret < 0)
return ret;
return susp_append(t, susp, CE); return susp_append(t, susp, CE);
} }
@ -799,6 +909,27 @@ int susp_add_ES(Ecma119Image *t, struct susp_info *susp, int to_ce, int seqno)
} }
/**
* A field beginning by 0 causes rrip_write_ce_fields() to pad up to the
* next block.
*/
static
int pseudo_susp_add_PAD(Ecma119Image *t, struct susp_info *susp)
{
unsigned char *pad;
int ret;
pad = malloc(1);
if (pad == NULL)
return ISO_OUT_OF_MEM;
pad[0] = 0;
ret = susp_append_ce(t, susp, pad);
if (ret < 0)
return ret;
return ISO_SUCCESS;
}
/** /**
* see doc/zisofs_format.txt : "ZF System Use Entry Format" * see doc/zisofs_format.txt : "ZF System Use Entry Format"
*/ */
@ -922,7 +1053,7 @@ int add_zf_field(Ecma119Image *t, Ecma119Node *n, struct susp_info *info,
/* Account for field size */ /* Account for field size */
if (*sua_free < 16 || *ce_len > 0) { if (*sua_free < 16 || *ce_len > 0) {
*ce_len += 16; susp_calc_add_to_ce(ce_len, 16, 0);
} else { } else {
*sua_free -= 16; *sua_free -= 16;
} }
@ -988,6 +1119,7 @@ int susp_calc_nm_sl_al(Ecma119Image *t, Ecma119Node *n, size_t space,
void *xipt; void *xipt;
size_t num_aapt = 0, sua_free = 0; size_t num_aapt = 0, sua_free = 0;
int ret; int ret;
uint8_t *aapt;
su_mem = *su_size; su_mem = *su_size;
ce_mem = *ce; ce_mem = *ce;
@ -1020,7 +1152,7 @@ int susp_calc_nm_sl_al(Ecma119Image *t, Ecma119Node *n, size_t space,
of the name will always fit into the directory entry.) of the name will always fit into the directory entry.)
*/; */;
*ce = 5 + namelen; susp_calc_add_to_ce(ce, 5 + namelen, 0);
*su_size = space; *su_size = space;
} }
if (n->type == ECMA119_SYMLINK) { if (n->type == ECMA119_SYMLINK) {
@ -1090,21 +1222,26 @@ int susp_calc_nm_sl_al(Ecma119Image *t, Ecma119Node *n, size_t space,
* the component can be divided between this * the component can be divided between this
* and another SL entry * and another SL entry
*/ */
*ce += 255; /* this SL, full */ /* Will fill up old SL and write it */
sl_len = 5 + (clen - fit); susp_calc_add_to_ce(ce, 255, 0);
sl_len = 5 + (clen - fit); /* Start new SL */
} else { } else {
/* /*
* the component will need a 2rd SL entry in * the component will need a 2rd SL entry in
* any case, so we prefer to don't write * any case, so we prefer to don't write
* anything in this SL * anything in this SL
*/ */
*ce += sl_len + 255; /* Will write non-full old SL */
sl_len = 5 + (clen - 250) + 2; susp_calc_add_to_ce(ce, sl_len , 0);
/* Will write another full SL */
susp_calc_add_to_ce(ce, 255, 0);
sl_len = 5 + (clen - 250) + 2; /* Start new SL */
} }
} else { } else {
/* case 2, create a new SL entry */ /* case 2, create a new SL entry */
*ce += sl_len; /* Will write non-full old SL */
sl_len = 5 + clen; susp_calc_add_to_ce(ce, sl_len, 0);
sl_len = 5 + clen; /* Start new SL */
} }
} else { } else {
sl_len += clen; sl_len += clen;
@ -1126,7 +1263,7 @@ int susp_calc_nm_sl_al(Ecma119Image *t, Ecma119Node *n, size_t space,
/* the whole SL fits into the SUA */ /* the whole SL fits into the SUA */
*su_size += sl_len; *su_size += sl_len;
} else { } else {
*ce += sl_len; susp_calc_add_to_ce(ce, sl_len, 0);
} }
} }
@ -1150,7 +1287,8 @@ int susp_calc_nm_sl_al(Ecma119Image *t, Ecma119Node *n, size_t space,
/* let the expert decide where to add num_aapt */ /* let the expert decide where to add num_aapt */
if (num_aapt > 0) { if (num_aapt > 0) {
sua_free = space - *su_size; sua_free = space - *su_size;
aaip_add_AL(t, NULL, NULL, num_aapt, &sua_free, ce, 1); aapt = (uint8_t *) xipt;
aaip_add_AL(t, NULL, &aapt, num_aapt, &sua_free, ce, 1);
*su_size = space - sua_free; *su_size = space - sua_free;
if (*ce > 0 && !(flag & 1)) if (*ce > 0 && !(flag & 1))
goto unannounced_ca; goto unannounced_ca;
@ -1185,7 +1323,9 @@ int add_aa_string(Ecma119Image *t, Ecma119Node *n, struct susp_info *info,
num_aapt = aaip_count_bytes((unsigned char *) xipt, 0); num_aapt = aaip_count_bytes((unsigned char *) xipt, 0);
if (num_aapt > 0) { if (num_aapt > 0) {
if (flag & 1) { if (flag & 1) {
ret = aaip_add_AL(t, NULL,NULL, num_aapt, sua_free, ce_len, 1); aapt = (unsigned char *) xipt;
ret = aaip_add_AL(t, NULL, &aapt, num_aapt, sua_free, ce_len,
1);
} else { } else {
aapt = malloc(num_aapt); aapt = malloc(num_aapt);
if (aapt == NULL) if (aapt == NULL)
@ -1193,10 +1333,10 @@ int add_aa_string(Ecma119Image *t, Ecma119Node *n, struct susp_info *info,
memcpy(aapt, xipt, num_aapt); memcpy(aapt, xipt, num_aapt);
ret = aaip_add_AL(t, info, &aapt, num_aapt, sua_free, ce_len, ret = aaip_add_AL(t, info, &aapt, num_aapt, sua_free, ce_len,
0); 0);
/* aapt is NULL now and the memory is owned by t */
} }
if (ret < 0) if (ret < 0)
return ret; return ret;
/* aapt is NULL now and the memory is owned by t */
} }
} }
return 1; return 1;
@ -1393,6 +1533,9 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
return ISO_ASSERT_FAILURE; return ISO_ASSERT_FAILURE;
} }
/* Mark start index of node's continuation area for later update */
info->current_ce_start = info->n_ce_susp_fields;
if (type == 2 && n->parent != NULL) { if (type == 2 && n->parent != NULL) {
node = n->parent; node = n->parent;
} else { } else {
@ -1699,6 +1842,16 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
} }
if (ce_is_predicted) { if (ce_is_predicted) {
if ((info->ce_len % BLOCK_SIZE) &&
(info->ce_len + ce_len_pd - 1 ) / BLOCK_SIZE !=
info->ce_len / BLOCK_SIZE) {
/* Linux fs/isofs wants byte_offset + ce_len <= BLOCK_SIZE
* Insert padding to shift CE offset to next block start
*/
ret = pseudo_susp_add_PAD(t, info);
if (ret < 0)
goto add_susp_cleanup;
}
/* Add the CE entry */ /* Add the CE entry */
ret = susp_add_CE(t, ce_len_pd, info); ret = susp_add_CE(t, ce_len_pd, info);
if (ret < 0) { if (ret < 0) {
@ -1854,6 +2007,51 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
return ret; return ret;
} }
/* Update the sizes of CE fields at end of info->susp_fields and in
single node range of info->ce_susp_fields.
*/
static
int susp_update_CE_sizes(Ecma119Image *t, struct susp_info *info, int flag)
{
size_t i, curr_pos;
uint8_t *curr_ce;
uint32_t size;
if (info->n_susp_fields == 0 ||
info->n_ce_susp_fields - info->current_ce_start == 0)
return ISO_SUCCESS;
if (info->susp_fields[info->n_susp_fields - 1][0] != 'C' ||
info->susp_fields[info->n_susp_fields - 1][1] != 'E') {
iso_msg_submit(t->image->id, ISO_ASSERT_FAILURE, 0,
"Last System Use Area field is not CE, but there are fileds in Continuation Area");
return ISO_ASSERT_FAILURE;
}
curr_ce = info->susp_fields[info->n_susp_fields - 1];
curr_pos = 0;
for (i = info->current_ce_start; i < info->n_ce_susp_fields; i++) {
if (info->ce_susp_fields[i][0] == 0) {
curr_pos = 0; /* pseudo SUSP PAD */
continue;
}
if (info->ce_susp_fields[i][0] == 'C' &&
info->ce_susp_fields[i][1] == 'E') {
size = (curr_pos + info->ce_susp_fields[i][2]) % BLOCK_SIZE;
if (size == 0)
size = BLOCK_SIZE;
iso_bb(curr_ce + 20, size, 4);
curr_ce = info->ce_susp_fields[i];
}
curr_pos = (curr_pos + info->ce_susp_fields[i][2]) % 2048;
}
if (curr_pos > 0) {
size = curr_pos % BLOCK_SIZE;
iso_bb(curr_ce + 20, size, 4);
}
return ISO_SUCCESS;
}
/** /**
* Write the given SUSP fields into buf. Note that Continuation Area * Write the given SUSP fields into buf. Note that Continuation Area
* fields are not written. * fields are not written.
@ -1866,11 +2064,16 @@ void rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info,
{ {
size_t i; size_t i;
size_t pos = 0; size_t pos = 0;
int ret;
if (info->n_susp_fields == 0) { if (info->n_susp_fields == 0) {
return; return;
} }
ret = susp_update_CE_sizes(t, info, 0);
if (ret < 0)
return;
for (i = 0; i < info->n_susp_fields; i++) { for (i = 0; i < info->n_susp_fields; i++) {
memcpy(buf + pos, info->susp_fields[i], info->susp_fields[i][2]); memcpy(buf + pos, info->susp_fields[i], info->susp_fields[i][2]);
pos += info->susp_fields[i][2]; pos += info->susp_fields[i][2];
@ -1896,6 +2099,7 @@ int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info)
size_t i; size_t i;
uint8_t *padding = NULL; uint8_t *padding = NULL;
int ret= ISO_SUCCESS; int ret= ISO_SUCCESS;
uint64_t written = 0, pad_size;
if (info->n_ce_susp_fields == 0) { if (info->n_ce_susp_fields == 0) {
goto ex; goto ex;
@ -1903,11 +2107,24 @@ int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info)
LIBISO_ALLOC_MEM(padding, uint8_t, BLOCK_SIZE); LIBISO_ALLOC_MEM(padding, uint8_t, BLOCK_SIZE);
for (i = 0; i < info->n_ce_susp_fields; i++) { for (i = 0; i < info->n_ce_susp_fields; i++) {
if (info->ce_susp_fields[i][0] == 0) {
/* Pseudo field: pad up to next block boundary */
pad_size = BLOCK_SIZE - (written % BLOCK_SIZE);
if (pad_size == BLOCK_SIZE)
continue;
memset(padding, 0, pad_size);
ret = iso_write(t, padding, pad_size);
if (ret < 0)
goto write_ce_field_cleanup;
written += pad_size;
continue;
}
ret = iso_write(t, info->ce_susp_fields[i], ret = iso_write(t, info->ce_susp_fields[i],
info->ce_susp_fields[i][2]); info->ce_susp_fields[i][2]);
if (ret < 0) { if (ret < 0) {
goto write_ce_field_cleanup; goto write_ce_field_cleanup;
} }
written += info->ce_susp_fields[i][2];
} }
/* pad continuation area until block size */ /* pad continuation area until block size */
@ -1915,6 +2132,9 @@ int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info)
if (i > 0 && i < BLOCK_SIZE) { if (i > 0 && i < BLOCK_SIZE) {
memset(padding, 0, i); memset(padding, 0, i);
ret = iso_write(t, padding, i); ret = iso_write(t, padding, i);
if (ret < 0)
goto write_ce_field_cleanup;
written += i;
} }
write_ce_field_cleanup: ; write_ce_field_cleanup: ;
@ -1925,6 +2145,7 @@ int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info)
free(info->ce_susp_fields); free(info->ce_susp_fields);
info->ce_susp_fields = NULL; info->ce_susp_fields = NULL;
info->n_ce_susp_fields = 0; info->n_ce_susp_fields = 0;
info->alloc_ce_susp_fields = 0;
info->ce_len = 0; info->ce_len = 0;
ex:; ex:;
LIBISO_FREE_MEM(padding); LIBISO_FREE_MEM(padding);

View File

@ -63,10 +63,22 @@ struct susp_info
uint32_t ce_block; uint32_t ce_block;
uint32_t ce_len; uint32_t ce_len;
/* Storage for Continuation Area for a whole directory */
size_t n_ce_susp_fields; size_t n_ce_susp_fields;
uint8_t **ce_susp_fields; uint8_t **ce_susp_fields;
/* The number of allocated members in ce_susp_fields */
size_t alloc_ce_susp_fields;
/* Marks the start index in ce_susp_fields of the current node */
size_t current_ce_start;
}; };
/* Step to increase allocated size of susp_info.ce_susp_fields */
#define ISO_SUSP_CE_ALLOC_STEP 16
/* SUSP 5.1 */ /* SUSP 5.1 */
struct susp_CE { struct susp_CE {
uint8_t block[8]; uint8_t block[8];