Ticket #4542 (closed defect: fixed)

Opened 4 months ago

Last modified 2 months ago

Nanosecond precision timestamps support broken on macOS / BSD / AIX / Solaris

Reported by: zaytsev Owned by: zaytsev
Priority: major Milestone: 4.8.32
Component: mc-vfs Version: master
Keywords: Cc: ag
Blocked By: Blocking: #3960
Branch state: merged Votes for changeset: committed-master

Description (last modified by zaytsev) (diff)

Followup ticket to:

Sadly no-one came around to fix this, so packagers still disable the support manually:

https://github.com/Homebrew/homebrew-core/blob/master/Formula/m/midnight-commander.rb
https://github.com/macports/macports-ports/blame/master/sysutils/mc/Portfile

Here are the notes how to possible fix this:

---

Unfortunately, I think that the easiest way to access XNU sources from macOS 10.13 right now would be to get hired as a kernel developer by Apple. The system is not even officially released to the public yet (target date is 25th of September), and sources are usually released some time after the final release.

Anyways, I believe that source code access is not really necessary in the first place, as I would speculate that all one needs to do is to add

AC_CHECK_MEMBERS([struct stat.st_blksize, struct stat.st_rdev, struct stat.st_mtim, struct stat.st_mtimespec])

and wherever we use HAVE_STRUCT_STAT_ST_MTIM *or* HAVE_UTIMENSAT replace it with

#ifdef HAVE_STRUCT_STAT_ST_MTIM
    st.st_atim.tv_nsec = st.st_mtim.tv_nsec = st.st_ctim.tv_nsec = 0;
#elif defined HAVE_STRUCT_STAT_ST_MTIMESPEC
    st.st_atimespec.tv_nsec = st.st_mtimespec.tv_nsec = st.st_mtimespec.tv_nsec = 0;
#endif

or something along these lines and you will magically not only fix the build, but gain nanosecond precision support for macOS 10.13+ *and* BSD at the same time. Of course, cruft keeps accumulating, so ideally one should think about defining clever struct manipulation functions limiting define proliferation...

Better yet would be to undertake a major refactoring to clean up our time mess afterwards, but that's, of course, just wishful thinking.

Attachments

Screenshot 2024-07-27 at 22.04.32.png (1.5 MB) - added by zaytsev 2 months ago.

Change History

comment:1 Changed 4 months ago by zaytsev

  • Status changed from new to accepted
  • Owner set to zaytsev

comment:2 Changed 4 months ago by zaytsev

Apparently, macOS fixed itself: https://github.com/Homebrew/homebrew-core/pull/173306

Need to check what's up in the BSD land.

comment:3 Changed 4 months ago by zaytsev

stat -f %Fm filename can be used if ls is unable to show nanosecond timestamps.

comment:5 Changed 4 months ago by zaytsev

Related code: #3960

comment:6 Changed 4 months ago by zaytsev

Related code: #3927

comment:7 Changed 4 months ago by zaytsev

  • Blocking 3960 added

comment:8 Changed 4 months ago by zaytsev

  • Branch state changed from no branch to on review
  • Milestone changed from Future Releases to 4.8.32

Branch: 4542_nanoseconds_cleanup
Initial changeset: 934efd96383900ce38fb55d5592f6ac2aecb1280

Hi Andrew, appreciate reviewing this stuff, maybe you can check on Linux? I've checked it on macOS and it works, will be trying on AIX shortly, it's a bit of a pain to build. I hope my idea is clear. If not just ask. Please feel free to commit fixups directly if you don't like something. I tried my best to keep to your style.

Version 1, edited 4 months ago by zaytsev (previous) (next) (diff)

comment:9 Changed 4 months ago by andrew_b

Let's move cleanup stuff to new branch 4524_cleanup or commit it directly to master.

Move #include <config.h> to .h is not a good idea. config.h must be included first in .c files, but indirectly includes via other headers became it not first.
As a result, ENABLE_EXT2FS_ATTR is not defined in local.c here:

#ifdef ENABLE_EXT2FS_ATTR
#include <e2p/e2p.h>            /* fgetflags(), fsetflags() */
#endif

but defined here:

  CC       local.lo
