python-3.xbashsymlinkdata-race

How to override existing symlink in Python3 if symlink already exists?


I have a working bash script which creates and/or rewrites current symbolic link to a new path without any data races. If the program tries to find a path it either gets an old path or a new path from the symlink. This works because of -f mode. Here how it looks in bash:

cd /srv/
ln -nsf /home/myproject/video123.ts latest_video.mkv
ln -nsf /home/myproject/video124.ts latest_video.mkv
ln -nsf /home/myproject/video125.ts latest_video.mkv

In python3 where is a module called os.symlink() which creates a symlink but It can't achieve the overriding behavior.

import os, errno

def symlink_force(target, link_name):
    try:
        os.symlink(target, link_name)
    except OSError, e:
        if e.errno == errno.EEXIST:
            os.remove(link_name)
            os.symlink(target, link_name)
        else:
            raise e

as you can see at a fraction of time there is downtime where os.remove(link_name) method is called. Any ideas on how to make override without removing the existing symlink?

Any help appreciated.


Solution

  • Checking the behavior of ln -nsf (with strace) will show that the command is executed by 2 systems calls:

    The advantage of this approach (over remove/symlink) is that rename guarantee that at any point of time, the target file will either point to the old file, or to the new file.

    Possible Python implementation (assuming safe to use ".new" as temporary link)

    def symlink_force(target, link_name):
        try:
           temp_link = link_name + ".new"
           os.remove(temp_link)
           os.symlink(target, temp_link)
           os.rename(temp_link, link_name)
        except OSError e:
           # Handle critical errors