Können Sie ändern, auf was ein Symlink verweist, nachdem er erstellt wurde?

122

Bietet ein Betriebssystem einen Mechanismus (Systemaufruf - kein Befehlszeilenprogramm) zum Ändern des Pfadnamens, auf den durch einen symbolischen Link (Symlink) verwiesen wird - außer durch Aufheben der Verknüpfung des alten und Erstellen eines neuen?

Der POSIX-Standard tut dies nicht. Solaris 10 nicht. MacOS X 10.5 (Leopard) nicht. (Ich bin mir ziemlich sicher, dass weder AIX noch HP-UX dies tun. Nach dieser Liste von Linux-Systemaufrufen zu urteilen , hat Linux auch keinen solchen Systemaufruf.)

Gibt es etwas, das tut?

(Ich erwarte, dass die Antwort "Nein" ist.)


Da es schwierig ist, ein Negativ zu beweisen, lassen Sie uns die Frage neu organisieren.

Wenn Sie wissen, dass ein (Unix-ähnliches) Betriebssystem, das noch nicht aufgeführt ist, keinen Systemaufruf zum Umschreiben des Werts eines Symlinks (der von zurückgegebenen Zeichenfolge readlink()) hat, ohne den alten Symlink zu entfernen und einen neuen zu erstellen, fügen Sie ihn hinzu - oder sie - in einer Antwort.

Jonathan Leffler
quelle
Was ist falsch daran, einfach neu zu verknüpfen? Warum nicht einfach den lnBefehl (oder die API gleichwertig) ausgeben, der den alten Link überschreibt? Welches Problem hast du?
S.Lott
9
Witzig - Ich frage, ob es einen Systemaufruf gibt, um einen Programmierjob auszuführen, und die Frage wird als "gehört auf eine andere Site" markiert.
Jonathan Leffler
3
Witzig - Es war absolut nicht klar, dass Sie nach einem Systemaufruf suchten, und Sie haben gerade die Frage bearbeitet, um dieses Detail hinzuzufügen. Wie können Sie also erwarten, dass die Leute etwas abziehen, bevor Sie es überhaupt schreiben?
Pascal Thivent
2
@ S.Lott: Ich schreibe ein Papier über Sicherheit und symbolische Links. An einer Stelle mache ich die Behauptung "der tatsächliche Eigentümer, die Gruppe, die Berechtigungen für den Symlink selbst sind unerheblich" und die Begründung ist, dass der Eigentümer des Symlinks ihn nur entfernen und den Wert nicht ändern kann. Ich überprüfe noch einmal, ob es keinen anderen Weg gibt, als den Symlink zu entfernen, um den Effekt des 'Umschreibens des Symlink-Werts' zu erzielen. Ich ignoriere den direkten Zugriff auf die unformatierte Festplatte und hacke den FS auf diese Weise - dies erfordert Root-Rechte und meine Bedenken betreffen Nicht-Root-Benutzer, nicht das, was Root tun kann.
Jonathan Leffler
4
@Pascal: Es tut mir leid - ich wusste nicht, dass es nicht klar war, dass ich über Systemaufrufe sprach, bis die Leute eine Tangente von dem abhielten, was ich beabsichtigte (was offensichtlich anders war als das, was ich sagte). Es tut mir leid, dass ich in die Irre geführt habe. es war unbeabsichtigt.
Jonathan Leffler

Antworten:

106

AFAIK, nein, das kannst du nicht. Sie müssen es entfernen und neu erstellen. Tatsächlich können Sie einen Symlink überschreiben und so den Pfadnamen aktualisieren, auf den er verweist:

