rapairing broken symlinks

I have moved a Leap 15.2 to a VM and upgrade to leap 15.4 for doing it I copied the filesystem with rsync and the booted the VM with opensuse leap 15.4 DVD and did an upgrade. It worked fine and the system boots and works… except I have detected some (but not much) errors, for instance awk oes not work, I look for it and I see it is provided by gawk, and I see in alternatives this


tutatis:/var/lib/mailman/data # ls /etc/alternatives/awk -l
lrwxrwxrwx 1 root root 12 Sep  6  2017 /etc/alternatives/awk -> usr/bin/gawk
tutatis:/var/lib/mailman/data # ls /usr/bin/gawk -l
-rwxr-xr-x 1 root root 614520 May 25  2018 /usr/bin/gawk
tutatis:/var/lib/mailman/data #

So gawk is there, and awk is a symlink from alternatives to it… but without the initial slash
I have detected more errors like this one … indeed a lot.
I think can get them all with

find / -xtype l

So I could run -exec with find to fix each one of them, adding a / at the begining right?

Hi
No, use the update-alternatives command to fix…


ls -la `which awk`
lrwxrwxrwx 1 root root 29 May 25  2018 /usr/bin/awk -> /etc/alternatives/usr-bin-awk

ls -la `which gawk`
-rwxr-xr-x 1 root root 614520 May 25  2018 /usr/bin/gawk

update-alternatives --list awk
/usr/bin/gawk

This was an example, but I have much more broken symlinks I want to fix, for instance

/etc/systemd/system/xdm.service -> usr/lib/systemd/system/display-manager.service

when it should be

/etc/systemd/system/xdm.service -> /usr/lib/systemd/system/display-manager.service

I know I could delete the symlink and create it again, but I want to do it in an automatic way as there are a lot of them

I can obtain all missing symlinks with find / -xtype l

But I don’t know an easy way to make the symlink absolute… there is a way to make the reverse with symlink -c

Hi
Maybe the symlinks program from the utilities repository will help better then.

https://build.opensuse.org/package/show/utilities/symlinks

**erlangen:~ #** zypper info symlinks 
Loading repository data... 
Reading installed packages... 


Information for package symlinks: 
--------------------------------- 
Repository     : home:Dead_Mozay (openSUSE_Tumbleweed) 
Name           : symlinks 
Version        : 1.4-2.62 
Arch           : x86_64 
Vendor         : obs://build.opensuse.org/home:Dead_Mozay 
Installed Size : 20.2 KiB 
Installed      : Yes 
Status         : up-to-date 
Source package : symlinks-1.4-2.62.src 
Upstream URL   : http://www.ibiblio.org/pub/Linux/utils/file/ 
Summary        : A commmand line tool to manipulate symbolic links 
Description    :  
    Symlinks is a nice tool to convert absolute or relative symbolic links. 

**erlangen:~ #**
**erlangen:~ #** zypper lr home_Dead_Mozay  
Alias          : home_Dead_Mozay 
Name           : home:Dead_Mozay (openSUSE_Tumbleweed) 
URI            : https://download.opensuse.org/repositories/home:/Dead_Mozay/openSUSE_Tumbleweed/ 
Enabled        : Yes 
GPG Check      : (r ) Yes 
Priority       : 100 (lowered priority)
Autorefresh    : Off 
Keep Packages  : Off 
Type           : rpm-md 
GPG Key URI    : https://download.opensuse.org/repositories/home:/Dead_Mozay/openSUSE_Tumbleweed/repodata/repomd.xml.key 
Path Prefix    :  
Parent Service :  
Keywords       : --- 
Repo Info Path : /etc/zypp/repos.d/home_Dead_Mozay.repo 
MD Cache Path  : /var/cache/zypp/raw/home_Dead_Mozay 
**erlangen:~ #**

I installed it but


tutatis:/var/lib/mailman/data # symlinks --help
symlinks: scan/change symbolic links - v1.4 - by Mark Lord

Usage:  symlinks -cdorstv] dirlist

Flags:  -c == change absolute/messy links to relative
        -d == delete dangling links
        -o == warn about links across file systems
        -r == recurse into subdirs
        -s == shorten lengthy links (displayed in output only when -c not specified)
        -t == show what would be done by -c
        -v == verbose (show all symlinks)


The -c option does just the opposite of what I want

I found a bug report of 2012 asking to include an option to convert relative to absolute symlinks … but it seems it was not included… at least in the symlinks program in opensuse repos.

Use this code to find broken links:

find /etc -type l -print0|xargs -0 ls -1d |while read i ; do 
        r=$(realpath "$i") 
        if  ! -e "$r" ]] ; then  
                echo name: "$i" 
                echo target "$r" does not exist. 
               **# insert repair actions here**
       fi  
