Daemon News Ezine BSD News BSD Mall BSD Support Forum BSD Advocacy BSD Updates

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

making test(1)'s -nt/-ot ignore nanosecond fractions


I faced an odd situation regarding file timestamps:

    knu@archon[2]% sh
    $ ls a b
    a	b
    $ touch -r a b
    $ [ a -nt b ] && echo "D'oh!"
    $ touch -r a a
    $ [ a -nt b ] || echo "Nah, how did it fix?"
    Nah, how did it fix?

(Note that this is only rarely reproducible)

I tracked it down and found out some facts:

    - File timestamps are recorded in nanoseconds on a UFS, and
      sometimes a file's timestamp actually has >1000 nanosecond
      (= >1 microsecond) value.

    - Stat(2) offers a file's mtime in nanoseconds through

    - sh(1)'s builtin test command, which entity is test(1), compares
      times strictly in nanoseconds when seconds match.

    - On the other hand, there is no API to set a file's mtime in
      nanoseconds; utimes(2) takes time values in microseconds, and
      utime(3), which just calls utimes, takes time values in seconds.

    - Which means touch(1) or any other userland tool can only set a
      file's mtime in microseconds at most.

    - So, copying timestamps from a file "a" to a file "b" leaves a
      under-microsecond fraction only in "a", and after the copy "a"
      is "newer" than "b" by test(1).

In order to fix this, we could consider adding a syscall or extend
utimes(2) to take nanoseconds, but I'm afraid it would take a long
time before it becomes available on -STABLE and the API spread over
third-party applications.  So, I'd suggest the following simple fix to
make test(1) ignore such nanosecond fractions as a temporary measure:

Index: test.c
RCS file: /mirror/freebsd/ncvs/root/src/src/bin/test/test.c,v
retrieving revision 1.52
diff -u -r1.52 test.c
--- test.c	19 Aug 2002 09:19:31 -0000	1.52
+++ test.c	7 May 2004 17:06:54 -0000
@@ -527,7 +527,7 @@
 	if (b1.st_mtimespec.tv_sec < b2.st_mtimespec.tv_sec)
 		return 0;
-       return (b1.st_mtimespec.tv_nsec > b2.st_mtimespec.tv_nsec);
+       return (b1.st_mtimespec.tv_nsec / 1000 > b2.st_mtimespec.tv_nsec / 1000);
 static int

Fortunately, bash and zsh behave the same way, as nanosecond times
support is so heavily system dependent they give up to care about,
which means the above change only makes test(1) compatible with other
shells and would cause any harm.  IMHO, no userland tool should care
about nanosecond fractions when there is no API to set times in


                    /__  __            Akinori.org / MUSHA.org
                   / )  )  ) )  /     FreeBSD.org / Ruby-lang.org
Akinori MUSHA aka / (_ /  ( (__(  @ iDaemons.org / and.or.jp

"It seems to me as we make our own few circles 'round the sun
          We get it backwards and our seven years go by like one"