Gibt es einen Algorithmus, um zu entscheiden, ob ein Symlink eine Schleife bildet?

16

Unix-Systeme versagen normalerweise nur dann, wenn sie mit einem Pfad konfrontiert werden, der eine Symlink-Schleife enthält, oder wenn zu viele Symlinks vorhanden sind, da die Anzahl der Symlinks, die sie in einer Pfadsuche durchlaufen, begrenzt ist. Aber gibt es eine Möglichkeit, tatsächlich zu entscheiden, ob ein bestimmter Pfad zu etwas aufgelöst wird oder eine Schleife enthält, selbst wenn er mehr Links enthält, als ein Unix bereit ist, zu folgen? Oder ist das ein formal unentscheidbares Problem? Und wenn es entschieden werden kann, kann es in einem angemessenen Zeitraum / Speicher entschieden werden (z. B. ohne alle Dateien auf einem Dateisystem besuchen zu müssen)?

Einige Beispiele:

a/b/c/d
where a/b is a symlink to ../e
and e is a symlink to f
and f is a symlink to a/b

a/b/c/d
where a/b/c is a symlink to ../c

a/b/c/d
where a/b/c is a symlink to ../c/d

a/b/c/d
where a/b/c is a symlink to /a/b/e
where a/b/e is a symlink to /a/b/f
where a/b/f is a symlink to /a/b/g

Bearbeiten :

Um dies zu verdeutlichen, frage ich nicht nach Schleifen im Dateisystem, sondern nach einem Entscheidungsalgorithmus, der über einen bestimmten Pfad entscheidet, ob er in eine bestimmte Datei / ein bestimmtes Verzeichnis aufgelöst wird oder ob er überhaupt nicht aufgelöst wird. In dem folgenden System gibt es beispielsweise eine Schleife, der angegebene Pfad wird jedoch weiterhin ordnungsgemäß aufgelöst:

/ -- a -- b
where b is a symlink to /a

Dieser Verzeichnisbaum hat eindeutig einen Zyklus, aber der Pfad wird a/b/b/b/b/btrotzdem gut aufgelöst /a.

JanKanis
quelle
Was sagt das Kommandozeilen-Tool readlink ...zu den oben genannten Situationen?
slm
1
Fragen Sie, ob wir nur anhand des Pfadnamens erkennen können, ob es Schleifen gibt? Oder können wir dies in einem realen Betriebssystem tun, indem wir die Standardtools verwenden und prüfen, in was die verschiedenen Komponenten des Pfadnamens aufgelöst werden?
Mike Diehn
@ MikeDiehn Offensichtlich kann man nicht an einem Pfad erkennen, ob die Auflösung ohne Dateisystemoperationen erfolgt. Aber auch in einer Betriebssystemumgebung ist es nicht einfach, einen Pfad zu unterscheiden, für dessen Auflösung lediglich viele Symlinks durchlaufen werden müssen, und einen Pfad, der überhaupt nicht aufgelöst wird.
JanKanis

Antworten:

10

Ich verstehe nicht ganz, was Sie fragen. Wenn ich es nicht besser wüsste, haben Sie sich wahrscheinlich gefragt, ob es eine Möglichkeit gibt, dies während des Umgangs mit einer Datei zu erkennen. Ich glaube nicht, dass das möglich ist.

Die einzige Methode, die ich mir vorstellen kann, ist das Durchsuchen eines bestimmten Zweigs im Verzeichnisbaum.

Beispiel

