Skip to content

Instantly share code, notes, and snippets.

@zhangyoufu
Created August 25, 2022 10:47
Show Gist options
  • Select an option

  • Save zhangyoufu/825e14d69c82464a16688977f791a3c8 to your computer and use it in GitHub Desktop.

Select an option

Save zhangyoufu/825e14d69c82464a16688977f791a3c8 to your computer and use it in GitHub Desktop.
create symlink on exFAT filesystem (macOS way)
#!/usr/bin/env python3
import argparse
import hashlib
import os
SMB_SYMHDRLEN = (4+1)+(4+1)+(32+1)
MAXPATHLEN = 0x400
# see smbfs_create_windows_symlink_data
def symlink(target: str, link: str) -> None:
if os.path.exists(link):
raise FileExistsError
body = target.encode()
if len(body) > MAXPATHLEN:
raise ValueError
hdr = f'Xsym\n{len(body):04d}\n{hashlib.md5(body).hexdigest()}\n'.encode()
assert len(hdr) == SMB_SYMHDRLEN
if len(body) < MAXPATHLEN:
body += b'\n'
body = body.ljust(MAXPATHLEN, b' ')
with open(link, 'wb') as f:
f.write(hdr + body)
def main() -> None:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('target')
parser.add_argument('link')
args = parser.parse_args()
symlink(args.target, args.link)
if __name__ == '__main__':
main()
@chinarut

chinarut commented Jan 3, 2025

Copy link
Copy Markdown

does this script need to be updated for macOS Sequoia?

I know it is still possible as I found symlinks rsync created on an ExFAT partition inside an app bundle:

Sparkle.framework % ls -altr
total 2304
drwx------@ 1 chinarut  staff  131072 Mar 24  2024 Versions
drwx------@ 1 chinarut  staff  131072 Mar 24  2024 ..
drwx------@ 1 chinarut  staff  131072 Mar 24  2024 .
lrwx------  1 chinarut  staff      24 Mar 24  2024 Sparkle -> Versions/Current/Sparkle
lrwx------  1 chinarut  staff      26 Mar 24  2024 Resources -> Versions/Current/Resources
lrwx------  1 chinarut  staff      31 Mar 24  2024 PrivateHeaders -> Versions/Current/PrivateHeaders
lrwx------  1 chinarut  staff      24 Mar 24  2024 Modules -> Versions/Current/Modules
lrwx------  1 chinarut  staff      24 Mar 24  2024 Headers -> Versions/Current/Headers
-rwx------  1 chinarut  staff    4096 Oct 29 23:35 ._Versions

I was actually quite surprised!

@zhangyoufu

Copy link
Copy Markdown
Author

does this script need to be updated for macOS Sequoia?

I have no idea what do you mean. Per my understanding, this should work against latest macOS, but I didn’t verify that.

My use case is creating “symlink” files on Windows/Linux. When I unplug the HDD and connect to macOS, they are treated as real symlinks. I never ran this script directly under macOS.

@zhangyoufu

Copy link
Copy Markdown
Author

@udance4ever

Copy link
Copy Markdown

ah - thanks for clarifying your context. I didn't realize ln actually works on an exFAT partition in macOS out of the box (just tried it).

but for what it's worth, this is what the script creates in macOS:

% cat > foo
hello!
% ln -s goo foo
% ~/bin/ln.py hoo foo
% cat goo
hello!
% cat hoo
Xsym
0003
acbd18db4cc2f85cedef654fccc4a4d8
foo

% ls -al
total 1280
drwx------  1 squatter  staff  131072 Dec  4 17:36 .
drwx------@ 1 squatter  staff  131072 Dec 31  1969 ..
-rwx------  1 squatter  staff       0 Dec  4 17:36 _created_macOS_
-rwx------  1 squatter  staff       7 Jan 10 17:11 foo
lrwx------  1 squatter  staff       3 Jan 10 17:09 goo -> foo
-rwx------  1 squatter  staff    1067 Jan 10 17:12 hoo

here's an easy revision:

	if os.uname().sysname == "Darwin":
		os.symlink(args.target, args.link)
	else:
		symlink(args.target, args.link)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment