Why Ln -Sf Does Not Overwrite Existing Link to Directory

Why ln -sf does not overwrite existing link to directory

When you run:

ln -sf DIR2 L

This is creating a symlink inside DIR1 cause L points to DIR1 and ln dereferences it, creating L/DIR2 -> DIR1.

The following:

rm -fr DIR1 DIR2 L
mkdir DIR1 DIR2
ln -v -s DIR1 L
ls -la L
ln -v -f -s DIR2 L
ls -la L

will output:

'L' -> 'DIR1'
lrwxrwxrwx 1 runner runner 4 Oct 21 18:13 L -> DIR1
'L/DIR2' -> 'DIR2'
lrwxrwxrwx 1 runner runner 4 Oct 21 18:13 L -> DIR1

To handle that, use the --no-dereference option as indicated in answer in this thread on superuser.com.

ln -sf does not overwrite a symlink to a directory

man ln

SYNOPSIS
ln [OPTION]... [-T] TARGET LINK_NAME (1st form)
ln [OPTION]... TARGET (2nd form)
ln [OPTION]... TARGET... DIRECTORY (3rd form)
ln [OPTION]... -t DIRECTORY TARGET... (4th form)

DESCRIPTION
In the 1st form, create a link to TARGET with the name LINK_NAME.
In the 2nd form, create a link to TARGET in the current directory.
In the 3rd and 4th forms, create links to each TARGET in DIRECTORY.

You have the 3rd form, because your link is a link to a directory.

Atomic `ln -sf` in python (symlink overwriting exsting file)

This code tries to minimise the possibilities for race conditions:

import os
import tempfile

def symlink_force(target, link_name):
'''
Create a symbolic link link_name pointing to target.
Overwrites link_name if it exists.
'''

# os.replace() may fail if files are on different filesystems
link_dir = os.path.dirname(link_name)

while True:
temp_link_name = tempfile.mktemp(dir=link_dir)
try:
os.symlink(target, temp_link_name)
break
except FileExistsError:
pass
try:
os.replace(temp_link_name, link_name)
except OSError: # e.g. permission denied
os.remove(temp_link_name)
raise

Note:

  1. If the function is interrupted (e.g. computer crashes), an additional random link to the target might exist.

  2. An unlikely race condition still remains: the symlink created at the randomly-named temp_link_name could be modified by another process before replacing link_name.

I raised a python issue to highlight the issues of os.symlink() requiring the target not exist.

Credit to Robert Seimer's input.

ln -s and overwriting a physical directory

The ln utility may be asked to remove the destination if it already exists by adding the -f option. However, the POSIX standard says that this is done with a call to the C library routine unlink(), and about that function, the standard says

The path argument shall not name a directory unless the process has appropriate privileges and the implementation supports using unlink() on directories.

I have not access to a system where unlink() is documented to remove directories, or where the -f flag to ln is documented to remove directories.

Your solution is therefore to either

$ rm -rf /path/to/A

or, which would be safer,

$ mv -f /path/to/A /path/to/A.orig

before creating the symbolic link.

Linux command for rmdir and mklink?

Well to answer

What is the rmdir and mklink command for linux platform ?

  1. There is no mklink command available in Linux however an equivalent command ln serves the purpose.

  2. Going By the man pages rmdir in Linux removes empty directories.

For windows it is - rmdir /S/Q for rmdir and mklink /D/J for mklink.

How to write similar in Linux ?



1. rmdir - Removes a directory provided it's empty, However If directory is not empty then Using rm is wise

Remove empty directory :

$ rmdir path/to/directory

Remove directories Recursively (useful for nested dirs) :

$ rmdir -p path/to/directory

Remove files from a location :

$ rm path/to/file path/to/another/file

Recursively remove a directory & all it's subdirectories :

$ rm -r path/to/directory

Force Remove a Directory, withour prompting for confirmation or showing any error messages :

$ rm -rf path/to/directory

Intercatively Remove multiple Files, with a prompt before every removal :

$ rm -i file another_file another_file



2. ln - Creates links to files and folders

Create a hard link to file :

$ ln path/to/file/to/hardlink

Create a symbolic link to file of folder :

$ ln -s path/to/file/to/symlink

Overwrite an existing symbolic link to point to another file :

$ ln -sf path/to/new/file path/to/symlink

NOTE - You cannot use ln to create hard links for directories.

Does symlink function in perl overwrite an existing link

symlink simply calls the OS call by the same name (symlink(2)), which returns error EEXIST when "newpath already exists".

If you wanted to implement -f, you could use

unlink($new_qfn);
symlink($old_qfn, $new_qfn)
or die("Can't create symlink \"$new_qfn\": $!\n");

However, the following does a better job of handling race conditions:

if (!symlink($old_qfn, $new_qfn)) {
if ($!{EEXIST}) {
unlink($new_qfn)
or die("Can't remove \"$new_qfn\": $!\n");
symlink($old_qfn, $new_qfn)
or die("Can't create symlink \"$new_qfn\": $!\n");
} else {
die("Can't create symlink \"$new_qfn\": $!\n");
}
}

ln uses the latter approach.

$ strace ln -sf a b
...
symlink("a", "b") = -1 EEXIST (File exists)
unlink("b") = 0
symlink("a", "b") = 0
...

Find circular symlinks with boost filesystem

You should be able to use boost::filesystem::canonical or weakly_canonical (in case the file need not exist).

Note that there can be performance overhead with many symlinks with many path elements, because path elements are stat-ed, building a new path instance.

Live On Coliru

#include <boost/filesystem.hpp>
#include <iostream>

int main(int argc, char** argv) {
using namespace boost::filesystem;
for (path link : std::vector(argv+1, argv+argc)) {
boost::system::error_code ec;
auto target = canonical(link, ec);
if (ec) {
std::cerr << link << " -> " << ec.message() << "\n";
} else {
std::cout << link << " -> " << target << "\n";
}

if (ec) {
target = weakly_canonical(link, ec);
if (!ec) {
std::cerr << " -- weakly: -> " << target << "\n";
}
}
}
}

When creating a few symlinks of various quality:

ln -sf main.cpp a
ln -sf b b
ln -sf d c

Incoking it with a b c d:

"a" -> "/tmp/1613746951-1110913523/main.cpp"
"b" -> Too many levels of symbolic links
"c" -> No such file or directory
-- weakly: -> "c"
"d" -> No such file or directory
-- weakly: -> "d"

ln command creates broken links

Some of the variations of ln command are:

ln -s abs_path_to_link_target rel_path_from_current_dir_to_link_source
ln -s rel_path_from_link_src_to_target rel_path_from_current_dir_to_link_source

But the following, which you were trying to use, is not one of them:

ln -s rel_path_from_current_dir_to_link_target ...

Your makefile has another subtle error, namely, the link source, does not depend on the changes to the link target, it only depends on the existence of the link target.

And another problem, is that you have a "side effect", when you are making $(DLIB) target. I am guessing you are a software eng, so you know that side effects are bad for parallelism, cause race conditions, and make code hard to read.

Also, one should always use automatic variables such as $@, and depend everything on the Makefile.

Finally, I am hoping that you know why you are using -f. Some of the responses above, including mine :), do not use it. It is very important in the Makefile context, don't drop it.

Bearing these points in mind, the cleanest and correct way to do this would be:

$(DLIB) $(DLIB).$(MAJOR): Makefile | $(DLIB).$(VERSION)
ln -sf $(abspath $|) $@


Related Topics



Leave a reply



Submit