Sind verkettete Befehle atomar?

8

Wenn ständig in eine Datei geschrieben wird und ich die Kontrolle über die Datei mit root übernehmen möchte, könnte ich Folgendes tun:

sudo rm somefile; sudo touch somefile

Kann der Anhängevorgang zwischen diesen beiden Befehlen an die Datei anhängen? Wenn ja, gibt es eine Möglichkeit, um sicherzustellen, dass kein anderer Befehl dazwischen ausgeführt wird?

Darth Egregious
quelle
4
Dies könnte ein XY-Problem sein . Können Sie das zugrunde liegende Problem erklären, das Sie lösen möchten?
Nate Eldredge
1
@NateEldredge: Ganz und gar nicht. Ich kann verstehen, warum es so scheint, basierend auf meiner erfundenen Hypothese. Hab nicht mal ein Problem. Nur neugierig.
Darth Egregious
5
Der Prozess wird wahrscheinlich weiterhin in die gelöschte Datei schreiben und nicht einmal versuchen, die neue zu öffnen.
Random832
Meine Annahme in der Hypothese stellte sich als etwas falsch heraus, aber ich denke, Sie könnten "Anhängen" in "Öffnen" oder "Berühren" ändern, und es wäre immer noch sinnvoll.
Darth Egregious

Antworten:

12

Eine verkettete Befehlszeile ist im Grunde ein kleines Shell-Skript. Der erste Befehl wird mit der üblichen fork + exec-Prozedur ausgeführt, auf das Beenden gewartet und der zweite auf die gleiche Weise ausgeführt. Zwischen den beiden Befehlen gibt es eine beliebige Zeitspanne, die die Shell für ihre Buchhaltung und Verarbeitung benötigt, während der gewöhnliche Mehrfachverarbeitung stattfindet und beliebige andere Prozesse beliebige andere Dinge tun können. Die Antwort lautet also "Nein". (Wenn Sie dies tatsächlich tun, werden Sie feststellen, dass der Verzeichniseintrag für somefileverschwindet, aber die Datei selbst bleibt (da sie von einem Prozess geöffnet wird), bis sie geschlossen wird. Der von der Datei verwendete Speicherplatz wird erst dann zurückgefordert Der touchBefehl erstellt eine neue, nicht verwandte Datei mit demselben Namen und Pfad.)

Wenn Sie den Besitz der Datei in root ändern möchten, tun Sie dies einfach sudo chown root:root somefile(obwohl ich nicht sicher bin, wie sich dies auf Prozesse mit einem geöffneten Dateihandle auswirkt). Wenn Sie den aktuellen Dateiinhalt zerstören möchten, versuchen Sie es truncate -s 0 somefile(der laufende Prozess wird weiterhin an die jetzt leere Datei angehängt). Wenn es etwas anderes ist, klären Sie vielleicht, was Sie tun möchten.

Tom Hunt
quelle
Danke Tom. Ich war nicht so sehr mit meiner hypothetischen Situation beschäftigt. Ich habe mich nur gefragt, ob so etwas möglich ist.
Darth Egregious
In Bezug auf die allgemeine Frage glaube ich nicht, dass es wirklich eine Möglichkeit gibt, die Atomizität in einem Shell-Skript sicherzustellen. Sie können Sperrdateien und dergleichen verwenden, um sicherzustellen, dass zwei kooperierende Prozesse nicht aufeinander treten, und Sie können Berechtigungen verwenden, um sicherzustellen, dass ein beliebiger Prozess Ihre Arbeit nicht betreten kann. Das Verhindern, dass etwas anderes ausgeführt wird, würde jedoch die Mehrfachverarbeitung des Kernels stark beeinträchtigen. Ich glaube nicht, dass Ring-3-Code das überhaupt kann.
Tom Hunt
Sie können obligatorische Dateisperren verwenden, wenn diese in Ihrem Dateisystem aktiviert und verfügbar sind.
Roaima
5
chownEinige Dateien wirken sich nicht auf Prozesse aus, die ein Dateihandle für eine Datei haben.
PSkocik
ZB kannst du tun exec 3>somefile; sudo chmod 0600 somefile; echo hello world >&3; sudo cat somefile; exec 3>&-.
PSkocik
6

Nein. Ein rmBefehl, dem ein Befehl folgt, touchist überhaupt nicht atomar. Es wird eine lange Zeit zwischen den beiden Befehlen dauern - möglicherweise im Millisekundenbereich. In dieser Zeit kann viel passieren. Wenn Sie Pech haben, laufen Ihre Sudo-Anmeldeinformationen möglicherweise sogar ab.

