4

New Linux user here, freshly transitioned from Windows 10 to Fedora 41. Lot's of experience with many OS's, including various *nix and Unix-like file systems.

I have a standard practice of using symlinks from test folders to code repos in order to recreate the deployed production folder structure locally for testing, while keeping testing and the code repo cleanly separated. I have many projects set up like this for local testing.

This looks as follows, for example:

~/dev
 test
 a-project
 app => ../../code/a-project/src
 (cfg)
 (web)
 dta
 cfg
 code
 a-project
 src
 cfg
 web

Key to this problem is that the src/cfg folder (which in production is the app/cfg folder) holds files that have relative references to optional files in dta/cfg which are loaded if they exist in production, expected to be relative to the app/cfg folder. Thus ../../dta/cfg/Optional.json when deployed references the dta folder. The PWD is the application root (in testing, this is ~/dev/test/a-project and all paths in the software are specified relative to the PWD. These paths are manipulated in Java code, using Java's File class, so this is not a shell-behavior problem.

This works perfectly well in Windows, since the filesystem takes the path app/cfg/../../dta/cfg/Optional.json to mean exactly what it looks like, and resolves the relative references first, resulting in dta/cfg/Optional.json (the leading app/cfg being eliminated by the following ../...

But Linux apparently resolves the symlink first (app/cfg/ => dev/code/project/src/) and only then then applies the relative path segments ../../dta/cfg/Optional.json, resulting in a file not found because, of course, the file is not located in ~/dev/code/project/dta/cfg.

Regardless of arguments about whether this is technically correct or not, is there any way that I can tell Linux at any level to resolve relative path segments before resolving symlinks so that this unexpected and unpredictable behavior does not occur? Apart from being confusing and counter-intuitive to me, I have a lot of existing code which expects paths to resolve relative references first, that is using the apparent path, not the underlying path.

The file system for my dev work is ext4 with case folding enabled (having come from Windows, and needing to remain compatible with other devs using Mac and Windows), and I'd really, really like to avoid making a destabilizing, invasive change to a bunch of Java classes to implement the relative path resolution before passing the path to the OS; both because it's potentially brittle, leading to security flaws, and because it's only needed for my testing as production systems (Ubuntu Linux servers) do not need these symlinks.

asked Jan 15 at 1:53

1 Answer 1

7

The simple answer is "no". Without changing the java code, symbolic links will not do what you ask.

Why?

That's because .. isn't what you think it is...

The behaviour isn't unpredictable. It's only really surprising because the cd command does something weird. In all other contexts .. is interpreted by the kernel as a real thing in the filesystem. Every directory actually contains a child named .. that directly refers to it's parent.

So foo/.. tells the kernel to follow the symbolic link foo and then lookup the name .. in that directory. The kernel really isn't doing anything different than it would for the path foo/bar.

Options

Alternative configuration layout

As an observation: the problem you face appears to come from the fact your configuration is also stored in in your source code directory. That's an unusual pattern.

Normally, you would put configuration in the test directory, making it specific to the test. The configuration may reference the location of the app or your run script knows the layout.

You can always symbolic link individual configuration files into the test/app/cfg directory. As long as the directory path itself is not a symbolic link, the real path of the configuration file itself can be a symbolic link.

Use bind mounts / docker

I personally prefer bind mounts for this type of thing, and for that purpose I usually use Docker and docker compose as a way to setup the test.

You may prefer to use a shell script to setup bind mounts for you, since java usually requires some sort of run script anyway.

Eg: docker-compose.yaml file in the test directory

services:
 test:
 image: openjdk:25-jdk-bullseye
 volumes: 
 - ../../code/a-project/src:/app/
 - ./data:/data/
 # Whatever your run script is...
 run: run.sh 

It is also possible to set this up manually using unshare and mount --bind and finally exec in a script. But that is more complex.

answered Jan 15 at 10:21
3
  • Thanks. The config set up is not as weird as you'd think. The app config sets up the app's requirements, and the dta config specifies optional overrides specific to the particular installation. We have hundreds of installation of several apps, so this is not going to change now. Commented Jan 16 at 17:38
  • @L.CorneliusDol oh I wouldn't suggest that you do change it. I've worked with enough "legacy" code to know that it's not worth it. I call it out in this answer as a hint for future readers. IE: If you have the option of coding your app in an idiomatic way then do so. The options I give are for times when you are backed into a corner with no option to re-code Commented Jan 17 at 13:00
  • @L.CorneliusDol FYI, I've seen the pattern of putting defaults in app code directory and overrides elsewhere, there's nothing wrong with that patten. But the assumption that the overrides live in any specific relative location to app code is discouraged on all operating systems. Each OS has it's own preference for where local config should live and that even changes with OS versions. So idiomatically, your apps ought to have either an environment variable or run script arg to specify the location of data. You can't change what you can't change. 🤷 Commented Jan 17 at 13:02

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.