ShFileOperation vs RemoveDirectory und DeleteFile

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.