Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 364
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von Jorg3000 »

Hallo!
FileExists(FileName) kann FALSE liefern, obwohl eine Datei vorhanden ist. Dieses als Warnung und gleichzeitig als Frage, siehe ganz unten.

Ich nutze oft ein oder mehrere FileExists(FileName) vor komplexen Vorgängen, um schon vorab zu prüfen, ob alle benötigten Dateien vorhanden sind - um nicht erst innerhalb eines komplexen Vorgangs darüber zu stolpern.
In letzter Zeit hatte ich verzweifelte Kunden, wo mein Programm immer für zwei oder drei Tage nach der Installation funktioniert, dann plötzlich nicht mehr. Sie haben deshalb das Programm alle paar Tage neu installiert.

In Telefonaten mit Admins konnte ich nun eingrenzen, dass dort FileExists(FileName) = FALSE liefert, obwohl dort jeder User die Datei im Windows Explorer sehen kann und problemlos aufrufen kann.
Da fehlen einem die Worte. Es hat etwas gedauert, bis ich auf die Idee gekommen bin, dass Windows Schattenkopien die Ursache für dieses Verhalten sein könnten.
Zu Schattenkopien siehe VSS: Volume Shadow Copy Service https://de.wikipedia.org/wiki/Volume_Sh ... py_Service

Bei FileExists() wurde irgendwann mal ein zweiter Parameter FollowLink : Boolean = True ergänzt.
Bisher habe ich mich nicht darum gekümmert, denn ich dachte, dass ich mit SymLinks ja nichts zu tun habe und dass der Default-Wert TRUE wohl gut sei. ... Bis mein Programm auf Kunden stieß, wo Schattenkopien auf deren Windows Server aktiviert sind, um Backups und Speicherplatz zu optimieren.

Datei-Schattenkopien werden von Windows dynamisch bei Bedarf erstellt und als SymLinks ins Dateisystem eingetragen (Symbolische Verknüpfung). https://de.wikipedia.org/wiki/Symbolisc ... C3%BCpfung
Und weil bei FileExists() der FollowLink-Parameter default TRUE ist, nimmt die kleine Katastrophe hier ihren Lauf ... denn FileExists() findet die SymLink-Datei und löst den SymLink-Ziel-Pfad als tatsächlichen Speicherort auf - und versucht darauf ein zweites FileExists().
Und diese zweite Prüfung scheint das Problem zu sein: Es kann sein, dass der User Zugriffsrechte auf den umgeleiteten Speicherort hat, aber in der Regel wird ein normaler User darauf gar keine Zugriffsrechte besitzen. Die braucht er im Normalfall auch nicht, weil sich das Dateisystem im SymLink-Fall darum kümmert, dass der User den Datei-Inhalt trotzdem lesen kann.

Wenn ich es mir richtig zusammengesponnen habe (ich selbst habe kein Windows Server OS) schlägt FileExists(FileName) bei aktivierten Schattenkopien üblicherweise fehl, weil - wie ich vermute - normale User oder sogar Admins keine Zugriffsrechte auf den umgeleiteten Speicherort der Schattenkopien haben.

