Currently, we use -T to set a given UNIX timestamp for all files, yet reproducible builds [1] requires what is called "timestamp clamping", IOW, a timestamp must be used no later than the value of this variable.
Let's support $SOURCE_DATE_EPOCH as well. [1] https://reproducible-builds.org/specs/source-date-epoch/ Suggested-by: nl6720 <nl6...@gmail.com> Signed-off-by: Gao Xiang <hsiang...@redhat.com> --- include/erofs/config.h | 7 +++++++ lib/inode.c | 15 +++++++++++++-- mkfs/main.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/include/erofs/config.h b/include/erofs/config.h index e425ce2..02ddf59 100644 --- a/include/erofs/config.h +++ b/include/erofs/config.h @@ -29,11 +29,18 @@ enum { FORCE_INODE_EXTENDED, }; +enum { + TIMESTAMP_NONE, + TIMESTAMP_FIXED, + TIMESTAMP_CLAMPING, +}; + struct erofs_configure { const char *c_version; int c_dbg_lvl; bool c_dry_run; bool c_legacy_compress; + char c_timeinherit; #ifdef HAVE_LIBSELINUX struct selabel_handle *sehnd; diff --git a/lib/inode.c b/lib/inode.c index 5695bbc..fee5c96 100644 --- a/lib/inode.c +++ b/lib/inode.c @@ -729,8 +729,19 @@ int erofs_fill_inode(struct erofs_inode *inode, inode->i_mode = st->st_mode; inode->i_uid = st->st_uid; inode->i_gid = st->st_gid; - inode->i_ctime = sbi.build_time; - inode->i_ctime_nsec = sbi.build_time_nsec; + inode->i_ctime = st->st_ctime; + inode->i_ctime_nsec = st->st_ctim.tv_nsec; + + switch (cfg.c_timeinherit) { + case TIMESTAMP_CLAMPING: + if (st->st_ctime < sbi.build_time) + break; + case TIMESTAMP_FIXED: + inode->i_ctime = sbi.build_time; + inode->i_ctime_nsec = sbi.build_time_nsec; + default: + break; + } inode->i_nlink = 1; /* fix up later if needed */ switch (inode->i_mode & S_IFMT) { diff --git a/mkfs/main.c b/mkfs/main.c index 6dda9e3..5c41fc0 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -198,6 +198,7 @@ static int mkfs_parse_options_cfg(int argc, char *argv[]) erofs_err("invalid UNIX timestamp %s", optarg); return -EINVAL; } + cfg.c_timeinherit = TIMESTAMP_FIXED; break; case 2: opt = erofs_parse_exclude_path(optarg, false); @@ -381,6 +382,33 @@ static void erofs_mkfs_generate_uuid(void) erofs_info("filesystem UUID: %s", uuid_str); } +/* https://reproducible-builds.org/specs/source-date-epoch/ for more details */ +int parse_source_date_epoch(void) +{ + char *source_date_epoch; + unsigned long long epoch = -1ULL; + char *endptr; + + source_date_epoch = getenv("SOURCE_DATE_EPOCH"); + if (!source_date_epoch) + return 0; + + epoch = strtoull(source_date_epoch, &endptr, 10); + if (epoch == -1ULL || *endptr != '\0') { + erofs_err("Environment variable $SOURCE_DATE_EPOCH %s is invalid", + source_date_epoch); + return -EINVAL; + } + + if (cfg.c_force_inodeversion != FORCE_INODE_EXTENDED) + erofs_info("SOURCE_DATE_EPOCH is set, forcely generate extended inodes instead"); + + cfg.c_force_inodeversion = FORCE_INODE_EXTENDED; + cfg.c_unix_timestamp = epoch; + cfg.c_timeinherit = TIMESTAMP_CLAMPING; + return 0; +} + int main(int argc, char **argv) { int err = 0; @@ -405,6 +433,12 @@ int main(int argc, char **argv) return 1; } + err = parse_source_date_epoch(); + if (err) { + usage(); + return 1; + } + err = lstat64(cfg.c_src_path, &st); if (err) return 1; -- 2.18.1