1

Across operating systems and when using git, I am trying to figure out how a relative symlink behaves when it is "inside" another symlink.

Here is an example:

a (file)
b (folder)
  a (symlink to `../a`)
c (folder)
  a (file)
  b (symlink to `../b`)

Will /c/b/a refer to /a or /c/a? I am hoping that it refers to /a. Is this consistent across linux, windows, macos and git? If I push such a structure to git, and pull it again, will I get my desired behaviour?

1 Answers1

3

POSIX-compliant systems

POSIX pathname resolution seems to be well-defined. Expect Linux and macOS to be POSIX-compliant in this matter.

The rules for symlinks and .. are there. The concept of predecessor means in order to resolve /c/b/a the process needs to resolve /, c, b and a in this exact order. In details it needs to:

  • resolve /, the result is /;
  • resolve c in the result of the previous step:
    • find c in /, i.e. /c,
    • learn it's a directory,
    • the result is /c;
  • resolve b in the result of the previous step:
    • find b in /c, i.e. /c/b,
    • learn it's a symlink,
    • substitute b with the content of the symlink: /c/b becomes /c/../b,
    • resolve /c/.. as /,
    • find b in /, i.e. /b,
    • learn it's a directory,
    • the result is /b;
  • resolve a in the result of the previous step:
    • find a in /b, i.e. /b/a,
    • learn it's a symlink (in some circumstances the procedure stops here and the final result is /b/a; this happens if "the function is required to act on the symbolic link itself, or certain arguments direct that the function act on the symbolic link itself"),
    • substitute a with the content of the symlink: /b/a becomes /b/../a,
    • resolve /b/.. as /,
    • find a in /, i.e. /a,
    • learn it's not a symlink,
    • the final result is /a.

The result is never /c/a. To get /c/a the process would need to resolve a existing in /b, while still referring to b as /c/b; so at some point it would parse /c/b/../a which would collapse to /c/a. But to find a in b it needs to get from /c/b to /c/../b to /b first. Only then a is resolved.

However if /c/b was a directory and /b was bind-mounted there, then /c/b/a would indeed resolve to /c/a. The crucial difference is /c/b would not be a symlink in this case.

If the original pathname contained .., then some tools might resolve symlinks after processing instances of ... Example: cd -L; compare this answer. But in your case .. appear from symlinks, so this issue does not apply.


Git

Git was created by Linus Torvalds, the principal developer of the Linux kernel. I guess it's fully compatible with the Linux way of interpreting symlinks. I have no experience with Git at all, so I cannot be sure. Still, after reading How does Git handle symbolic links? I believe there is no problem in "pushing such a structure to Git, and pulling it again".


Windows

Microsoft says:

Symbolic links are designed to aid in migration and application compatibility with UNIX operating systems. Microsoft has implemented its symbolic links to function just like UNIX links.

Wikipedia states few differences though. In the context of your question this may be relevant:

In Windows Vista and later, when the working directory path ends with a symbolic link, the current parent path reference, .., will refer to the parent directory of the symbolic link rather than that of its target. This behaviour is also found at the shell level in at least some POSIX systems, including Linux, but never in accessing files and directories through operating system calls.

(This behavior at the shell level in Linux was mentioned above.)

Plus the fact that non-administrative users cannot create symlinks by default.

In Windows 7, NTFS, I have created the desired directory structure and performed few tests. Command line tools (e.g. copy) seem to resolve \c\b\a to \a. But explorer.exe, after navigating to \c\b and double-clicking a, opens \c\a. On the other hand if I drag a and make a copy, the content of \a gets copied.

And I noticed I can go to \c\b from \c by double-clicking b in the main area of the window; but if I choose \c\b in the tree (navigation pane) then the program jumps to \b where a is always \a.

Quite inconsistent. I haven't tested newer Windows systems, maybe they are better.