About | ACP | Buy | Industry Watch | Learning Curve | News | Products | Search | Substack
Home » Learning Curve » Developers Workshop

Hard Links: 3662262 Revisited

Speaking friend still won't get you in.

Get It

Try It

VAXHOLM (Rixstep) — Looking back at the 678 articles in our Learning Curve, we came upon this gem from 2004 - fifteen years ago.


It deals with Unix hard links on macOS (OS X) running HFS.

So why not try it on Apple's new file system, on APFS?

Here goes. We download the first image from the above piece, rename it '1.png', then hard-link it to '2.png'.

$ ln 1.png 2.png

This is what we get.

$ ls -ail
   20760680 drwx------  5 rixstep  staff    160 Feb 11 17:05 .
    1135505 drwx------  6 rixstep  staff    192 Jan 19  2038 ..
12888677090 -r--------  2 rixstep  staff  16182 Feb 11 17:05 1.png
12888677090 -r--------  2 rixstep  staff  16182 Feb 11 17:05 2.png

OK, so now we 'rotate' the image as suggested in the article.

Then we check things again at the command line.

$ ls -ail
   20760680 drwx------  5 rixstep  staff    160 Feb 11 17:06 .
    1135505 drwx------  6 rixstep  staff    192 Jan 19  2038 ..
12888677090 -r--------  1 rixstep  staff  16182 Feb 11 17:05 1.png
12888677420 -r--------  1 rixstep  staff  22266 Feb 11 17:06 2.png

'1.png' and '2.png' have the same inode in the first listing (12888677090) but once we've rotated '2.png', '2.png' acquires a new inode, and the link count of both files is back from 2 to 1.

What gives?

First off, Apple's document controller in 'Cocoa' (NSDocumentController) is still reprogrammed not to save data to existing files, but to obliterate existing files and create new files (with the same name and extended attributes etc). The document controller knows how to deal with this, to not break the 'user experience', but the chain is still broken. The file is not the same file any longer.

Hard-linking from the command line of course still works. It did in the old HFS days too. But a creepy question is 'where does the hard link go?' For in the old HFS days, Apple hid those files (what they called 'multi-linked files') in a secret directory that they made as difficult as they could to find and get into.

Not only was the directory name riddled with impossible characters, but they often (yes they changed the name from time to time) ended the name with a 'carriage return' character, a value not used on Unix systems.

[The carriage-return/line-feed (CRLF) combination was originally two successive bytes, as the ASCII set was used to power teletype machines. The characters did exactly what it sounds like they did: the first pushed the typewriter carriage back to the beginning, and the second flipped it up by one line (line-feed). But, as there was no longer any actual teletype in use, Unix synthesised the sequence to a single line-feed character (which advances an onscreen caret to the beginning of the next line). All vendors followed suit, using line-feed ('\n') for the sequence - all except Apple, who used carriage-return ('\r') for the same purpose. Inserting this character in text on Apple's macOS and most modern systems today will produce surprising results. Ed.]

It used to be possible to see where Apple hid those files, using the API getdirentries() which is the Unix logic supplying data to the directory enumeration API readdir().

But getdirentries() is 32-bit only. Something gets very funky with the followup getdirentries64(). For getdirentries64() is found all over the place. For example:


#define SYS_getdirentries64 344


struct getdirentries64_args {
    char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
    char buf_l_[PADL_(user_addr_t)]; user_addr_t buf; char buf_r_[PADR_(user_addr_t)];
    char bufsize_l_[PADL_(user_size_t)]; user_size_t bufsize; char bufsize_r_[PADR_(user_size_t)];
    char position_l_[PADL_(user_addr_t)]; user_addr_t position; char position_r_[PADR_(user_addr_t)];

int getdirentries64(struct proc *, struct getdirentries64_args *, user_ssize_t *);


typedef struct {
    uint32_t   id;
    const char *name;
} kd_event_t;
kd_event_t kd_events[] = {
    {0x40c0560, "BSC_getdirentries64"},

And yet the IDE (Xcode) will not link to it - no matter the esoteric defines used.

Undefined symbols for architecture x86_64:

And it doesn't seem to matter how you try to trick the linker - by, for example, providing the 64-bit code in a shared library or through a service instead. Apple's Xcode will still spit it out.

So it appears the API really exists, yet can't be accessed without knowing the secret code word.

(Perhaps knowing more about 'struct proc' or 'struct getdirentries64_args' would help?)

[Remember that the original article is from 2004, before Apple changed the behaviour of NSDocumentController. Back then, NSDocumentController wrote to files when saving - it didn't destroy them. Ed.]

See Also
Learning Curve: 3662262

About Rixstep

Stockholm/London-based Rixstep are a constellation of programmers and support staff from Radsoft Laboratories who tired of Windows vulnerabilities, Linux driver issues, and cursing x86 hardware all day long. Rixstep have many years of experience behind their efforts, with teaching and consulting credentials from the likes of British Aerospace, General Electric, Lockheed Martin, Lloyds TSB, SAAB Defence Systems, British Broadcasting Corporation, Barclays Bank, IBM, Microsoft, and Sony/Ericsson.

Rixstep and Radsoft products are or have been in use by Sweden's Royal Mail, Sony/Ericsson, the US Department of Defense, the offices of the US Supreme Court, the Government of Western Australia, the German Federal Police, Verizon Wireless, Los Alamos National Laboratory, Microsoft Corporation, the New York Times, Apple Inc, Oxford University, and hundreds of research institutes around the globe. See here.

All Content and Software Copyright © Rixstep. All Rights Reserved.

John Cattelin
Media Contact
ACP/Xfile licences
About | ACP | Buy | Industry Watch | Learning Curve | News | Products | Search | Substack
Copyright © Rixstep. All rights reserved.