src/vfs/local/local.c: In function 'local_fgetflags':
src/vfs/local/local.c:198:12: warning: implicit declaration of function 'fgetflags'; did you mean 'mc_fgetflags'? [-Wimplicit-function-declaration]
     return fgetflags (path, flags);
            ^~~~~~~~~
            mc_fgetflags
src/vfs/local/local.c: In function 'local_fsetflags':
src/vfs/local/local.c:209:12: warning: implicit declaration of function 'fsetflags'; did you mean 'mc_fsetflags'? [-Wimplicit-function-declaration]
     return fsetflags (path, flags);
            ^~~~~~~~~
            mc_fsetflags
Last edited 4 months ago by andrew_b (previous) (diff)

comment:10 follow-up: ↓ 11 Changed 4 months ago by zaytsev

Let's move cleanup stuff to new branch 4524_cleanup or commit it directly to master.

Sorry, I don't get what you want. I think it's better to have a separate branch for nanoseconds stuff instead of doing everything in cleanup. It's a new feature supporting it correctly on AIX & BSDs. The commits doing the refactoring make it possible to implement the new feature centrally, so I would also keep them to this branch.

If you want to take changeset:607fe8 and changeset:c50106 elsewhere, no problem, but I would prefer master, so that I can rebase this branch and continue testing. Regarding changeset:c50106, I'm not sure if something else has to be changed / removed. The function was present before glib 2.31 according to git blame of their tree, so maybe something went wrong at some point?

Move #include <config.h> to .h is not a good idea. config.h must be included first in .c files, but indirectly includes via other headers became it not first.

Ah, ok, thank you, I put it back. I thought it was unused, but I was wrong.

But am I allowed also to use config.h in h-files? Are there any rules for that? I need to know the structure of the struct and I want the functions to be in h-files, so that they can be inlined everywhere, where they are used.

comment:11 in reply to: ↑ 10 Changed 4 months ago by andrew_b

Replying to zaytsev:

If you want to take changeset:607fe8 and changeset:c50106 elsewhere, no problem, but I would prefer master,

Ok.

Regarding changeset:c50106, I'm not sure if something else has to be changed / removed. The function was present before glib 2.31 according to git blame of their tree, so maybe something went wrong at some point?

That's my mistake. There was no need to redefine g_direct_equal() in our code because it's implemented in glib since 2005.

Move #include <config.h> to .h is not a good idea. config.h must be included first in .c files, but indirectly includes via other headers became it not first.

Ah, ok, thank you, I put it back. I thought it was unused, but I was wrong.

But am I allowed also to use config.h in h-files?

I've never seen <config.h> included in h-files, only in c-files.

Are there any rules for that?

Probably yes, but Google can't help me.

I need to know the structure of the struct and I want the functions to be in h-files, so that they can be inlined everywhere, where they are used.

Your h-files are included in c-files after config.h so no problems.
But why you want make function inline?

comment:12 Changed 4 months ago by zaytsev

I've never seen <config.h> included in h-files, only in c-files.

Okay, I understand, so the rule is to use config.h in C-files so that it doesn't contaminate other files per mistake if used in an H-file, and use it first. I can check if there are other H-files where we include it by mistake.

But why you want make function inline?

These are nothing but accessor functions that reshuffle struct fields. Before my changes it was handled at every place where it was needed in the code. Now everywhere there is an extra function call, but the code is reused. I wanted to make it a function and not a macro, because macros are evil and non-type-safe. But it has some performance impact. For inline functions the impact is zero.

But if we don't care, then I can move these functions in a C-file, probably the impact will be some microseconds anyways which are not important...

comment:13 Changed 4 months ago by zaytsev

I have added a commit implementing this, took the two build fixes in master and rebased the branch squashing the fixups.

comment:14 Changed 4 months ago by andrew_b

I've created an alternative branch with some fixups: 4542_nanoseconds_2
Initial changeset:e89d65e17053e10912a2b1ed3afc9be06ec8c746

comment:15 Changed 4 months ago by andrew_b

  • Type changed from enhancement to defect
  • Component changed from mc-core to mc-vfs

comment:16 follow-up: ↓ 17 Changed 3 months ago by zaytsev

I'm a bit concerned about you removing config.h from vfs.h - it means vfs.h must be only included in C-files which include config.h themselves to work correctly. But I guess this is C life :-(

Thank you for the rest of the changes, I think I tried %ju but got some problems on Mac and AIX, and decided to use %llu. I will test again on Mac, AIX and I also wanted to check Solaris. Then report back.