Nun endlich meine Frage:
Wenn ich mich in meinem Programm nicht dafür interessiere, wie das Dateisystem eine Datei gespeichert hat (direkt oder mittels SymLink oder auf'm Mond oder sonstwo) - wäre es dann nicht besser, wenn man FileExists grundsätzlich immer mit zweitem Parameter FollowLink=FALSE aufrufen würde? ... FileExists(FileName,FALSE);

Ich frage deshalb, weil ich nun geneigt bin, bei sehr sehr vielen FileExists() in meinem Programm ein ,FALSE einzufügen - ich aber keinen Denkfehler machen will, ob es dadurch ganz andere Probleme geben könnte. Was meint ihr?
Grüße, Jörg

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 364
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von Jorg3000 »

Nachtrag:
Das beschriebene Verhalten kann sogar zu einem selbstgemachten Datenverlust führen, z.B. bei ...

Code: Alles auswählen

if FileExists(DestFilename1) then Reset(File1) else Rewrite(File1); 
... wenn man durch das unterwartete Result FALSE fälschlicherweise davon ausgeht, dass eine Datei neu angelegt werden muss (leer), obwohl sie bereits als gefüllte Datei existiert.
Davon werden die Anwender gar nicht begeistert sein.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6780
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von af0815 »

Ich würde sagen, das ist ein Sideeffekt den Windows verursacht hat. Weil die Schattenkopie ist erst später und nachträglich eingeführt worden.

Die defaults von FileExists zu ändern ist keine gute Idee, damit zerstörst du existierenden Code. Sinnvoll ist es vielleicht auf den Zustand hinzuweisen (Bugreport), das der zumindest in Doku Einzug hält.

Vielleicht gibt es Reports auch bei MS, weil das Problem ist ja sicher nicht auf die FCL bzw. LCL beschränkt. Oder sieht MS da etwas spezielles vor in der WinAPI.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 364
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von Jorg3000 »

Hi!
Das default in FileExists() will ich gar nicht geändert haben, eben wegen der Kompatibilität.
Meine Frage wäre, ob man im Normalfall immer empfehlen kann, im seinen Aufrufen ein ...,FALSE anzuhängen oder ob jemand dabei Bedenken wegen anderer Effekte hätte (die mir gerade nicht einfallen).

In der Windows-API gibt es vieles, aber ein einfaches Windows-FileExists gibt es nicht. Deshalb wird es in der Delphi- und FreePascal-Funktion durch eine Abfrage von Datei-Attributen und FindFirstFile gelöst.

Von Microsoft gibt es zu dem Thema nichts, weil die FollowLink-Option von FileExists() eine Delphi-/FreePascal-spezifische Sache ist.
Würde es die Option in der Pascal-Funktion nicht geben, bestünde das Problem erst gar nicht.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6780
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von af0815 »

Bedenken ja, weil auch unter MS es normale Links geben kann. Sie sind nicht so verbreitet wie unter Linux. Die Frage ist, warum das speziell auf einem ServerBS auftritt, Hängt es wirklich mit der Schattenkopie zusammen oder gibt es dort noch zusätzliche Backupsoftware ?

Weil es gibt/gab Backupsoftware, die die History über Filesystem Links macht. Ist mir aber schon länger nicht mehr begegnet, da die meistem Server virtualisiert laufen.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 364
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von Jorg3000 »

Die Schattenkopien sind eine Funktion speziell von Windows Server, die der Admin wahlweise aktivieren kann, z.B. für ein Laufwerk.
Ob die dadurch erzeugten Datei-SymLinks durch Windows erzeugt werden oder von irgendeinem anderem Backup-Programm erzeugt werden, spielt ja keine Rolle, denn sie sind dann einfach da.

Und ich meine, dass mich solche SymLinks (das sind NICHT .lnk-Dateien!) des Dateisystems in meinem Anwendungsprogramm grundsätzlich nicht zu interessieren brauchen, wenn ich eine Datei ganz normal lesen, schreiben, löschen oder umbenennen will, denn die Schattenkopie-Funktion von Windows kümmert sich von selbst darum, den SymLink zu dereferenzieren, ohne dass ich von meinem Programm aus Einfluss darauf hätte.

Das Problem ist, dass man als Programmierer eines Windows-Programms nicht weiß, ob irgendein Kunde jetzt oder irgendwann Schattenkopien aktiviert (außerhalb meines Einflussbereichs) und dann alle im Lazarus-Programm üblicherweise verwendeten FileExists(Filename) plötzlich und unerwartet fehlschlagen. ... außer wenn man beim Aufruf explizit ( ..., FALSE) verwendet hat.

Es ist zusätzlich blöd, dass das Problem zunächst nicht auffällt (in den ersten Tagen), weil ein SymLink auf eine Schattenkopie erst dann erzeugt wird, wenn Windows nach ein paar Tagen feststellt, dass sich der Datei-Inhalt nicht mehr geändert hat und deshalb die Datei und ihre Backups auf eine gemeinsame, zentrale Kopie verlinkt werden können (Festplattenplatz sparen).

MmVisual
Beiträge: 1581
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 4 FPC 3.2.2)
CPU-Target: 32/64Bit

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von MmVisual »

Richtigerweise müsste eigentlich FileExists() erkennen dass es sich um eine Schattenkopie handelt und dann eben nicht den Link folgen.
Weil die Original Fundstelle ja bereits die Datei ist.

Ich würde das als Bug einstufen, den man als Lazarus/FPC Anwender auch nicht wirklich von Hand umgehen kann.
EleLa - Elektronik Lagerverwaltung - www.elela.de

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 587
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon, Laz 4.1 Fpc 3.2.3 und allerlei mit FpcUpDeLuxe
Kontaktdaten:

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von Niesi »

