I do not understand why `touch -h` is not working as documented in the man page on macOS Big Sur
The default touch
utility on macOS Big Sur does not respect the -h
flag.
If one wants to use the -h
option as claimed in the man
pages of Apple, one has to use the GNU version of touch
.
Is there a way to update touch
to a newer version that respects what is written in its man
pages?
I am using macOS Big Sur, with APFS, fully up to date as of 2021-08-15. The default touch
has man pages written in 1995.
Here is an example of what happens:
$ ls -l
total 8
-rw-r--r-- 1 xxx staff 9 Aug 14 10:10 testfile
lrwxr-xr-x 1 xxx staff 8 Aug 14 10:11 testfilelink@ -> testfile
$ touch -h testfilelink
$ ls -l
total 8
-rw-r--r-- 1 xxx staff 9 Aug 14 10:15 testfile
lrwxr-xr-x 1 xxx staff 8 Aug 14 10:11 testfilelink@ -> testfile
$
It seems you have found a bug in macOS. The next-to-last major version of the operating system works according to the manual paage, but the latest version does not.
Either the manual page is outdated, or the software has a bug (most likely).
You can send feedback to Apple as a general user here:'
https://www.apple.com/feedback/macos.html
Or if you are a developer, you can use the Feedback Assistant to file a bug report as described here:
https://developer.apple.com/bug-reporting/
There's currently no newer version of touch
available from Apple so you cannot as such upgrade your way out of the problem. As you indicate yourself, you can instead use the GNU version of touch
to get the job done. You can find that in Homebrew by installing coreutils
.
The GNU version of touch
works by calling the newer futimens()
/utimensat()
functions (in the latter case with the flag
argument set to AT_SYMLINK_NOFOLLOW
to change the timestamp for the link itself).
The Catalina version of touch
(287.100.2) works by calling the older lutimes()
function, which explicitly sets the timestamp on the link itself. One of the core differences between the newer and older APIs are that the newer supports timestamps in nanoseconds, whereas the older has a lower resolution.
The lutimes()
function on Big Sur actually doesn't implement a system call itself, but is actually contained fully in the standards library, using the setattrlist()
function (which results in a system call) to actually perform the file system modification. setattrlist()
is highly file system dependent (i.e. how it works on HFS+ file systems would be different from how it works on APFS file systems).
The Big Sur version of touch
(321.100.11) works by calling the setattrlist()
function directly, and only if that fails, fall back to lutimes
. Unfortunately, it seems the programmer forgot about the need to specify that when -h
is specified, the modification must take place on the link itself.
The actual bug is in line 219 of touch.c, where this line:
if (!setattrlist(*argv, &ts_req, &ts_struct, sizeof(ts_struct), 0))
should have been:
if (!setattrlist(*argv, &ts_req, &ts_struct, sizeof(ts_struct), utimes_f == lutimes ? FSOPT_NOFOLLOW : 0))
You could change that in touch.c, recompile it, and get a working binary.