done 
**erlangen:~ #** ./findlinks.sh  
name: /etc/alternatives/rmic 
target /usr/lib64/jvm/java-15-openjdk-15/bin/rmic does not exist. 
name: /etc/alternatives/rmic.1.gz 
target /usr/share/man/man1/rmic-java-15-openjdk.1.gz does not exist. 
name: /etc/mtab 
target /proc/6457/mounts does not exist. 
name: /etc/systemd/system/timers.target.wants/mandb.timer 
target /usr/lib/systemd/system/mandb.timer does not exist. 
**erlangen:~ #** 

I was thinking on that


#!/bin/bash

find $1 -xtype l -exec ls -l {} \; 


but of course, instead ls -l use “repair actions” the problem I have is with the repair actions, I want to change the broken links to just insert a / at the beginning of it… I don’t know how to do it… working on it.

I’m testing to call a script, someting like

#!/bin/bash

RELATIVEPATH=$(readlink  $1)
echo $RELATIVEPATH
ABSOLUTEPATH="/${RELATIVEPATH}"
echo $ABSOLUTEPATH


This test works, and I think I will remove the link with the relative path and then recreate the link with the absolute path.
I don’t know if there is a better way to do it.

bor@bor-Latitude-E5450:~$ ln -s usr/bin/ls /tmp/link
bor@bor-Latitude-E5450:~$ ll /tmp/link
lrwxrwxrwx 1 bor bor 10 Sep 11 20:14 /tmp/link -> usr/bin/ls
bor@bor-Latitude-E5450:~$ /tmp/fixlink.sh /tmp/link
Link is /tmp/link
Target is usr/bin/ls
bor@bor-Latitude-E5450:~$ ll /tmp/link
lrwxrwxrwx 1 bor bor 11 Sep 11 20:14 /tmp/link -> /usr/bin/ls*
bor@bor-Latitude-E5450:~$ /tmp/fixlink.sh /tmp/link
Link is /tmp/link
Target exists
bor@bor-Latitude-E5450:~$ cat /tmp/fixlink.sh 
#!/bin/sh


echo Link is "$1"
 -L "$1" ] || { echo Not a symbolic link ; exit 0 ; }
 -e "$1" ] && { echo Target exists; exit 0 ; }
link=$(readlink "$1")
echo Target is "$link"
case "$link" in
    /* ) exit 0 ;;
esac
ln -sf /"$link" "$1"
bor@bor-Latitude-E5450:~$ 

Ok, I was testing this script to remove the link and recreate it

fperal@tutatis:~/.kde> cat repairbrokenlink.sh 
#!/bin/bash 

echo  File 
echo $1 

RELATIVEPATH=$(readlink  $1) 
echo symlink relative path 
echo $RELATIVEPATH 

ABSOLUTEPATH="/${RELATIVEPATH}" 
echo symlink absolute path 
echo $ABSOLUTEPATH 

echo repairing symlink 
rm -f $1 
ln -s $ABSOLUTEPATH $1



It works.

fperal@tutatis:~/.kde> ls -l /home/fperal/.kde/tmp-pc1510 
lrwxrwxrwx 1 fperal 984 14 sep 24  2009 **/home/fperal/.kde/tmp-pc1510** -> **tmp/kde-fperal
** 
fperal@tutatis:~/.kde> ./repairbrokenlink.sh /home/fperal/.kde/tmp-pc1510 
File 
/home/fperal/.kde/tmp-pc1510 
symlink relative path 
tmp/kde-fperal 
symlink absolute path 
/tmp/kde-fperal 
repairing symlink 

fperal@tutatis:~/.kde> ls -l /home/fperal/.kde/tmp-pc1510                 
lrwxrwxrwx 1 fperal users 15 sep 11 19:47 /home/fperal/.kde/tmp-pc1510 -> **/tmp/kde-fperal**
 

I see you are doing it just with ln forcing to overwrite the old link … yes, it is better .

I am really curious what you did to get this system state …

I cloned it from a physical machine to a virtual machine, I did it creating the filesystems in the clone and copying the files using rsync, with the physical machine running, doing

rsync  -aAxXhH --delete -v  / 192.168.2.160::root/

where 192.168.2.160 was the virtual machine running a system rescue CD, with the root partition mounted on /target and shared by rsyncd

uid=root
gid=root

[root]
path=/target
read only=no
hosts allow=192.168.2.100
munge symlinks=no

I don’t know what I did wrong, but the funny thing is that the clone is working … so not all symlinks must be bad… I think.

Well, in fact I used not only this command to make the clone, but three commands several times…


rsync  -aAxXhH --delete -v  / 192.168.2.160::root/
rsync  -aAxXhH --delete -v  /home/ 192.168.2.160::root/home/
rsync  -aAxXhH --delete -v  /imagenes/ 192.168.2.160::root/imagenes/

where /imagenes was a separate filesystem in the target, with images of software, machines, etc.

From man rsyncd.conf

When this parameter is disabled on a writable module and "use chroot" is off (or the inside-chroot path is not "/"), incoming symlinks will be modified to drop a leading slash and to remove ".." path elements that rsync believes will allow a symlink to escape the module's hierarchy.

