Discussion:
Files.move() scheitert, wirft aber keine Ausnahme
(zu alt für eine Antwort)
Christoph Schneegans
2016-01-29 17:22:16 UTC
Permalink
Hallo allerseits!

In meiner Applikation gibt es folgenden Aufruf:

java.nio.Files.move(source, target,
java.nio.file.StandardCopyOption.REPLACE_EXISTING);

Nun ist offenbar – bislang einmalig – folgendes passiert:

• Die vorhandene, nicht leere Datei target wurde geleert bzw. durch eine
0 Bytes großen Datei ersetzt.
• source ist noch unverändert vorhanden (und ist nicht leer, sondern ca.
1,5 MB groß).
• Es gab _keine_ Ausnahme.

Die Applikation wird unter CentOS 5.11 ausgeführt. source und target
sind Pfade im lokalen Dateisystem. Dieselbe Operation hat zuvor in fast
einer Million Fällen fehlerfrei funktioniert.

Was könnte hier passiert sein?
--
<http://schneegans.de/lv/> · Validator für BCP 47
Stefan Ram
2016-01-30 07:09:27 UTC
Permalink
Post by Christoph Schneegans
Die vorhandene, nicht leere Datei target wurde geleert bzw. durch eine
0 Bytes großen Datei ersetzt.
source ist noch unverändert vorhanden (und ist nicht leer, sondern ca.
1,5 MB groß).
Es gab _keine_ Ausnahme.
Ich würde das dann in eine eigene Methode verpacken, die
(gegebenenfalls vor dem Bewegen das Ziel sichert und dann)
nach dem Bewegen eine Verifikation vornimmt und dann eine
Ausnahme wirft, falls die Verifikation einen Fehler entdeckt.
Bernd Laengerich
2016-02-01 08:30:43 UTC
Permalink
Post by Stefan Ram
[move-probleme]
Ich würde das dann in eine eigene Methode verpacken, die
(gegebenenfalls vor dem Bewegen das Ziel sichert und dann)
nach dem Bewegen eine Verifikation vornimmt und dann eine
Ausnahme wirft, falls die Verifikation einen Fehler entdeckt.
Das halte ich für keine gute Idee. move sollte atomar ablaufen, wenn man
ATOMIC_MOVE angibt und das FS dies unterstützt. Alle anderen Fälle sind
undefiniert, wenn der anderweitige Zugriff auf die Datei nicht synchronisiert
erfolgt.
Eine Kapselung, die den anderweitigen Zugriff auf die Datei nicht
berücksichtigt, schafft an dieser Stelle IMHO noch mehr Komplexität und
Fehlermöglichkeiten.

Bernd
--
Meine Glaskugel ist mir leider unvorhersehbarerweise vom Balkon gefallen.
P.Liedermann in defa
Bastian Blank
2016-01-30 10:44:49 UTC
Permalink
Post by Christoph Schneegans
java.nio.Files.move(source, target,
java.nio.file.StandardCopyOption.REPLACE_EXISTING);
• Die vorhandene, nicht leere Datei target wurde geleert bzw. durch eine
0 Bytes großen Datei ersetzt.
• source ist noch unverändert vorhanden (und ist nicht leer, sondern ca.
1,5 MB groß).
• Es gab _keine_ Ausnahme.
Kannst du Zeigen das es zu dem Zeitpunkt genau so aussah, sprich auch
die Quelldatei schon diesen Inhalt hatte?
Post by Christoph Schneegans
Die Applikation wird unter CentOS 5.11 ausgeführt.
Oje, updaten auf was nicht prähistorisches?
Post by Christoph Schneegans
source und target
sind Pfade im lokalen Dateisystem.
Das gleiche Filesystem?
Post by Christoph Schneegans
Was könnte hier passiert sein?
Die Quelldatei war leer als sie verschoben wurde und wurde danach neu
angelegt.

Bastian
Andreas Karrer
2016-02-01 14:35:37 UTC
Permalink
Post by Christoph Schneegans
Hallo allerseits!
java.nio.Files.move(source, target,
java.nio.file.StandardCopyOption.REPLACE_EXISTING);
• Die vorhandene, nicht leere Datei target wurde geleert bzw. durch eine
0 Bytes großen Datei ersetzt.
• source ist noch unverändert vorhanden (und ist nicht leer, sondern ca.
1,5 MB groß).
• Es gab _keine_ Ausnahme.
Die Applikation wird unter CentOS 5.11 ausgeführt. source und target
sind Pfade im lokalen Dateisystem. Dieselbe Operation hat zuvor in fast
einer Million Fällen fehlerfrei funktioniert.
Das sollte nicht passieren können.

Files.move() geht bei Files auf einem lokalen Unix/Linux-Filesystem so vor:

- target wird gelöscht (weil REPLACE_EXISTING)
- rename(source, target) (der System call rename(2))
- wenn rename erfolgreich war, dann ist die Sache gegessen
- EISIR (target ist ein Directory) wird irgendwie abgehandelt,
interessiert hier nicht
- beim Fehler EXDEV (source und target auf unterschiedlichen Filesystemen):
- das File wird Block für Block kopiert
- die Metainformationen (mode, owner etc) werden kopiert
- source wird gelöscht.

Wenn source und target zwei Files auf dem gleichen lokalen Fileystem
sind, dann ist der Systemcall rename(2) unter CentOS atomar, d.h. das
beschriebene Verhalten kann nicht auftreten.

Es handelt sich im vorliegenden Fall also offenbar um Files auf
unterschiedlichen Filesystemen (oder auch dem gleichen Filesystem, das
an unterschiedlichen Mountpoints eingehängt ist). Dieser Code ist
nicht atomar, d.h. er kann (z.B. durch ein Signal, Disk voll,
Hardwarefehler oder so etwas) mittendrin unterbrochen werden. Im
beschriebenen Fall wäre das dann passiert, als das target-File bereits
zum Schreiben geöffnet war, aber noch keine Daten kopiert wurden. Das
sollte aber immer eine IOException zur Folge haben.

http://cr.openjdk.java.net/~littlee/7129029/webrev.00/src/solaris/classes/sun/nio/fs/UnixCopyFile.java.html

Es ist natürlich auch denkbar, dass der vorgefundene Zustand (source
intakt, target überschrieben und 0 Bytes gross) gar nicht durch
Files.move() ausgelöst wurde, sondern durch irgendwelchen anderen Code
in der Applikation.


- Andi
Christoph Schneegans
2016-02-02 16:55:12 UTC
Permalink
Post by Andreas Karrer
(...)
Wenn source und target zwei Files auf dem gleichen lokalen Fileystem
sind, dann ist der Systemcall rename(2) unter CentOS atomar, d.h.
das beschriebene Verhalten kann nicht auftreten.
source und target liegen tatsächlich im gleichen lokalen Dateisystem.
Nach gründlicher Logfile-Analyse scheint es nun in der Tat
wahrscheinlich, daß ein anderer Prozeß auf die Dateien zugegriffen hat,
also Files.move() korrekterweise eine leere Datei nach target verschoben
hat, und source anschließend wieder angelegt wurde. Da dürften sich
weitere Spekulationen wohl erübrigen.

Danke jedenfalls für die Antworten!
--
<http://schneegans.de/computer/safer/> · SAFER mit Windows
Lesen Sie weiter auf narkive:
Loading...