Jorg3000 hat geschrieben: Mi 12. Feb 2025, 08:30 Die Schattenkopien sind eine Funktion speziell von Windows Server, die der Admin wahlweise aktivieren kann, z.B. für ein Laufwerk.
Ob die dadurch erzeugten Datei-SymLinks durch Windows erzeugt werden oder von irgendeinem anderem Backup-Programm erzeugt werden, spielt ja keine Rolle, denn sie sind dann einfach da.

Und ich meine, dass mich solche SymLinks (das sind NICHT .lnk-Dateien!) des Dateisystems in meinem Anwendungsprogramm grundsätzlich nicht zu interessieren brauchen, wenn ich eine Datei ganz normal lesen, schreiben, löschen oder umbenennen will, denn die Schattenkopie-Funktion von Windows kümmert sich von selbst darum, den SymLink zu dereferenzieren, ohne dass ich von meinem Programm aus Einfluss darauf hätte.

Das Problem ist, dass man als Programmierer eines Windows-Programms nicht weiß, ob irgendein Kunde jetzt oder irgendwann Schattenkopien aktiviert (außerhalb meines Einflussbereichs) und dann alle im Lazarus-Programm üblicherweise verwendeten FileExists(Filename) plötzlich und unerwartet fehlschlagen. ... außer wenn man beim Aufruf explizit ( ..., FALSE) verwendet hat.

Es ist zusätzlich blöd, dass das Problem zunächst nicht auffällt (in den ersten Tagen), weil ein SymLink auf eine Schattenkopie erst dann erzeugt wird, wenn Windows nach ein paar Tagen feststellt, dass sich der Datei-Inhalt nicht mehr geändert hat und deshalb die Datei und ihre Backups auf eine gemeinsame, zentrale Kopie verlinkt werden können (Festplattenplatz sparen).
Wenn ich es richtig verstanden habe, dann ist auf der SSD der User die Datei nicht mehr vorhanden, sondern ein Systemlink zur tatsächlichen Datei. Und das System kümmert sich darum, dass die User die Daten lesen können.

So weit, so gut oder schlecht.

Dann ist doch aber der Parameter "FollowLink" exakt dafür da: Nach "FileExists(aFilename, true)" wird dem Link gefolgt und auf das Original zugegriffen. Und wenn das nicht passieren soll oder darf, dann wird FileExists(aFilename, false) aufgerufen. Und das System ist dafür verantwortlich, dass auf die Datei zugegriffen werden kann.

Oder sehe ich das jetzt falsch?

Nachtrag: Ist so eine Schattenkopie aus einem Programm heraus erkennbar? (FileGetAtrr oder ähnlich)
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6780
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von af0815 »

Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 364
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von Jorg3000 »

Unser Problem liegt darin, dass FileExists(Filename) sich standardmäßig um das Attribut "FILE_ATTRIBUTE_REPARSE_POINT" kümmert, obwohl es sich lieber gar nicht darum kümmern sollte.
Denn man kann ja trotz eines SymLink ganz normal auf "C:\Ordner\Datei.xyz" zugreifen, eben ohne das SymLink-Ziel auflösen zu müssen oder sich überhaupt irgendwie dafür interessieren zu müssen.

Nur weil FileExists() eigentlich überflüssig versucht, das SymLink-Ziel nachzuverfolgen, kommt es überhaupt zum Scheitern im Falle von Schattenkopien, weil jene eben nicht über die umgeleitete Adresse erreichbar sind, sondern ganz einfach über den ursprünglichen Pfad "C:\Ordner\Datei.xyz"

Die Lösung liegt darin, im Aufruf FileExists(Filename,FALSE) zu verwenden. Aber man muss das ,FALSE immer manuell ergänzen und darf es nicht vergessen, sonst läuft man unter Windows Server Gefahr, ein falsches Resultat (ein False Negative) zu erhalten.
Grüße, Jörg

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 587
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon, Laz 4.1 Fpc 3.2.3 und allerlei mit FpcUpDeLuxe
Kontaktdaten:

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von Niesi »

.

Da ich ebenfalls sehr oft FileExists() nutze, muss ich einfach mal blöd fragen: Welche Nachteile oder Katastrophen sind möglich, wenn FileExists(aFilename, false); verwendet wird?

Nicht nur unter diesen kaputten Microsoft-Systemen, sondern eben auch Linux, macOS usw. ...
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

MmVisual
Beiträge: 1581
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 4 FPC 3.2.2)
CPU-Target: 32/64Bit

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von MmVisual »

Jörg, du siehst das falsch.

