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

Reply via email to