comment:17 in reply to: ↑ 16 Changed 3 months ago by andrew_b

Replying to zaytsev:

I'm a bit concerned about you removing config.h from vfs.h - it means vfs.h must be only included in C-files which include config.h themselves to work correctly. But I guess this is C life :-(

Yes. We do compile c-files, not h-files. c-file must begin with include of config.h, then include system-wide h-files (from /usr/include), then project h-files.

Thank you for the rest of the changes, I think I tried %ju but got some problems on Mac and AIX, and decided to use %llu. I will test again on Mac, AIX and I also wanted to check Solaris. Then report back.

I get a warning for %llu:

src/vfs/shell/shell.c: In function 'shell_utime':
src/vfs/shell/shell.c:1490:69: warning: format '%llu' expects argument of type 'long long unsigned int', but argument 7 has type 'long unsigned int' [-Wformat=]
 1490 |                               "SHELL_FILENAME=%s SHELL_FILEATIME=%llu SHELL_FILEMTIME=%llu "
      |                                                                  ~~~^
      |                                                                     |
      |                                                                     long long unsigned int
      |                                                                  %lu
 1491 |                               "SHELL_TOUCHATIME=%s SHELL_TOUCHMTIME=%s SHELL_TOUCHATIME_W_NSEC=\"%s\" "
 1492 |                               "SHELL_TOUCHMTIME_W_NSEC=\"%s\";\n", rpath, (uintmax_t) atime.tv_sec,
      |                                                                           ~~~~~~~~~~~~~~~~~~~~~~~~
      |                                                                           |
      |                                                                           long unsigned int
src/vfs/shell/shell.c:1490:90: warning: format '%llu' expects argument of type 'long long unsigned int', but argument 8 has type 'long unsigned int' [-Wformat=]
 1490 |                               "SHELL_FILENAME=%s SHELL_FILEATIME=%llu SHELL_FILEMTIME=%llu "
      |                                                                                       ~~~^
      |                                                                                          |
      |                                                                                          long long unsigned int
      |                                                                                       %lu
......
 1493 |                               (uintmax_t) mtime.tv_sec, utcatime, utcmtime, utcatime_w_nsec,
      |                               ~~~~~~~~~~~~~~~~~~~~~~~~                                    
      |                               |
      |                               long unsigned int

and system_data_types(7) says:

The length modifier for uintmax_t for the printf(3) and the scanf(3)
families of functions is j; resulting commonly in %ju or %jx for printing
uintmax_t values.

Conforming to: C99 and later; POSIX.1-2001 and later.

comment:18 Changed 3 months ago by andrew_b

@zaytsev: well? What is your plan here?

comment:19 Changed 3 months ago by zaytsev

Been busy with other stuff, but probably should just test on Solaris and be done with it. Will see how I can get back to it.

comment:22 Changed 2 months ago by zaytsev

Started building on Solaris, still struggling to compile the dependencies. Libffi is done:

https://github.com/libffi/libffi/pull/846

MAKE=gmake AR=gar READELF=greadelf \
../configure --disable-static --enable-shared --prefix=/home/zaytsev/opt/libffi

Glib needs more work.

MAKE=gmake AR=gar \
CFLAGS=-D_XPG6 \
LIBFFI_LIBS=$HOME/opt/libffi/lib LIBFFI_CFLAGS=-I$HOME/opt/libffi/include \
../configure \
--disable-static --enable-shared \
--with-libiconv=gnu --disable-dtrace --disable-compile-warnings \
--prefix=/home/zaytsev/opt/glib

Glib is done with lots of makefile editing to remove -Wl,... and disable linking binaries / tests. Maybe newer versions than glib-2.43.92.tar.xz are not as bad, but at some point they started depending on a non-autotools build system... even more pain.

Slang was "easy":

MAKE=gmake CFLAGS=-D_XPG6 ./configure --prefix=$HOME/opt/slang
gmake install

mc was also okay, but patches are needed (see below):

XGETTEXT=gxgettext ./autogen.sh

GLIB_LIBS="-L$HOME/opt/glib/lib -lglib-2.0" \
GLIB_CFLAGS="-I$HOME/opt/glib/include/glib-2.0 -I$HOME/opt/glib/lib/glib-2.0/include" \
SLANG_LIBS="-L$HOME/opt/slang/lib -lslang" SLANG_CFLAGS=-I$HOME/opt/slang/include \
MAKE=gmake \
../configure --prefix=$HOME/opt/mc
Last edited 2 months ago by zaytsev (previous) (diff)

