ShFileOperation vs RemoveDirectory und DeleteFile
« | 20 Nov 2022 | »Ein weiterer halb-zerstörter windowsfilters
Docker Fall hat mir
aufgezeigt, dass APIs wie RemoveDirectory()
und DeleteFile()
entgegen
allen Erwartungen nicht funktionieren.
Doch der Windows Explorer schafft die Löschungen problemlos. Und dafür gibt es eine Erklärung: Er nutzt die Shell-APIs.
Vorgeschichte
Windows unterscheidet sich in einem Punkt sehr von anderen Systemen, weil es nämlich den Aufbau seiner “Shell” als Funktionsschnittstelle offenlegt.
Unter Unix/Linux ist “die Shell” lediglich ein Konsolenverwalter wie
bin/ksh
oder bin/bash
, der über Befehle Prozesse starten und
verknüpfen lässt.
Doch in Windows wurde eine “benutzerfreundlichere” Verwaltungsebene erdacht,
und diese ist in shell32.dll
implementiert und ein explorer.exe
zeigt sie
uns grafisch an, angefangen bei jeder Dateiauflistung, bis hin zur Vorschau
von Webinhalten.
shell32.dll
sieht auch das Dateisystem anders, während die kernel32
APIs bei Laufwerken und NT-Root-Pfaden die Hierarchie aufbauen, führt die
Shell-API virtuelle Namensräume, wie den “Computer” und den “Desktop” ein,
auf dem dann die Laufwerke liegen.
Und das führt dazu, dass viele nativen APIs von kernel32
in irgend einer
ähnlichen Form auch in der Shell abgebildet sind, allerdings ein bisschen
mehr und etwas andere Pfade verstehen.
Und bisher dachte ich, dass die Shell-API nur ihre virtuellen Spielchen in
native Pfade übersetzt und im Hintergrund dann die kernel32
APIs bedient.
RemoveDirectory mit Access Denied
RemoveDirectory()
kann Links (SymLinks, Junctions) löschen und tut das auch
brav. Doch bei meinen windowsfilters
Löschversuchen scheiterten meine
Aufrufe, wie auch der cmd
Befehl rmdir
mit einem Access-Denied, das
keinen Sinn machte. Die erforderlichen Rechte waren vorhanden und kein
Prozess sperrte den Verzeichnispfad.
Doch wenn man diesen verlinkten Ordner im Explorer zur Löschung auswählte, verschwand dieser auch prompt.
Nach einigem Suchen fand ich Beiträge, die meine Beobachtungen bestätigten:
Bestimmte Links zu Shell-UI-Ordnern erhalten einen besonderen Schutz durch die
kernel
APIs. Diese können bzw. dürfen nicht gelöscht werden.
Das sind Dokumente
, Musik
, Videos
… und wie sie sonst noch
alle heißen.
Man kann sie auch nicht von cmd
aus mit rmdir
oder del
löschen, doch der
Explorer schafft es stets ohne Probleme.
ShFileOperation
löst das Problem
Wirft man aber solche “störrischen” Pfade der API
SHFileOperation
mit SHFILEOPSTRUCT::wFunc = FO_DELETE
vor die Füße, verschwinden die Links
genau so wortlos, wie sie es im Explorer tun.
Die Funktion, bzw. die SHFILEOPSTRUCT
Struktur hat nur ein paar
Besonderheiten bei den Pfaden: Diese müssen doppelt-null-terminiert sein,
weil in einem Parameter mehrere null-terminierte Dateipfade vorkommen dürfen.
Deshalb muss SHFILEOPSTRUCT::pFrom = "path/to/link\0\0"
derart gesetzt sein.
Und weil ich keine UI-Interaktionen möchte, ist auch noch
fFlags = FOF_NO_UI
notwendig.
Fazit
Ich habe also die WinAPI-Implementierung im GATE-Framework angepasst und den
RemoveDirectory()
und DeleteFile()
Aufrufen einen Sonderfall angefügt:
Wenn der Primäraufruf fehlschlägt, wird
ShFileOperation()
probiert. Ist es ebenso nicht erfolgreich, wird der Fehler des Primäraufrufes weiterbehandelt.
Mit diesem Patch konnte ich nahezu vollständig die Verzeichnisse einer
weiteren versauten windowsfilters
Installation löschen, ohne dass auf dem
Server Probleme auftraten, weil Dateien am Hostsystem mitgelöscht wurden.
Aber seltsam ist das schon!
Dass ausgerechnet die kernel
API von etwas abgelenkt wird, was die Shell-API
problemlos hinbekommt, ist für mich eine verkehrte Welt.
Denn meine Erwartungshaltung ist, dass Shell-APIs ein paar geheime Schützlinge
haben darf, die sie nicht löscht, dass aber die kernel
APIs alles dürfen.
However … Gefahr erkannt, Gefahr gebannt.