$ ln -s .bashrc test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 7 2009-09-23 17:12 test -> .bashrc
$ ln -s .profile test
ln: creating symbolic link `test': File exists
$ ln -s -f .profile test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 8 2009-09-23 17:12 test -> .profile

EDIT : Wie der OP in einem Kommentar darauf hingewiesen, die mit --forceOption macht lneinen Systemaufruf durchzuführen , unlink()bevor symlink(). Unten stracebeweist die Ausgabe auf meiner Linux-Box:

$ strace -o /tmp/output.txt ln -s -f .bash_aliases test
$ grep -C3 ^unlink /tmp/output.txt 
lstat64("test", {st_mode=S_IFLNK|0777, st_size=7, ...}) = 0
stat64(".bash_aliases", {st_mode=S_IFREG|0644, st_size=2043, ...}) = 0
symlink(".bash_aliases", "test")        = -1 EEXIST (File exists)
unlink("test")                          = 0
symlink(".bash_aliases", "test")        = 0
close(0)                                = 0
close(1)                                = 0

Die endgültige Antwort lautet also "nein".

BEARBEITEN : Das Folgende wurde aus der Antwort von Arto Bendiken auf unix.stackexchange.com, circa 2016, kopiert .

Dies kann in der Tat atomar erfolgen rename(2), indem zuerst der neue Symlink unter einem temporären Namen erstellt und dann der alte Symlink auf einmal sauber überschrieben wird. Wie in der Manpage angegeben :

Wenn sich newpath auf einen symbolischen Link bezieht, wird der Link überschrieben.

In der Shell würden Sie dies mv -Twie folgt tun :

$ mkdir a b
$ ln -s a z
$ ln -s b z.new
$ mv -T z.new z

Sie können stracediesen letzten Befehl ausführen, um sicherzustellen, dass er tatsächlich rename(2)unter der Haube verwendet wird:

$ strace mv -T z.new z
lstat64("z.new", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
lstat64("z", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
rename("z.new", "z")                    = 0

Beachten Sie, dass oben beide mv -Tund straceLinux-spezifisch sind.

Verwenden Sie unter FreeBSD mv -habwechselnd.

Anmerkung des Herausgebers: So macht es Capistrano seit Jahren, seit ~ 2.15. Siehe diese Pull-Anfrage .

Pascal Thivent
quelle
2
Erzwingt die Option '-f' nicht 'ln', die Systemaufrufe unlink () und dann symlink () auszuführen?
Jonathan Leffler
1
Es tut. Aber die Frage könnte als "Wie mache ich das in einem Schritt" wahrgenommen worden und läuft dann auf die Definition von "Schritt" - Befehlszeile hinaus? syscall?
Michael Krelin - Hacker
1
Ich habe gerade bemerkt, dass Sie Ihrer Frage einen Syscall-Wortlaut hinzugefügt haben ;-)
Michael Krelin - Hacker
2
@Pascal: das tut es. Unter Solaris zeigt die Ausgabe von 'truss ln -spx' und 'truss ln -s -fpx' einen Aufruf von unlink () vor dem Aufruf von symlink () im zweiten Fall (und einen fehlgeschlagenen Aufruf von symlink () im ersten Fall). Ich würde erwarten, dass dies für die meisten, wenn nicht alle Unix-Varianten gilt.
Jonathan Leffler
12
+1 bis @Taai Kommentar - Wenn es sich um einen Symlink handelt, der ein Verzeichnis dereferenziert (auf dieses verweist), müssen Sie "-n" angeben, um sicherzustellen, dass dieser Trick funktioniert.
Wally
161

Ja, du kannst!

$ ln -sfn source_file_or_directory_name softlink_name
Taai
quelle
9
Danke für das Antworten. Die -fOption bedeutet "Vorhandenes Ziel entfernen", bevor das neue erstellt wird. Dieser Befehl erzielt also das Ergebnis, unlink(2)gefolgt von symlink(2). Darum ging es in der ursprünglichen Frage nicht. Beachten Sie auch, dass sowohl die akzeptierte Antwort als auch die am zweithäufigsten gewählte Antwort ln -sffür die Ausführung des Jobs verwendet werden. Wie die straceAusgabe zeigt, ist dies jedoch der Fall, unlink()und symlink()es gibt keinen Systemaufruf zum Ändern eines Symlinks.
Jonathan Leffler
17
Das -n scheint erforderlich zu sein, wenn der ursprüngliche Link zu einem Verzeichnis anstelle einer Datei führt.
Steampowered
11
Der 'n' Schalter ist wichtig. Ich hatte mit dem Problem zu kämpfen, dass der Symlink nicht aktualisiert wurde, aber der Befehl einen weiteren Link innerhalb des vorhandenen Links erstellt hat, da die Option "Kein Derefencing" nicht festgelegt wurde.
Jonathan Gruber
14

Es ist nicht erforderlich, die Verknüpfung des alten Symlinks explizit aufzuheben. Du kannst das:

ln -s newtarget temp
mv temp mylink

(oder verwenden Sie den entsprechenden Symlink und benennen Sie Anrufe um). Dies ist besser als das explizite Aufheben der Verknüpfung, da das Umbenennen atomar ist. Sie können also sicher sein, dass der Link immer entweder auf das alte oder das neue Ziel verweist. Dadurch wird der ursprüngliche Inode jedoch nicht wiederverwendet.

Auf einigen Dateisystemen wird das Ziel des Symlinks im Inode selbst (anstelle der Sperrliste) gespeichert, wenn es kurz genug ist. Dies wird zum Zeitpunkt der Erstellung festgelegt.

In Bezug auf die Behauptung, dass der tatsächliche Eigentümer und die Gruppe unerheblich sind, gibt symlink (7) unter Linux an, dass es einen Fall gibt, in dem dies von Bedeutung ist:

Der Eigentümer und die Gruppe eines vorhandenen symbolischen Links können mit lchown (2) geändert werden. Das Eigentum an einem symbolischen Link ist nur dann von Bedeutung, wenn der Link entfernt oder in einem Verzeichnis umbenannt wird, in dem das Sticky-Bit gesetzt ist (siehe stat (2)).

Die Zeitstempel für den letzten Zugriff und die letzte Änderung einer symbolischen Verknüpfung können mit utimensat (2) oder lutimes (3) geändert werden.

Unter Linux werden die Berechtigungen eines symbolischen Links in keiner Operation verwendet. Die Berechtigungen sind immer 0777 (Lesen, Schreiben und Ausführen für alle Benutzerkategorien) und können nicht geändert werden.

mark4o
quelle
@ mark4o: The Good - Der Verweis auf lchown () und den Besitz in einem Sticky-It-Verzeichnis ist nützlich - danke. Ich war mir von lchown () bewusst und es war für meine These nicht wesentlich. Mir waren auch Sticky-Bit-Verzeichnisse bekannt. Ich denke, dass der Besitz fast eine Standardfolge der Regeln ist - der Unterschied und vermutlich der Grund, warum er aufgerufen wird, besteht darin, dass Sie normalerweise eine Datei entfernen können, wenn Sie darauf schreiben können, und nominell kann jeder auf einen Symlink schreiben, weil von den 777 Berechtigungen, aber in diesem Fall sind die Regeln etwas anders.
Jonathan Leffler
1
@ mark4o: The Not So Good - Die angezeigte Befehlssequenz macht nicht das, was Sie denken (zumindest im Szenario :) set -x -e; mkdir junk; ( cd junk; mkdir olddir newdir; ln -s olddir mylink; ls -ilR; ln -s newdir temp; ls -ilR; mv temp mylink; ls -ilR; ); rm -fr junk. Wenn Sie Dateien oldfile und newfile anstelle von Verzeichnissen erstellen und das entsprechende Skript ausführen (hauptsächlich olddir in oldfile ändern, newdir in newfile), erhalten Sie den erwarteten Effekt. Nur eine zusätzliche Komplexität! Danke für die Antwort.
Jonathan Leffler
2

Nur eine Warnung zu den richtigen Antworten oben:

Die Verwendung der Methode -f / --force birgt das Risiko, dass die Datei verloren geht, wenn Sie Quelle und Ziel verwechseln:

mbucher@server2:~/test$ ls -la
total 11448
drwxr-xr-x  2 mbucher www-data    4096 May 25 15:27 .
drwxr-xr-x 18 mbucher www-data    4096 May 25 15:13 ..
-rw-r--r--  1 mbucher www-data 4109466 May 25 15:26 data.tar.gz
-rw-r--r--  1 mbucher www-data 7582480 May 25 15:27 otherdata.tar.gz
lrwxrwxrwx  1 mbucher www-data      11 May 25 15:26 thesymlink -> data.tar.gz
mbucher@server2:~/test$ 
mbucher@server2:~/test$ ln -s -f thesymlink otherdata.tar.gz 
mbucher@server2:~/test$ 
mbucher@server2:~/test$ ls -la
total 4028
drwxr-xr-x  2 mbucher www-data    4096 May 25 15:28 .
drwxr-xr-x 18 mbucher www-data    4096 May 25 15:13 ..
-rw-r--r--  1 mbucher www-data 4109466 May 25 15:26 data.tar.gz
lrwxrwxrwx  1 mbucher www-data      10 May 25 15:28 otherdata.tar.gz -> thesymlink
lrwxrwxrwx  1 mbucher www-data      11 May 25 15:26 thesymlink -> data.tar.gz

Dies ist natürlich beabsichtigt, aber normalerweise treten Fehler auf. Das Löschen und Wiederherstellen des Symlinks ist also ein bisschen mehr Arbeit, aber auch ein bisschen sparsamer:

mbucher@server2:~/test$ rm thesymlink && ln -s thesymlink otherdata.tar.gz 
ln: creating symbolic link `otherdata.tar.gz': File exists