comment:23 Changed 2 months ago by zaytsev

May I commit the following two patches directly to master?

From 1be8bbb4cf3667317f4361c0b749d9ee68032cf1 Mon Sep 17 00:00:00 2001
From: "Yury V. Zaytsev" <yury@shurup.com>
Date: Sat, 27 Jul 2024 21:17:52 +0200
Subject: [PATCH 1/2] buildsys: support bootstrapping on Solaris by using
 backticks in autogen.sh

Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
---
 autogen.sh | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/autogen.sh b/autogen.sh
index ee76efc22..8c06da89c 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -2,7 +2,9 @@
 
 set -e
 
-srcdir="$(cd "$(dirname "$0")" && pwd)"
+# Use backticks to support ksh on Solaris
+basedir=`dirname "$0"`
+srcdir=`cd "$basedir" && pwd`
 
 cd "$srcdir"
 
-- 
2.39.3 (Apple Git-146)
From 889ef3d4a7f3048ff9979e2fc6332d93f0242eed Mon Sep 17 00:00:00 2001
From: "Yury V. Zaytsev" <yury@shurup.com>
Date: Sat, 27 Jul 2024 21:18:48 +0200
Subject: [PATCH 2/2] buildsys: fix ar check by doing it before toolchain
 checks implicitly looking for ar

Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
---
 configure.ac | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 12556f563..21c0ca71b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,6 +36,10 @@ dnl ############################################################################
 dnl Check for compiler
 dnl ############################################################################
 
+dnl This should be checked before toolchain macros, otherwise they will remember
+dnl that ar cannot be found and linking via libtool will fail at a later stage
+AC_CHECK_TOOLS([AR], [ar gar])
+
 AC_PROG_CC
 AM_PROG_CC_C_O
 
@@ -211,8 +215,7 @@ dnl ############################################################################
 dnl Check for other tools
 dnl ############################################################################
 
-AC_CHECK_TOOL(AR, ar, ar)
-AC_CHECK_TOOL(INDENT, gindent, indent)
+AC_CHECK_TOOLS([INDENT], [gindent indent])
 mc_UNIT_TESTS
 
 
-- 
2.39.3 (Apple Git-146)

comment:24 Changed 2 months ago by zaytsev

N.B. Tested nanosecond timestamps on Solaris, they work now. Solaris supports ls -alsE to show them.

Changed 2 months ago by zaytsev

comment:25 follow-up: ↓ 29 Changed 2 months ago by zaytsev

I've retested on AIX 7.3 and it looks really good. The timestamps are also preserved.

I've looked at your changes, Andrew, and I agree with all of them, thank you for your help! Without you I've probably wouldn't come that far. How do we go about merging this branch? Shall I take yours, rebase and merge? Or you want to do this?

comment:26 Changed 2 months ago by zaytsev

Last edited 2 months ago by zaytsev (previous) (diff)

comment:27 Changed 2 months ago by zaytsev

  • Description modified (diff)

comment:28 Changed 2 months ago by zaytsev

  • Summary changed from Timestamps with nanosecond precision support is broken on macOS to Nanosecond precision timestamps support broken on macOS / BSD / AIX / Solaris

comment:29 in reply to: ↑ 25 Changed 2 months ago by andrew_b

  • Votes for changeset set to andrew_b
  • Branch state changed from on review to approved

Replying to zaytsev:

Shall I take yours, rebase and merge?

Yes, please.

Replying to zaytsev:

May I commit the following two patches directly to master?

Why not to the current branch before merge?

comment:30 Changed 2 months ago by zaytsev

Why not to the current branch before merge?

No problem, can do that :) Last time you didn't want "unrelated" patches fixing the build system in the topic branches, which is why I asked.

comment:31 Changed 2 months ago by zaytsev

  • Status changed from accepted to testing
  • Resolution set to fixed

comment:32 Changed 2 months ago by zaytsev

  • Status changed from testing to closed

comment:33 Changed 2 months ago by zaytsev

  • Votes for changeset changed from andrew_b to committed-master
  • Branch state changed from approved to merged
Note: See TracTickets for help on using tickets.