From 6fadf3311a2450d4dcb0f6f2346b56aafff92df1 Mon Sep 17 00:00:00 2001 From: ksss Date: Fri, 18 Dec 2020 16:50:11 +0900 Subject: [PATCH] Implement File.birthtime On linux could not use File::Stat#birthtime. But it can call if use `statx(2)` --- Rakefile | 12 ++++++++++++ config.h.in | 3 +++ configure | 11 +++++++++++ configure.ac | 1 + src/file-stat.c | 38 ++++++++++++++++++++++++++++++++++++++ test/file-stat.rb | 8 ++++++++ 6 files changed, 73 insertions(+) diff --git a/Rakefile b/Rakefile index b3ce7cd..0112ec4 100644 --- a/Rakefile +++ b/Rakefile @@ -16,3 +16,15 @@ task :test => "#{mruby_dir}/ci_build_config.rb" do sh "rake -E 'STDOUT.sync=true' test all MRUBY_CONFIG=ci_build_config.rb" end end + +file 'configure' => 'configure.ac' do + sh 'autoconf' +end + +file 'config.h.in' => 'configure.ac' do + sh 'autoheader' + sh 'rm -f config.h.in~' +end + +desc "update" +task :update => ['configure', 'config.h.in'] diff --git a/config.h.in b/config.h.in index 5a6c4ea..ab3b419 100644 --- a/config.h.in +++ b/config.h.in @@ -12,6 +12,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H +/* Define to 1 if you have the `statx' function. */ +#undef HAVE_STATX + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H diff --git a/configure b/configure index 6fc5869..817479d 100755 --- a/configure +++ b/configure @@ -3478,6 +3478,17 @@ _ACEOF fi done +for ac_func in statx +do : + ac_fn_c_check_func "$LINENO" "statx" "ac_cv_func_statx" +if test "x$ac_cv_func_statx" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STATX 1 +_ACEOF + +fi +done + for ac_header in sys/sysmacros.h do : diff --git a/configure.ac b/configure.ac index 2883e8b..43dcda6 100644 --- a/configure.ac +++ b/configure.ac @@ -26,6 +26,7 @@ AC_CHECK_MEMBERS([struct stat.st_birthtimespec]) AC_CHECK_FUNCS(getgroups) AC_CHECK_FUNCS(lstat) +AC_CHECK_FUNCS(statx) AC_CHECK_HEADERS(sys/sysmacros.h) diff --git a/src/file-stat.c b/src/file-stat.c index fa2e000..47d17f5 100644 --- a/src/file-stat.c +++ b/src/file-stat.c @@ -465,6 +465,43 @@ stat_ctime(mrb_state *mrb, mrb_value self) return time_at_with_sec_nsec(mrb, ts.tv_sec, ts.tv_nsec); } +#if defined(HAVE_STATX) +static mrb_value +file_s_birthtime(mrb_state *mrb, mrb_value klass) +{ + struct statx *stx; + char *locale_path; + char *path = NULL; + int statx_result; + + mrb_get_args(mrb, "z", &path); + + locale_path = mrb_locale_from_utf8(path, -1); + statx_result = statx(AT_FDCWD, locale_path, 0, STATX_BTIME, &stx); + mrb_locale_free(locale_path); + if (statx_result == -1) { + mrb_sys_fail(mrb, path); + } + if (!(stx->stx_mask & STATX_BTIME)) { + /* birthtime is not supported on the filesystem */ + errno = ENOSYS; + mrb_sys_fail(mrb, path); + } + return time_at_with_src_nsec(mrb, stx->stx_btime.tv_sec, stx->stx_btime.tv_nsec); +} +#else +static mrb_value +file_s_birthtime(mrb_state *mrb, mrb_value klass) +{ + mrb_value path; + mrb_value stat; + + mrb_get_args(mrb, "S", &path); + stat = mrb_funcall(mrb, klass, "stat", 1, path); + return mrb_funcall(mrb, stat, "birthtime", 0); +} +#endif + #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) static mrb_value stat_birthtime(mrb_state *mrb, mrb_value self) @@ -898,6 +935,7 @@ mrb_mruby_file_stat_gem_init(mrb_state* mrb) mrb_define_method(mrb, io, "stat", io_stat, MRB_ARGS_NONE()); mrb_define_class_method(mrb, file, "lstat", file_s_lstat, MRB_ARGS_REQ(1)); + mrb_define_class_method(mrb, file, "birthtime", file_s_birthtime, MRB_ARGS_REQ(1)); mrb_define_method(mrb, stat, "initialize", stat_initialize, MRB_ARGS_REQ(1)); mrb_define_method(mrb, stat, "initialize_copy", stat_initialize_copy, MRB_ARGS_REQ(1)); diff --git a/test/file-stat.rb b/test/file-stat.rb index 28d871e..c7e94a1 100644 --- a/test/file-stat.rb +++ b/test/file-stat.rb @@ -22,6 +22,14 @@ def chmod(mode, path) assert_kind_of File::Stat, File.lstat('README.md') end +assert 'File.birthtime' do + begin + assert_kind_of Time, File.birthtime('README.md') + rescue NotImplementedError + skip 'This system not supported `birthtime`' + end +end + assert 'File::Stat#initialize_copy' do orig = File::Stat.new('README.md') copy = orig.dup