New API call iso_write_opts_set_rrip_tf_long() and date check for year 2156 limit to produce 17-byte timestamps

This commit is contained in:
2025-06-24 17:33:07 +02:00
parent 2d4dd99b46
commit 517a8d9d4a
7 changed files with 227 additions and 31 deletions

View File

@@ -4419,6 +4419,15 @@ int iso_write_opts_set_rrip_1_10_px_ino(IsoWriteOpts *opts, int enable)
return ISO_SUCCESS;
}
int iso_write_opts_set_rrip_tf_long(IsoWriteOpts *opts, int enable)
{
if (opts == NULL) {
return ISO_NULL_POINTER;
}
opts->rrip_tf_long = enable ? 1 : 0;
return ISO_SUCCESS;
}
int iso_write_opts_set_aaip_susp_1_10(IsoWriteOpts *opts, int oldvers)
{
if (opts == NULL) {

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2007 Vreixo Formoso
* Copyright (c) 2009 - 2023 Thomas Schmitt
* Copyright (c) 2009 - 2025 Thomas Schmitt
*
* 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
@@ -215,6 +215,12 @@ struct iso_write_opts {
*/
unsigned int rrip_1_10_px_ino :1;
/**
* Write field TF with timestamps of long form with 17 bytes instead
* of 7-byte timestamps
*/
unsigned int rrip_tf_long :1;
/**
* See iso_write_opts_set_hardlinks()
*/

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2007 Vreixo Formoso
* 2012 - 2014 Thomas Schmitt
* 2012 - 2025 Thomas Schmitt
*
* 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
@@ -77,6 +77,9 @@ struct ecma119_node
/** this field points to the relocated directory. */
Ecma119Node *real_me;
} info;
/* If set to 1, use 17-byte time format in RRIP field TF */
unsigned int rrip_tf_long :1;
};

View File

@@ -1877,6 +1877,19 @@ int iso_write_opts_set_rrip_version_1_10(IsoWriteOpts *opts, int oldvers);
*/
int iso_write_opts_set_rrip_1_10_px_ino(IsoWriteOpts *opts, int enable);
/**
* Force writing of RRIP field TF with LONG FORM timestamps of 17 bytes
* instead of 7-byte timestamps. Without this call or with enable==0, the
* long form is used only with IsoNode objects which bear a timestamp after
* 01 Jan 2150 UTC.
* (Future programmers may widen ISO_RR_SHORT_FORM_TIME_LIMIT in rockridge.h
* to the end of year 2155. The six years inbetween shall serve as chance
* to fix problems.)
*
* @since 1.5.8
*/
int iso_write_opts_set_rrip_tf_long(IsoWriteOpts *opts, int enable);
/**
* Write AAIP as extension according to SUSP 1.10 rather than SUSP 1.12.
* I.e. without announcing it by an ER field and thus without the need

View File

@@ -400,5 +400,6 @@ iso_util_encode_lfa_flags;
iso_util_get_effective_lfa_mask;
iso_util_get_lfa_masks;
iso_write_opts_set_gpt_with_gaps;
iso_write_opts_set_rrip_tf_long;
} LIBISOFS6;

View File

@@ -35,6 +35,7 @@
#endif
#define ISO_CE_ENTRY_SIZE 28
#define ISO_TF_LONG_SIZE 56
static
@@ -44,6 +45,10 @@ static
int susp_make_CE(Ecma119Image *t, uint8_t **CE,
uint32_t block_offset, uint32_t byte_offset, uint32_t size);
static
int add_tf_long(Ecma119Image *t, Ecma119Node *n, struct susp_info *info,
size_t *sua_free, size_t *ce_len, size_t base_ce, int flag);
static
int susp_append(Ecma119Image *t, struct susp_info *susp, uint8_t *data)
@@ -207,28 +212,56 @@ int rrip_add_PX(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp)
* time stamps related to the file (RRIP, 4.1.6).
*/
static
int rrip_add_TF(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp)
int rrip_add_TF(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp,
int to_ca)
{
IsoNode *iso;
uint8_t *TF = malloc(5 + 3 * 7);
uint8_t *TF;
int len;
if (n->rrip_tf_long) {
len = ISO_TF_LONG_SIZE;
} else {
len = 5 + 3 * 7;
}
TF = malloc(len);
if (TF == NULL) {
return ISO_OUT_OF_MEM;
}
TF[0] = 'T';
TF[1] = 'F';
TF[2] = 5 + 3 * 7;
TF[2] = len;
TF[3] = 1;
TF[4] = (1 << 1) | (1 << 2) | (1 << 3);
TF[4] = (1 << 1) | (1 << 2) | (1 << 3) | ((!!n->rrip_tf_long) << 7);
iso = n->node;
iso_datetime_7(&TF[5], t->replace_timestamps ? t->timestamp : iso->mtime,
t->opts->always_gmt);
iso_datetime_7(&TF[12], t->replace_timestamps ? t->timestamp : iso->atime,
t->opts->always_gmt);
iso_datetime_7(&TF[19], t->replace_timestamps ? t->timestamp : iso->ctime,
t->opts->always_gmt);
return susp_append(t, susp, TF);
if (n->rrip_tf_long) {
iso_datetime_17(&TF[5],
t->replace_timestamps ? t->timestamp : iso->mtime,
t->opts->always_gmt);
iso_datetime_17(&TF[5 + 17],
t->replace_timestamps ? t->timestamp : iso->atime,
t->opts->always_gmt);
iso_datetime_17(&TF[5 + 2 * 17],
t->replace_timestamps ? t->timestamp : iso->ctime,
t->opts->always_gmt);
} else {
iso_datetime_7(&TF[5],
t->replace_timestamps ? t->timestamp : iso->mtime,
t->opts->always_gmt);
iso_datetime_7(&TF[12],
t->replace_timestamps ? t->timestamp : iso->atime,
t->opts->always_gmt);
iso_datetime_7(&TF[19],
t->replace_timestamps ? t->timestamp : iso->ctime,
t->opts->always_gmt);
}
if (to_ca) {
return susp_append_ce(t, susp, TF);
} else {
return susp_append(t, susp, TF);
}
}
/**
@@ -1348,6 +1381,15 @@ int susp_calc_nm_sl_al(Ecma119Image *t, Ecma119Node *n, size_t space,
goto unannounced_ca;
}
if (n->rrip_tf_long) {
/* Long TF entry (short TF would be put into SUA unconditionally) */
sua_free = space - *su_size;
add_tf_long(t, n, NULL, &sua_free, ce, base_ce, 1 | (flag & 2));
*su_size = space - sua_free;
if (*ce > 0 && !(flag & 1))
goto unannounced_ca;
}
#ifdef Libisofs_ce_calc_debug_filetraP
if (n->node->name != NULL)
@@ -1432,6 +1474,34 @@ int add_aa_string(Ecma119Image *t, Ecma119Node *n, struct susp_info *info,
return 1;
}
/*
@param flag bit0= only account sizes in sua_free resp. ce_len.
Parameter susp may be NULL in this case
bit1= account for crossing block boundaries
(implied by bit0 == 0)
@param ce_len counts the freshly added CA size of the current node
@param ce_mem tells the CA size of previous nodes in the same directory
*/
static
int add_tf_long(Ecma119Image *t, Ecma119Node *n, struct susp_info *info,
size_t *sua_free, size_t *ce_len, size_t base_ce, int flag)
{
int ret;
if (!(flag & 1))
flag |= 2;
if (*sua_free < ISO_TF_LONG_SIZE || *ce_len > 0) {
susp_calc_add_to_ce(t, ce_len, base_ce, ISO_TF_LONG_SIZE, flag & 2);
} else {
*sua_free -= ISO_TF_LONG_SIZE;
}
if (flag & 1)
return ISO_SUCCESS;
ret = rrip_add_TF(t, n, info, (*ce_len > 0));
return ret;
}
static
void iso_msg_too_many_ce(Ecma119Image *t, Ecma119Node *n, int err)
@@ -1448,6 +1518,37 @@ void iso_msg_too_many_ce(Ecma119Image *t, Ecma119Node *n, int err)
}
/**
* Mark node for 17-byte TF if requested by t->opts->rrip_tf_long
* or if needed to represent the timestamps.
*/
static
void iso_decide_rrip_tf_form(Ecma119Image *t, Ecma119Node *n)
{
IsoNode *iso_node;
/* The decision for long form cannot be revoked */
if (n->rrip_tf_long)
return;
if (t->opts->rrip_tf_long) {
n->rrip_tf_long = 1;
} else {
iso_node= n->node;
if (sizeof(iso_node->atime) > 4) {
/* Check [acm]time for (nearly) year 2156 or later */
if (iso_node->atime > ISO_RR_SHORT_FORM_TIME_LIMIT) {
n->rrip_tf_long = 1;
} else if (iso_node->ctime > ISO_RR_SHORT_FORM_TIME_LIMIT) {
n->rrip_tf_long = 1;
} else if (iso_node->mtime > ISO_RR_SHORT_FORM_TIME_LIMIT) {
n->rrip_tf_long = 1;
}
}
}
}
/**
* Compute the length needed for write all RR and SUSP entries for a given
* node.
@@ -1471,6 +1572,8 @@ ssize_t rrip_calc_len(Ecma119Image *t, Ecma119Node *n, int type, size_t used_up,
int ret, retry = 0;
size_t aaip_sua_free= 0, aaip_len= 0;
iso_decide_rrip_tf_form(t, n);
try_again:
/* Directory record length must be even (ECMA-119, 9.1.13). Maximum is 254.
@@ -1496,11 +1599,14 @@ try_again:
su_size += 5;
#endif
/* PX and TF, we are sure they always fit in SUA */
/* PX and possibly short form TF, we are sure they always fit in SUA */
if (t->opts->rrip_1_10_px_ino || !t->opts->rrip_version_1_10) {
su_size += 44 + 26;
su_size += 44;
} else {
su_size += 36 + 26;
su_size += 36;
}
if (!n->rrip_tf_long) {
su_size += 26;
}
if (n->type == ECMA119_DIR) {
@@ -1561,7 +1667,7 @@ try_again:
* we need to write SP and ER entries. The first fits in SUA,
* ER needs a Continuation Area, thus we also need a CE entry
*/
su_size += 7 + 28; /* SP + CE */
su_size += 7 + ISO_CE_ENTRY_SIZE; /* SP + CE */
t->curr_ce_entries++;
/* ER of RRIP */
if (t->opts->rrip_version_1_10) {
@@ -1582,6 +1688,21 @@ try_again:
if (ret < 0)
return ret;
*ce += aaip_len;
if (n->rrip_tf_long) {
/* Long form TF */
*ce += ISO_TF_LONG_SIZE;
}
} else {
/* Not the /. directory */
if (n->rrip_tf_long) {
/* Long TF surely fits into SUA:
* Record + ER + RR + PX + PL + TF = 156
*/
su_size += ISO_TF_LONG_SIZE;
}
}
}
@@ -1676,7 +1797,7 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
size_t rrip_er_len= 182;
size_t su_size_pd, ce_len_pd; /* predicted sizes of SUA and CA */
int ce_is_predicted = 0;
size_t aaip_sua_free= 0, aaip_len= 0, ce_mem;
size_t aaip_sua_free = 0, aaip_len = 0, ce_mem, tf_len = 0;
int space;
if (t == NULL || n == NULL || info == NULL) {
@@ -1736,14 +1857,21 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
}
#endif /* Libisofs_with_rrip_rR */
/* PX and TF, we are sure they always fit in SUA */
/* PX and short TF, we are sure they always fit in SUA */
ret = rrip_add_PX(t, node, info);
if (ret < 0) {
goto add_susp_cleanup;
}
ret = rrip_add_TF(t, node, info);
if (ret < 0) {
goto add_susp_cleanup;
/* Omit TF here if 17-byte time form is requested.
* In case of omission, TF will be written after NM and others,
* possibly into Continuation Area.
*/
if (!n->rrip_tf_long) {
ret = rrip_add_TF(t, node, info, 0);
if (ret < 0) {
goto add_susp_cleanup;
}
}
if (n->type == ECMA119_DIR) {
@@ -1816,7 +1944,7 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
namelen = strlen(name);
sua_free = space - info->suf_len;
/* Try whether NM, SL, AL will fit into SUA */
/* Try whether NM, SL, AL and possibly long TF will fit into SUA */
su_size_pd = info->suf_len;
ce_len_pd = ce_len;
ret = susp_calc_nm_sl_al(t, n, (size_t) space,
@@ -2075,18 +2203,24 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
}
}
/* Eventually write zisofs ZF field */
/* Possibly write zisofs ZF field */
ret = add_zf_field(t, n, info, &sua_free, &ce_len, ce_mem, 0);
if (ret < 0)
goto add_susp_cleanup;
/* Eventually obtain AAIP field string from node
/* Possibly obtain AAIP field string from node
and write it to directory entry or CE area.
*/
ret = add_aa_string(t, n, info, &sua_free, &ce_len, ce_mem, 0);
if (ret < 0)
goto add_susp_cleanup;
/* In case of short form the TF field was already written above */
if (n->rrip_tf_long) {
ret = add_tf_long(t, n, info, &sua_free, &ce_len, ce_mem, 0);
if (ret < 0)
goto add_susp_cleanup;
}
} else {
@@ -2151,8 +2285,12 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
if (ret < 0)
goto add_susp_cleanup;
if (n->rrip_tf_long)
tf_len = ISO_TF_LONG_SIZE;
/* Allocate the necessary CE space */
ret = susp_add_CE(t, rrip_er_len + aaip_er_len + aaip_len, info);
ret = susp_add_CE(t, rrip_er_len + aaip_er_len + aaip_len + tf_len,
info);
if (ret < 0) {
goto add_susp_cleanup;
}
@@ -2173,6 +2311,21 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
if (ret < 0)
goto add_susp_cleanup;
if (n->rrip_tf_long) {
ret = rrip_add_TF(t, n, info, 1);
if (ret < 0)
goto add_susp_cleanup;
}
} else {
/* Not the /. directory */
if (n->rrip_tf_long) {
/* Long TF surely fits into SUA */
ret = rrip_add_TF(t, node, info, 0);
if (ret < 0)
goto add_susp_cleanup;
}
}
}
@@ -2267,7 +2420,7 @@ int rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info,
uint8_t *buf)
{
size_t i;
size_t pos = 0;
ssize_t pos = 0;
int ret;
if (info->n_susp_fields == 0) {
@@ -2275,14 +2428,17 @@ int rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info,
}
ret = susp_update_CE_sizes(t, info, 0);
if (ret < 0)
return -1;
if (ret < 0) {
pos= -1;
goto cleanup;
}
for (i = 0; i < info->n_susp_fields; i++) {
memcpy(buf + pos, info->susp_fields[i], info->susp_fields[i][2]);
pos += info->susp_fields[i][2];
}
cleanup:
/* free susp_fields */
for (i = 0; i < info->n_susp_fields; ++i) {
free(info->susp_fields[i]);

View File

@@ -1,7 +1,7 @@
/*
* Copyright (c) 2007 Vreixo Formoso
* Copyright (c) 2007 Mario Danic
* Copyright (c) 2009 - 2023 Thomas Schmitt
* Copyright (c) 2009 - 2025 Thomas Schmitt
*
* 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
@@ -87,6 +87,14 @@ struct susp_info
/* Step to increase allocated size of susp_info.ce_susp_fields */
#define ISO_SUSP_CE_ALLOC_STEP 16
/* Upper limit for use of 7-byte time form: 01 Jan 2150 00:00:00 UTC
That is six years before the end of the 7-byte form capacity, just to
allow future software archeologists to widen this time limit to end of
year 2155 so that any problems with the longer TF can be sorted out
before it is too late.
*/
#define ISO_RR_SHORT_FORM_TIME_LIMIT 5680281600
/* SUSP 5.1 */
struct susp_CE {