ODS-5 (or VMS/Posix) symlinks:

The easy part: a symbolic link file is special. It has the file organization
FAT_SPECIAL and the record attribute FAT_SYMBOLIC_LINK.

The contents is a text pointing to another file.

So far so good. Now, the text is a Posix file specification!

    $ CREATE/SYMLINK="../hello" WORLD

creates a symbolic link, the symbolic link file is WORLD.;1 and the symbolic
link points to the file hello in the parent directory.

Obviously the symbolic link is not an ODS-5 file specification. How is the
target found? With translating it to an ODS-5 file, the latest version of it.

For example

    $ DIRECTORY [-]

    HELLO.;7 Hello.;16 Hello.;15 hello.;3 hello.DIR;1



prints the contents of the file hello.;3

    $ DIR [.WORLD]

gives the files in the directory [-.hello]

If hello.;3 is deleted, then


fails or prints the contents of the file HELLO.;7

This all depends on the VMS process context. If the context is case sensitive,
then the target is not found and a VMS error FNF is returned. If the context
is case blind, the default, then the contents of the file HELLO.;7 is

Similar, if the context is case blind and if HELLO.;7 is deleted,


prints the contents of the file Hello.;16

So far so good(?). One user may have created symbolic links in the case
blind context, expecting "../hello" being matched with "[-]HELLO.;7" (This
is usually the case because users tend to type all commands and filenames in
lowercase: '$ create/symlink="../hello" world' and create/symlink creates a
lowercase link text !!!.) On the same disk another user may have created
other symbolic links in the case sensitive context. In other words, looking
up the target is not determined by the file system on the disk. A Linux
file system for ODS-5 can't pick up the right or a one-fits-all context. It also
can't change the context based on the process. A Linux process has a case
sensitive context. Therefore the lookup and match in the file system for ODS-5
is case sensitive. There is no exception and no option at mount time. If the
VMS user got it wrong, it will not work, here.

The developer of this kernel module decided to treat each version of an ODS-5
file as an own file. The reason is simple. The target was to get to all
files on an ODS-5 disk. Some tools or utilities which present VMS files to
Unix hide the version and some hide the version if there is only one version
of the file. These tools also convert the ';' to a '.'. The above ODS-5 directory
then is listed by such tools as

    HELLO Hello..16 Hello..15 hello HELLO.DIR

Or, if the directories are "typeless"

    HELLO..7 Hello..16 Hello..15 hello HELLO

The developer of this kernel module didn't like that. Imagine the file
Hello..15 is deleted, then the file Hello..16 becomes Hello. OK, deleting a
file will not happen, here, because this is a read-only Linux file system for

On the other hand, ODS-5 has a file header for each version of a file. The
header contains the version in the filename. So strictly speaking there are no
file versions in the file system. That's another reason to always use/display file
versions in this Linux implementation of the ODS-5 file system. There are
only versions in the directory. In a directory there is only one filename
which does not include a version. Then there are version entries to that
filename. Each entry holds the file id (which is an index in the fileheader
file - INDEXF.SYS - addressing the file header with the filename including
the version).

However, with symlinks the ODS-5 module has to find the right target, which
is specified as a file name without version but is matched as one with a
version. This can be hidden for following a link and accessing the target
inside the ODS-5 module. So
    # ls -l WORLD.;1
    lrwxr-x---  1 bin bin    8 Oct  8 12:33 WORLD.;1 -> ../hello
    # cat WORLD.;1
will print the contents of hello.;3.

But then
    # file WORLD.;1
is broken. The symbolic link as shown and as returned in a readlink() is
"../hello" and the file utility does a stat() on "../hello" which is not an
ODS-5 file in the parent directory!

This is a dilemma which requires a compromise. On one hand, if
stat("../hello") is allowed to work because of a result of reading a symbolic
link, an
    # ls WORLD
will also work. Inside stat() and the ODS-5 function it uses, it is not known
if the passed file name was from a symbolic link or not.

Flagging the symbolic link doesn't really help. For example a trailing ";0",
the traditional VMS specification for the last (=highest) version may be
suggested as a flag. Flagging a symbolic link like this is like faking a

If a version is faked, that is a ";0" is added to
indicate the last version, the output of "ls" also changes:
    # ls -l WORLD.;1
    lrwxr-x---  1 bin bin    10 Oct  8 12:33 WORLD.;1 -> ../hello;0

Did you see that there is no dot? All VMS file names have a type part. The
"." is part of the type and always present. There is no VMS file like
"hello;0". But the dot is not added because only a version is faked. Did you
see that the file size also changed? It has to. If a smart program sees a
symbolic link file and uses a buffer of its size to get the link, the
reported size must be as long as the faked symbolic link. This leaves us
with the oddity of WORLD.;1 pointing to ../hello;0 which can be ../hello.;3
or ../hello.DIR;1.

And again, now
    # ls -l WORLD;0
    lrwxr-x---  1 bin bin    10 Oct  8 12:33 WORLD;0 -> ../hello;0
"works", too and prints as output WORLD;0
Did you see that there is no dot? "ls" prints the filename as it gets it.

Another option to flag the symbolic link is to use a character which will
never show in an ODS-5 file name: "*" is one. The symbolic link would then
show as ../hello*. Then in a lookup, any trailing asterisk can be matched
with ../hello.;3 or ../HELLO.DIR;1/ or maybe to ../Hello.;16. This is nice
and somehow compatible with an
    # ls WORLD*
which would be matched similar.

Although the added asterisk sounds like a good compromise, any flag (";0" or
"*") will not work if a symbolic link from ODS-5 points to a Linux file: 
../../../hello;0 and ../../../hello* will never be matched with an existing
../../../hello, when the uppermost parent directory is in a Unix file

Sigh, so stat("../hello") has to work to make the best out of the ODS-5
symbolic links.

Implementing this means, that any file without a ;<version> is from a
symbolic link and is treated like a ";0".

Then there are the obscure cases where there are two files like hello.;1;1
(DCL shows it as hello^.^;1.;1) and hello.;1 in one directory and a symbolic
link world.;1 -> hello.;1 Now, on Linux with this file system do an
  ls hello.;1 -> print info on hello.;1
  cat world.;1 -> print contents of hello.;1;1
  file world -> stat("hello.;1") -> check status of the wrong file hello.;1

Maybe it isn't worth the effort. Maybe symbolic links should not be
supported, here.