I made some changes:

#!/bin/bash 
find /usr -type l|while read n ; do 
        t=$(readlink "$n") 
        if  ! -e "$t" ]] ; then  
                if  "$t" == usr/* ]] ; then 
                        echo target "$t" does not exist. 
                        **echo** ln -sf /"$t" "$n" 
                fi 
        fi  
done

The above prints the missing and the necessary actions. Remove the echo to perform them.

Correct me if I’m wrong, but for instance

/home/fperal/.local/share/akonadi/socket-tutatis2 which points to tmp/akonadi-fperal.5sdlq4 and should point to /[FONT=monospace]tmp/akonadi-fperal.5sdlq4 will not be corrected with that because you search in [FONT=monospace]/usr and test if the link is pointing to usr/*

I have tried this

[FONT=monospace]**tutatis:/imagenes/scripts #** cat findlinks.sh 
#!/bin/bash 

find $1 -xtype l -exec ./repairbrokenlink.sh  {} \;


[FONT=monospace]**tutatis:/imagenes/scripts #** cat repairbrokenlink.sh 
#!/bin/bash 

#echo  File 
#echo $1 

RELATIVEPATH=$(readlink  $1) 
#echo symlink relative path 
#echo $RELATIVEPATH 

ABSOLUTEPATH="/${RELATIVEPATH}" 
#echo symlink absolute path 
#echo $ABSOLUTEPATH 


if  ${RELATIVEPATH:0:1} != "/" ] &&  -e "$ABSOLUTEPATH" ] ; 
then 
  echo repairing symlink 
  ln -sf $ABSOLUTEPATH $1 
   
  echo "File:  $1   Broken link: $RELATIVEPATH  Replaced with   $ABSOLUTEPATH "  
  echo "File:  $1   Broken link: $RELATIVEPATH  Replaced with   $ABSOLUTEPATH " >> repairbrokenlinks.log 

fi

[/FONT]

And it works mostly, but for some links it returns an error
./repairbrokenlink.sh: line 15: : !=: unary operator expected

I think something is not well quoted.

[/FONT]
[/FONT]
[/FONT]

Whit the sript I justo posted before I have some errors, for instance:



tutatis:/imagenes/scripts # ls -l /home/fperal/instituto/documentos\ de\ otros\ cursos/micros/18-19/transparencias/2-codificacion.sxi
lrwxrwxrwx 1 fperal 984 108 Feb 25  2021 /home/fperal/instituto/documentos de otros cursos/micros/18-19/transparencias/2-codificacion.sxi -> home/fernando/documentos/instituto/documentos de otros cursos/micros/13-14/transparencias/2-codificacion.sxi

tutatis:/imagenes/scripts # ./repairbrokenlink.sh /home/fperal/instituto/documentos de otros cursos/micros/18-19/transparencias/2-codificacion.sxi
/home/fperal/instituto/documentos
./repairbrokenlink.sh: line 15: : !=: unary operator expected
tutatis:/imagenes/scripts #

I have tried yours and I get similar errors, the problem is the names with spaces.


**tutatis:/imagenes/scripts #** cat repair.sh      
#!/bin/sh 


echo Link is "$1" 
 -L "$1" ] || { echo Not a symbolic link ; exit 0 ; } 
 -e "$1" ] && { echo Target exists; exit 0 ; } 
link=$(readlink "$1") 
echo Target is "$link" 
case "$link" in 
    /* ) exit 0 ;; 
esac 
ln -sf /"$link" "$1" 

**tutatis:/imagenes/scripts #** ./repair.sh /home/fperal/instituto/documentos de otros cursos/micros/18-19/transparencias/2-codificacion.sxi 
Link is /home/fperal/instituto/documentos 
Not a symbolic link 
**tutatis:/imagenes/scripts #** 


It is very unlikely if you tried it with your find command. Show the actual invocation with error.

the problem is the names with spaces.

Yes, you need to quote such name on command line, but it does not apply to find invocation. And your script of course lacks quoting completely, so readlink treats each space separated part as separate filename and returns empty string. This is common mistake in shell programming.

Oh, yes, you’re right!

I missed the \ when calling the script, What I did testing your script was


tutatis:/imagenes/scripts # ./repair.sh /home/fperal/instituto/documentos de otros cursos/micros/18-19/transparencias/2-codificacion.sxi
Link is /home/fperal/instituto/documentos
Not a symbolic link

that fails, when I should have done


tutatis:/imagenes/scripts # ./repair.sh /home/fperal/instituto/documentos\ de\ otros\ cursos/micros/18-19/transparencias/2-codificacion.sxi
Link is /home/fperal/instituto/documentos de otros cursos/micros/18-19/transparencias/2-codificacion.sxi
Target is home/fernando/documentos/instituto/documentos de otros cursos/micros/13-14/transparencias/2-codificacion.sxi
tutatis:/imagenes/scripts # 


that works