Ein einzelnes Programm, das die Aufrufe unlinkund openaufruft, würde ein viel kürzeres Fenster für ein Rennen lassen, aber es könnte immer noch passieren.

Ein sicherer Ansatz wäre, die neue Datei mit einem temporären Namen zu erstellen und den renameSystemaufruf zu verwenden. Das "Überschreiben" eines Namens mit dem renameSystemaufruf ist garantiert atomar. Dies könnte mit touchund erreicht werden mv.

Ein Prozess, der die alte Datei zum Schreiben geöffnet hat, kann jedoch noch lange nach dem Löschen darauf schreiben. Dies ist sowohl für eine mit unlinkals auch für eine gelöschte Datei der Fall rename.

Kasperd
quelle
3

Sobald ein Prozess ein Dateihandle zum Lesen geöffnet hat, spielt es keine Rolle, was Sie mit dem Besitz oder den Berechtigungen tun: Der Prozess kann weiterhin auf die Datei zugreifen. Sie können die Datei sogar löschen und der Prozess kann weiterhin über das Dateihandle darauf zugreifen.

Betrachten Sie die Berechtigungen als Steuergatter zum Abrufen eines Dateihandles.

Wenn Sie eine Datei atomar erstellen möchten, gehen Sie stattdessen folgendermaßen vor:

sudo rm somefile
sudo touch somefile

Sie könnten dies in Betracht ziehen:

sudo touch anotherfile
sudo perl -e "rename 'anotherfile', 'somefile'"

das wird atomar ersetzen somefilemit anotherfile. (Ich hätte es vorgezogen, zu verwenden, mv -faber ich kann keine Anweisung finden, die garantiert, dass dies den rename(2)Systemaufruf aufruft .


Vielleicht könnten Sie Ihre Frage aktualisieren, um zu erklären, was Sie unter "Kontrolle über die Datei übernehmen" verstehen. Möglicherweise haben Sie hier ein XY-Problem .

Roaima
quelle
Verwenden Sie mv -fdiese Option , um einen rename(2)Systemaufruf zu erhalten . Sie haben Recht, das lnwird nicht, weil es immer den link(2)Systemaufruf verwendet, der nicht atomar ersetzt werden kann. ln -ftut nur zuerst unlink(2)auf das Ziel.
Peter Cordes
@ PeterCordes ah ja natürlich, danke. Ich hatte lnim Gehirn. Ich war mir nicht sicher, wie ich rename(2)garantiert
mitmachen
POSIX garantiert, dass mv"Aktionen ausführen soll, die" entsprechen rename(old, new). pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html . mv -fwirkt wie mv -iwenn das Ziel nicht beschreibbar ist, aber es ist nicht wie cp -foder ln -fwo es zuerst entkoppeln wird.
Peter Cordes
2

Nein, aber

sudo rm somefile; sudo touch somefile

ist in den meisten Situationen sicher.

Die meisten Prozesse öffnen Dateien und verwenden dann den erfassten Dateideskriptor, um auf den Dateiinhalt zuzugreifen.

Wenn ein Prozess eine Datei öffnet, in sh:

 exec 3>somefile

Dann kann ein anderer Prozess die Verknüpfung einer Datei aufheben (= entfernen), und der Dateiskriptor, auf dem eine Datei beim ersten Prozess geöffnet war (in diesem Fall 3), verweist weiterhin auf den ursprünglichen Inhalt einer Datei, die jetzt eine Datei in der Schwebe ist.

sudo touch somefile

erstellt eine neue, nicht verwandte Somefile, und alle Prozesse, bei denen die alte Somefile geöffnet war und die jetzt nur einen Dateideskriptor verwenden, um darauf zu verweisen, sind nicht betroffen, da sie auf eine andere Datei verweisen - eine, die sich jetzt in der Schwebe befindet.

Wenn ein Nicht-Root-Prozess versucht, auf eine Datei mit Namen zu verweisen, wird ein EPERM-Fehler angezeigt, da sich die neue Somefile im Root-Besitz befindet.

Wenn Sie verhindern möchten, dass mehrere Prozesse unter demselben Benutzer (z. B. root) eine Datei beschädigen, verfügt Linux über eine obligatorische und empfohlene Dateisperrung. Sie können den flockBefehl in Shell-Skripten zum Sperren von Hinweisdateien verwenden (weitere Informationen finden Sie in der Manpage).

PSkocik
quelle