das hält zumindest meine Datei.

Markus Bucher
quelle
Die Verwendung einer -foder --force-Option ist immer etwas gefährlich. Es setzt voraus, dass der Benutzer weiß, worum es geht. Es ist doppelt tödlich, wenn Sie es gewöhnlich verwenden ( rm -fr …jedes Mal ist gefährlich).
Jonathan Leffler
1

Würde es am Ende sowieso nicht dasselbe tun, wenn die Verknüpfung aufgehoben und das neue erstellt wird?

matt b
quelle
2
Warum überhaupt die Verknüpfung aufheben? Warum nicht einfach überschreiben?
S.Lott
5
Das Nettoergebnis ist ungefähr das gleiche - aber der Eigentümer und die Gruppe sowie die letzten Änderungszeiten (und wahrscheinlich die Inode-Nummer) wären im Allgemeinen alle unterschiedlich.
Jonathan Leffler
2
@ S.Lott: Welchen Systemaufruf überschreibt das 'Überschreiben'? In POSIX gibt es keinen solchen Aufruf. In Solaris und MacOS X gibt es keinen solchen Aufruf. Gibt es einen Aufruf, dies unter ... Linux, AIX, HP-UX oder irgendetwas anderem zu tun, das wie Unix aussieht? Windows hat nicht wirklich Symlinks auf die gleiche Weise, AFAICT, daher ist es für meine Analyse nicht kritisch - aber Informationen über die entsprechende Windows-API wären nützlich.
Jonathan Leffler
@ Jonathan Leffler: Ähm ... der ln --forceBefehl wird einen vorhandenen Link absolut überschreiben.
S.Lott
Leute, ich denke du redest über Kreuz. S.Lott diskutiert eine ausführbare Datei ln (1), während matt b. diskutiert die Tatsache, dass symlink (2)das Überschreiben nicht unterstützt wird. Sie haben beide Recht, und ich würde vorschlagen, dass ein Blick auf die Implementierung von ln (1)die idomatischste Möglichkeit darstellt, das Unlink-Relink-Verfahren durchzuführen.
dmckee --- Ex-Moderator Kätzchen
0

