From 1447b1a02295b7c660cf58e9ea5090bc2c080446 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Tue, 16 Sep 2025 20:46:49 +0200 Subject: [PATCH] New API call iso_write_opts_set_rrip_tf_year0() --- libisofs/ecma119.c | 9 +++++++++ libisofs/ecma119.h | 7 +++++++ libisofs/libisofs.h | 12 ++++++++++++ libisofs/libisofs.ver | 1 + libisofs/rockridge.c | 39 +++++++++++++++++++++++++++++---------- libisofs/rockridge.h | 3 +++ libisofs/util.c | 4 ++-- 7 files changed, 63 insertions(+), 12 deletions(-) diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index 9dacdf7..f5d27c7 100644 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -4428,6 +4428,15 @@ int iso_write_opts_set_rrip_tf_long(IsoWriteOpts *opts, int enable) return ISO_SUCCESS; } +int iso_write_opts_set_rrip_tf_year0(IsoWriteOpts *opts, int enable) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->rrip_tf_year0 = enable ? 1 : 0; + return ISO_SUCCESS; +} + int iso_write_opts_set_aaip_susp_1_10(IsoWriteOpts *opts, int oldvers) { if (opts == NULL) { diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index 6a51337..38ed5eb 100644 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -221,6 +221,13 @@ struct iso_write_opts { */ unsigned int rrip_tf_long :1; + /** + * Enable writing of time values before year 1900 AD in RRIP field TF. + * If enabled, then dates down to the begin of year 0 (= 1 BC) can be + * written into the Rock Ridge data. + */ + unsigned int rrip_tf_year0 :1; + /** * See iso_write_opts_set_hardlinks() */ diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 2845b14..0006677 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -1890,6 +1890,18 @@ int iso_write_opts_set_rrip_1_10_px_ino(IsoWriteOpts *opts, int enable); */ int iso_write_opts_set_rrip_tf_long(IsoWriteOpts *opts, int enable); +/** + * Enable writing of time values before year 1900 AD in RRIP field TF. + * Without this call or with enable==0, such values will be defaulted to + * Jan 1 1900 UTC because Linux up to at least version 6.16 misrepresents times + * before year 1900 as Jan 1 1970 00:00:00 UTC. + * If enabled, then dates down to the begin of year 0 (= 1 BC) can be written + * into the Rock Ridge data. + * + * @since 1.5.8 + */ +int iso_write_opts_set_rrip_tf_year0(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 diff --git a/libisofs/libisofs.ver b/libisofs/libisofs.ver index 108b328..ae96883 100644 --- a/libisofs/libisofs.ver +++ b/libisofs/libisofs.ver @@ -401,5 +401,6 @@ 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; +iso_write_opts_set_rrip_tf_year0; } LIBISOFS6; diff --git a/libisofs/rockridge.c b/libisofs/rockridge.c index 81a632b..ffc415d 100644 --- a/libisofs/rockridge.c +++ b/libisofs/rockridge.c @@ -218,6 +218,7 @@ int rrip_add_TF(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp, IsoNode *iso; uint8_t *TF; int len; + time_t node_time; if (n->rrip_tf_long) { len = ISO_TF_LONG_SIZE; @@ -237,15 +238,27 @@ int rrip_add_TF(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp, iso = n->node; if (n->rrip_tf_long) { + node_time= iso->mtime; + if ((!t->opts->rrip_tf_year0) && + node_time < ISO_RR_SHORT_FORM_TIME_START) + node_time= ISO_RR_SHORT_FORM_TIME_START; iso_datetime_17(&TF[5], - t->replace_timestamps ? t->timestamp : iso->mtime, - t->opts->always_gmt); + t->replace_timestamps ? t->timestamp : node_time, + t->opts->always_gmt); + node_time= iso->atime; + if ((!t->opts->rrip_tf_year0) && + node_time < ISO_RR_SHORT_FORM_TIME_START) + node_time= ISO_RR_SHORT_FORM_TIME_START; iso_datetime_17(&TF[5 + 17], - t->replace_timestamps ? t->timestamp : iso->atime, - t->opts->always_gmt); + t->replace_timestamps ? t->timestamp : node_time, + t->opts->always_gmt); + node_time= iso->ctime; + if ((!t->opts->rrip_tf_year0) && + node_time < ISO_RR_SHORT_FORM_TIME_START) + node_time= ISO_RR_SHORT_FORM_TIME_START; iso_datetime_17(&TF[5 + 2 * 17], - t->replace_timestamps ? t->timestamp : iso->ctime, - t->opts->always_gmt); + t->replace_timestamps ? t->timestamp : node_time, + t->opts->always_gmt); } else { iso_datetime_7(&TF[5], t->replace_timestamps ? t->timestamp : iso->mtime, @@ -1537,12 +1550,18 @@ void iso_decide_rrip_tf_form(Ecma119Image *t, Ecma119Node *n) } 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) { + /* Check [acm]time for year 1900 and (nearly) year 2156 */ + if (iso_node->atime > ISO_RR_SHORT_FORM_TIME_LIMIT || + (iso_node->atime < ISO_RR_SHORT_FORM_TIME_START && + t->opts->rrip_tf_year0)) { n->rrip_tf_long = 1; - } else if (iso_node->ctime > ISO_RR_SHORT_FORM_TIME_LIMIT) { + } else if (iso_node->ctime > ISO_RR_SHORT_FORM_TIME_LIMIT || + (iso_node->ctime < ISO_RR_SHORT_FORM_TIME_START && + t->opts->rrip_tf_year0)) { n->rrip_tf_long = 1; - } else if (iso_node->mtime > ISO_RR_SHORT_FORM_TIME_LIMIT) { + } else if (iso_node->mtime > ISO_RR_SHORT_FORM_TIME_LIMIT || + (iso_node->mtime < ISO_RR_SHORT_FORM_TIME_START && + t->opts->rrip_tf_year0)) { n->rrip_tf_long = 1; } } diff --git a/libisofs/rockridge.h b/libisofs/rockridge.h index d59ab81..2de94be 100644 --- a/libisofs/rockridge.h +++ b/libisofs/rockridge.h @@ -95,6 +95,9 @@ struct susp_info */ #define ISO_RR_SHORT_FORM_TIME_LIMIT 5680281600 +/* Lower limit for use of 7-byte time form: 01 Jan 1900 00:00:00 UTC +*/ +#define ISO_RR_SHORT_FORM_TIME_START -2208988800 /* SUSP 5.1 */ struct susp_CE { diff --git a/libisofs/util.c b/libisofs/util.c index 0a62656..0c32493 100644 --- a/libisofs/util.c +++ b/libisofs/util.c @@ -1674,8 +1674,8 @@ void iso_datetime_17(unsigned char *buf, time_t t, int always_gmt) tzoffset = 0; } - if (tm.tm_year <= -1900) { - strcpy((char *) buf, "00010101000000"); + if (tm.tm_year < -1900) { + strcpy((char *) buf, "00000101000000"); } else if (tm.tm_year >= 8100) { strcpy((char *) buf, "99991231235959"); } else {