FileExists(...,FALSE) ist eben nicht die Lösung. Es mag zwar als Krücke in deinem Fall funktionieren, jedoch nicht wenn man für mehrere OS eine SW schreibt.

Ich bin nach wie vor der Meinung dass Fileexists wissen muss wann es die richtige Datei gefunden hat und dann nicht weiter den Link verfolgen.
Normalerweise ist es so:
Link > Link > Link > File

Jedoch bei der Schattenkopie ist es so:

File > Link > Schatten-File > Link...

Und da darf Fileexists nicht die Links folgen, weil der erste Verweis ja schon die Datei ist.

Dann gibt es noch den Mix:

Link > Link > Link > File > Link > Schatten-File > Link...

Da muss FileExists dann wissen dass es die Datei ist und nicht weiter suchen.
EleLa - Elektronik Lagerverwaltung - www.elela.de

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 364
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von Jorg3000 »

Aber wenn du z.B. StringList.LoadFromFile(Filename) macht, gibst du doch nur den einfachen Pfad an, nicht irgendeinen aufgelösten SymLink-Pfad - und es funktioniert trotzdem.
Ich will damit sagen: Mich juckt es doch gar nicht, ob es SymLinks gibt oder nicht.
Oder gibt es Situationen, wo ich nicht einfach StringList.LoadFromFile(Filename) machen kann, sondern den Filename zunächst in meinem Lazarus-Programm SymLink-mäßig nachverfolgen muss?
Ich dachte es wäre das Tolle an SymLinks oder Hardlinks, dass man als Anwender nichts davon mitkriegt.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von Socke »

Jorg3000 hat geschrieben: Mi 12. Feb 2025, 11:47 Aber wenn du z.B. StringList.LoadFromFile(Filename) macht, gibst du doch nur den einfachen Pfad an, nicht irgendeinen aufgelösten SymLink-Pfad - und es funktioniert trotzdem.
Ich will damit sagen: Mich juckt es doch gar nicht, ob es SymLinks gibt oder nicht.
Oder gibt es Situationen, wo ich nicht einfach StringList.LoadFromFile(Filename) machen kann, sondern den Filename zunächst in meinem Lazarus-Programm SymLink-mäßig nachverfolgen muss?
Ich dachte es wäre das Tolle an SymLinks oder Hardlinks, dass man als Anwender nichts davon mitkriegt.
Dem pflichte ich bei.

Unter *nix muss ich doch eigentlich immer wissen, was für eine Datei ich anspreche: ein Symlink ist da auch nur eine Datei.

Wie wäre es, wenn du deine Prüfungen mit FileExists() ganz weg lässt und stattdessen zum Start deiner komplexen Routine alle Dateien in Streams öffnest. Im weiteren Programmverlauf kannst du die Streams weitergeben. Wenn eine Datei nicht zugreifbar oder nicht vorhanden ist, kannst du das über die Exceptions abfangen. Damit vermeidest du auch Race Conditions (Datei wird zwischen FileExists und FileOpen gelöscht).
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 364
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: Vorsicht bei FileExists() und Schattenkopien unter Windows Server

Beitrag von Jorg3000 »

Socke hat geschrieben: Mi 12. Feb 2025, 13:42 ... alle Dateien in Streams öffnest. ... Damit vermeidest du auch Race Conditions (Datei wird zwischen FileExists und FileOpen gelöscht).
Das stimmt!
Da ich in meinem größten Programm an 130 Stellen FileExists() verwende, habe ich nun alle Stellen zunächst durch ein kapselndes FileExists2() ersetzt, worin der zweite Parameter FALSE gesetzt wird.

Code: Alles auswählen

function FileExists2(const fn: RawByteString): Boolean;  overload;
begin
  Result:=FileExists(fn,false); 
end;

function FileExists2(const fn: UnicodeString): Boolean;  overload;
begin
  Result:=FileExists(fn,false);
end; 
Wenn jemand Argumente gegen den zweiten Parameter FollowLink=FALSE hat, "so möge er jetzt sprechen oder für immer schweigen" :lol: ... denn ich habe kaum eine andere Wahl, als es schon morgen auf einen Kunden loszulassen, der das Programm täglich benötigt, aber die Schattenkopien für sein ganzes Server-Laufwerk nicht abschalten kann/will.

Ich wüsste in meinem Programm keinen Fall, wo ich FollowLink=true benötigen würde. Es genügt mir immer, wenn ich weiß, ob es an dem ursprünglichen Pfad einen Eintrag gibt, egal ob echte Datei oder SymLink.

Antworten