Nur für den Fall, dass es hilft: Es gibt eine Möglichkeit, einen Symlink mit Midnight Commander (mc) zu bearbeiten. Der Menübefehl lautet (auf Französisch auf meiner mc-Oberfläche):

Fichier / Éditer le lien symbolique

was übersetzt werden kann zu:

File / Edit symbolic link

Die Verknüpfung lautet Cx Cs

Vielleicht verwendet es intern den ln --forceBefehl, ich weiß es nicht.

Jetzt versuche ich, eine Möglichkeit zu finden, viele Symlinks gleichzeitig zu bearbeiten (so bin ich hier angekommen).

Pierre
quelle
1
Haben Sie überprüft, was MC hinter den Kulissen tut? Ich würde vermuten, dass es tatsächlich ein unlink()gefolgt von einem gibt symlink(), einfach weil dies Unix-ähnliche Systeme bieten.
Jonathan Leffler
Nein, ich habe nicht überprüft. Und ja, es ist ziemlich wahrscheinlich, dass es tatsächlich so ist, wie Sie es sagen. Die Tatsache, dass ich ein Textfeld bearbeite, gibt das Gefühl, dass ich das Symlink-Ziel tatsächlich bearbeite, aber ich wurde wahrscheinlich getäuscht ...
Pierre
0

Technisch gesehen gibt es keinen integrierten Befehl zum Bearbeiten eines vorhandenen symbolischen Links. Dies kann leicht mit wenigen kurzen Befehlen erreicht werden.

Hier ist eine kleine Bash / Zsh-Funktion, die ich geschrieben habe, um einen vorhandenen symbolischen Link zu aktualisieren:

# -----------------------------------------
# Edit an existing symbolic link
#
# @1 = Name of symbolic link to edit
# @2 = Full destination path to update existing symlink with 
# -----------------------------------------
function edit-symlink () {
    if [ -z "$1" ]; then
        echo "Name of symbolic link you would like to edit:"
        read LINK
    else
        LINK="$1"
    fi
    LINKTMP="$LINK-tmp"
    if [ -z "$2" ]; then
        echo "Full destination path to update existing symlink with:"
        read DEST
    else
        DEST="$2"
    fi
    ln -s $DEST $LINKTMP
    rm $LINK
    mv $LINKTMP $LINK
    printf "Updated $LINK to point to new destination -> $DEST"
}
blizzrdof77
quelle
2
Vielen Dank für die Mühe. Dies entspricht nicht meiner Anforderung, ein Systemaufruf in C-Sprache zu sein. Es gibt zahlreiche Möglichkeiten, einen Link zu ändern, solange Sie über eine Schreibberechtigung für das Verzeichnis verfügen, in dem sich der Link befindet (was hierfür erforderlich ist). Es gibt auch jetzt noch keine Möglichkeiten, den Wert eines Symlinks zu ändern, ohne dies zu tun unlink()und symlink()mit dem neuen Wert, der sich hinter den Kulissen mit Ihrem Code abspielt. Persönlich würde ich die Funktion auf genau zwei (oder vielleicht ein Vielfaches von zwei) Argumenten bestehen lassen und eine Kaution hinterlegen, wenn der Aufruf nicht korrekt wäre. Das ist jedoch eine besondere Perspektive.
Jonathan Leffler