$ tree 
.
`-- a
    `-- b
        |-- c
        |   `-- d
        |       `-- e -> ../../../../a/b
        `-- e -> e

5 directories, 1 file

Der findBefehl erkennt diese Schleife, sagt Ihnen aber nicht wirklich viel darüber.

$ find -L . -mindepth 15
find: File system loop detected; `./a/b/c/d/e' is part of the same file system loop as `./a/b'.
find: `./a/b/e': Too many levels of symbolic links

Ich habe willkürlich 15 Stufen ausgewählt, um die Ausgabe der zu blockieren find. Sie können diesen Schalter ( -mindepth) jedoch fallen lassen, wenn Sie sich nicht für den angezeigten Verzeichnisbaum interessieren. Der findBefehl erkennt die Schleife weiterhin und stoppt:

$ find -L . 
.
./a
./a/b
./a/b/c
./a/b/c/d
find: File system loop detected; `./a/b/c/d/e' is part of the same file system loop as `./a/b'.
find: `./a/b/e': Too many levels of symbolic links

Übrigens , wenn Sie die Standardeinstellung außer Kraft zu setzen , MAXSYMLINKSdie scheinbar 40 auf Linux (neuere 3.x Versionen des Kernels) ist , dass Sie diese U & L sehen Q & A Titel: How do you MAXSYMLINKS erhöhen .

Verwenden Sie den Befehl symlinks

Es gibt ein Tool, das von Betreuern von FTP-Sites verwendet werden kann. symlinksMit diesem Tool können Probleme mit langen oder baumelnden Bäumen aufgedeckt werden, die durch symbolische Links verursacht wurden.

In bestimmten Fällen kann das symlinksTool auch verwendet werden, um störende Links zu löschen.

Beispiel

$ symlinks -srv a
lengthy:  /home/saml/tst/99159/a/b/c/d/e -> ../../../../a/b
dangling: /home/saml/tst/99159/a/b/e -> e

Die glibc Bibliothek

Die glibc-Bibliothek scheint einige C-Funktionen zu bieten, aber ich weiß nicht genau, welche Rolle sie spielt oder wie man sie tatsächlich benutzt. Deshalb kann ich Sie nur darauf hinweisen.

Die Manpage man symlinkzeigt die Funktionsdefinition für eine aufgerufene Funktion symlink(). Die Beschreibung lautet wie folgt:

symlink () erstellt einen symbolischen Link mit dem Namen newpath, der den String oldpath enthält.

Einer der Fehler besagt, dass diese Funktion Folgendes zurückgibt:

ELOOP Beim Auflösen von newpath wurden zu viele symbolische Links gefunden.

Ich werde Sie auch auf die Manpage weiterleiten, auf man path_resolutionder erläutert wird, wie Unix die Pfade zu Elementen auf der Festplatte ermittelt. Speziell dieser Absatz.

If  the component is found and is a symbolic link (symlink), we first 
resolve this symbolic link (with the current lookup directory as starting 
lookup directory).  Upon error, that error is returned.  If the result is 
not a directory, an ENOTDIR error is returned.  If the resolution of the 
symlink is successful and returns a directory, we set the current lookup
directory to that directory, and go to the next component.  Note that the 
resolution process here involves recursion.  In order  to  protect  the 
kernel against stack overflow, and also to protect against denial of 
service, there are limits on the maximum recursion depth, and on the maximum 
number of symbolic links followed.  An ELOOP error is returned  when  the
maximum is exceeded ("Too many levels of symbolic links").
slm
quelle
Wenn möglich, möchte ich eine Möglichkeit finden, eine Symlink-Schleife zu erkennen, wenn ein einzelner Pfad angegeben wird, und die Symlinks manuell in einem Programm aufzulösen, anstatt das Betriebssystem dies tun zu lassen. Aber ich frage mich, ob das überhaupt möglich ist. Die Find-Lösung sieht interessant aus, aber haben Sie eine Idee, wie / find Symlink-Schleifen erkennt und ob die verwendete Methode vollständig ist (dh alle möglichen Schleifen erkennt und keine nicht-schleifenförmigen Pfade falsch identifiziert)?
JanKanis
@Somejan - siehe meine Updates zum A. Lass mich wissen, ob das Sinn macht.
slm
5

OK, nach einigem Nachdenken denke ich, ich habe eine klare Lösung.

Die entscheidende Erkenntnis ist, dass, wenn jeder Link, der Teil eines Pfades ist, zu etwas aufgelöst wird, der gesamte Pfad aufgelöst wird. Oder umgekehrt, wenn sich ein Pfad nicht auflöst, muss es einen bestimmten Symlink geben, der eine Überquerung erfordert, die sich nicht auflöst.

Während ich zuvor über dieses Problem nachdachte, verwendete ich einen Algorithmus, der Elemente eines Pfades ab dem Stammverzeichnis durchlief. Als er auf einen Symlink stieß, ersetzte er dieses Pfadelement durch den Inhalt des Symlinks und fuhr dann mit dem Durchlaufen fort. Da sich dieser Ansatz nicht daran erinnert, welcher Symlink gerade aufgelöst wird, kann er nicht erkennen, ob er sich in einer nicht auflösenden Schleife befindet.

Wenn der Algorithmus nachverfolgt, welchen Symlink er gerade auflöst (oder welche Symlinks bei rekursiven Links), kann er erkennen, ob er versucht, einen Link rekursiv wieder aufzulösen, der noch aufgelöst wird.

Algorithmus:

initialize `location` to the current working directory
initialize `link_contents` to the path we want to resolve
initialize `active_symlinks` to the empty set

def resolve_symlink(location, link_contents, active_symlinks) :
    loop forever:
        next_location = location / [first element of link_contents]
        see if next_location is a symlink.
        if so:
            if next_location in active_symlinks: abort, we have a loop
            location = resolve_symlink(location, readlink(next_location), active_symlinks ∪ {next_location})
        else:
            location = next_location
        strip first element of link_contents
        if link_contents is empty: 
            return location

bearbeiten :

Ich habe eine funktionierende Implementierung in Python unter https://bitbucket.org/JanKanis/python-inotify/src/853ed903e870cbfa283e6ce7a5e41aeffe16d4e7/inotify/pathresolver.py?at=pathwatcher .

JanKanis
quelle
3

Python hat eine Funktion namens networkx.simple_cycles (), die dafür verwendet werden kann. Aber ja, es müsste jede Datei auf dem System lesen.

>>> import networkx as nx
>>> G = nx.DiGraph()
>>> G.add_edge('A', 'B')
>>> G.add_edge('B', 'C')
>>> G.add_edge('C', 'D')
>>> G.add_edge('C', 'A')
>>> nx.simple_cycles(G)
[['A', 'B', 'C', 'A']]
Back2Basics
quelle
Ich habe auch überlegt, einen Diagrammalgorithmus zu verwenden, bin mir aber nicht sicher, ob ein Verzeichnisbaum mit Symlinks in einem einfachen Diagramm angemessen dargestellt werden kann. In einem Verzeichnisbaum abc, in dem c ein Symlink zu .. ist, gibt es eine Schleife, Pfade wie a / b / c / b / c / b werden jedoch weiterhin aufgelöst, da sie der Schleife nur eine endliche Anzahl von Malen folgen und dies nicht tun Schleife weiter.
JanKanis
@Somejan: Ein Dateisystem-Namespace ist ein Diagramm, und ein Dateiname ist ein Pfad, der über diesem Diagramm ausgewählt wird.
Ninjalj
@ninjalj: Ja, ein Dateisystem ist eine Grafik, aber ich glaube nicht, dass ein Dateiname einfach ein Pfad über dieser Grafik ist. Der Dateiname kann als eine Reihe von Anweisungen zum Durchlaufen des Diagramms angesehen werden. Auch wenn das Diagramm Zyklen enthält, die nicht bedeuten, dass ein Dateiname, der auf diesen Zyklus folgt, nicht automatisch aufgelöst wird, siehe mein Beispiel in meinem vorherigen Kommentar.
JanKanis
3

Auf einem stillstehenden System (dh wenn keine Änderungen stattfinden) gibt es einen Algorithmus. Es gibt eine endliche Anzahl symbolischer Verknüpfungen, daher bilden sie einen endlichen Graphen, und das Erkennen von Zyklen ist ein endlicher Prozess.

Auf einem Live-System gibt es keine Möglichkeit, Zyklen zu erkennen, da sich symbolische Verknüpfungen ändern können, während der Zyklusdetektor ausgeführt wird. Das Lesen jeder symbolischen Verknüpfung ist atomar, das Lesen einer symbolischen Verknüpfung jedoch nicht. Wenn sich einige Symlinks ständig ändern, während der Kernel den Durchlauf durchführt, könnte dies auf einem unendlichen Pfad enden, der verschiedene Links beinhaltet.

Gilles 'SO - hör auf böse zu sein'
quelle
Es gibt Möglichkeiten, diese Änderungen zu verringern, um eine Genauigkeit von 98-99% zu erzielen. Sie könnten dafür sorgen, dass die Zeitstempel in den Dateien beachtet werden, und ich würde nicht vorschlagen, den Links tatsächlich zu folgen. Da es vom Stamm rekursiv ist, wird es später das tatsächliche Verzeichnis finden.
Back2Basics
1
@ Back2Basics Diese Zahlen sind völlig bedeutungslos. Dies ist eine Kernel-Schnittstelle. Wenn es nicht die ganze Zeit funktioniert, funktioniert es nicht, Punkt.
Gilles 'SO - hör auf, böse zu sein'
2

Soweit ich aus aktuellen Linux-Kernel-Quellen ersehen kann, zählt der Kernel nur, wie viele Links verfolgt werden, und es tritt ein Fehler auf, wenn diese größer als eine bestimmte Anzahl sind. Siehe Zeile 1330 in namei.c für den Kommentar und die nested_symlink()Funktion. Das ELOOP-Makro (die Fehlernummer, die von einem read(2)Systemaufruf für diese Situation zurückgegeben wurde) wird an mehreren Stellen in dieser Datei angezeigt, sodass es möglicherweise nicht so einfach ist, die folgenden Links zu zählen, aber das ist sicher, wie es aussieht.

Es gibt eine Reihe von Algorithmen zum Auffinden von "Zyklen" in verknüpften Listen ( Floyd's Cycle Detection-Algorithmus ) oder in gerichteten Diagrammen . Mir ist nicht klar, welchen Vorgang Sie ausführen müssen, um eine tatsächliche "Schleife" oder einen "Zyklus" in einem bestimmten Pfad zu erkennen. In jedem Fall kann die Ausführung der Algorithmen einige Zeit in Anspruch nehmen. Wenn Sie also nur die Anzahl der symbolischen Verknüpfungen zählen, sind Sie zu 90% am Ziel.

Bruce Ediger
quelle
Für praktische Zwecke ist es in Ordnung, nur die Anzahl der durchquerten Links zu zählen, zumal dies der Kernel tut. Selbst wenn Sie auf einen Pfad stoßen, der korrekt aufgelöst wurde und zu viele Symlinks enthält, können Sie diesen Pfad nicht für praktische Zwecke verwenden ( dh das beinhaltet nicht das manuelle Auflösen von Symlinks)
JanKanis