C# - Effiziente Suche im kompletten Dateisystem

Kokujou

Lieutenant
Registriert
Dez. 2017
Beiträge
1.010
Hallo ihr Lieben,

Ich hab ein kleines Projekt am Laufen wo ich Musik aus dem Dateisystem in eine Datenbank eintrage. Und zwar per Referenz, ich dupliziere also nicht meine gesamte Sammlung.
Sollte ich aber irgendwann mal eine Datei verschieben oder umbenennen wäre das Programm im Eimer.

Hier also meine Frage: Ich würde gerne einen MD5 vom Datei-Inhalt erstellen und damit das gesamte Dateisystem durchsuchen um im Falle einer kaputten Referenz die korrekte Datei wiederzufinden.

Allerdings klingt das höllisch imperformant. habt ihr für sowas vielleicht Ansätze die etwas performanter sind? oder muss man da in den Sauren Apfel beißen und das gesamte Dateisystem rekursiv durchsuchen und von jeder Datei den Hash berechnen... (ouch... )
 
Also ich würde mir auf jeden Fall alle Hashes der gescannten Dateien merken zusammen mit Änderungsdatum, Größe und Name. Und so lange sich die nicht geändert haben, würde ich auch keinen neuen Hash berechnen lassen.

Wenn du also was verschoben hast und danach der Scan läuft, müssen nur noch "unbekannte" Dateien eben neu gehasht werden (also Dateien, deren Name sich geändert hat, deren Inhalt sich geändert hat oder deren Zeitstempel sich geändert hat).

Ich nehme nicht an, dass dein Programm ständig läuft? Wenn ja doch, wären Dateisystem-Watcher ebenfalls eine Möglichkeit.
 
Zuletzt bearbeitet: ("ja" durch "doch" ersetzt)
  • Gefällt mir
Reaktionen: Kokujou
Wenn dein Programm läuft kannst du mit nem File Watcher arbeiten.

https://docs.microsoft.com/de-de/dotnet/api/system.io.filesystemwatcher.changed?view=net-5.0

Falls nicht, heißt es rekursiv durchforsten. Um Zeit zu sparen würde ich aber eher die MFT parsen, was tausend mal schneller geht als rekursiv über :\ zu iterieren, vor allem bei HDDs.

https://stackoverflow.com/questions/21661798/how-do-we-access-mft-through-c-sharp

Bei Dateisystemen ohne MFT (FAT und wenn du mit Core arbeitest natürlich auch Linux und Mac) bleibt dir allerdings nichts anderes übrig als rekursiv aufzulisten.
 
  • Gefällt mir
Reaktionen: SoDaTierchen und abcddcba
Die ganze Platte crawlen ist langsam, bei SSDs inzwischen aber nicht unbedingt extrem langsam. Kann also durchaus akzeptabel sein wenn das nur im Notfall passiert wie hier. Ich würde da dann auch drauf achten das du wenn du die erste verschobene Datei findest ausprobierst ob nicht einfach ein ganzer Ordner verschoben wurde. Du kannst vermutlich einiges rausholen in dem du die typischen Fälle optimiert behandelst, z.B. wenn in einem Ordner alle Dateien vom Namen her bekannst sind außer einer, ist das entweder eine neue Datei oder eine gerade umbenannte Datei.

Ich würde mir auch die Dateinamen und andere Metadaten merken, wie schon erwähnt. Das ist viel schneller zu checken als ein Hash. Wenn der Hash häufiger benutzt wird kann es auch besser sein einen zweiten Hash von einem kleinen Teil der Datei zu speichern. Der ist viel schneller zu checken um Dateien auszuschließen. Es gibt auch schnellere Hashes als MD5 (z.B. BLAKE3), aber nicht unbedingt mit Implementierung in deiner Progammiersprache
 
Wenn er MD5-Hashes berechnen will, dann spielt die Zeit für das Iterieren über das Dateisystem aber vermutlich die extrem untergeordnete Rolle. Und die Auswahl des Root-Ordners (oder der verschiedenen Root-Ordner) für den rekursiven Scan sollte halt auch einigermaßen vernünftig gewählt werden.
 
also ständig laufen tut mein Programm schonmal nicht, zumindest würde ich nicht gerne davon ausgehen. ich meine theoretisch ist es dafür ausgelegt aber da ich noch viel daran schraube stürzt es manchmal ab oder stoppt sich selbst oder so und wenn zwischendurhc was geändert wird wäre mir das doch zu unsicher.

ich würde auch gerne nur Inhalt-Spezifische Metadaten verwenden. Die Datei-Größe als Filterkriteirum zu verwenden ist z.B. schonmal eine gute Idee. Damit würdest du so ziemlich alles ausschließlen a la DLL Files, ausführbare Programme... die Idee ist echt gut, wenn man die Größe auf Byte-Level verwendet reicht das allein ja mitunter schon als unique identifier. Nicht immer aber oft.

Dazu eine Frage... kann es sein dass sich die Dateigröße ohne zutun ändert? Wenn man sie z.B. im Browser oder im Media-Player wiedergibt? Ich meine im byte-Bereich. Nicht dass irgendwelche File-Header sich da vielleicht ändern. sonst bau ich vielleicht ne Abweichung beim Check ein.

kennt denn jemand einen schnelleren Hash als MD5 der für C# implementiert ist?

MFT käme für mich in Frage, das würde ich mir mal angucken, das klingt interessant
 
Betriebssysteme halten doch sowieso such-indizes vor für ihre eigene Suche vor. Ich würde also gar nicht erst selber suchen, sondern mich da einklinken.
Jedenfalls in getrennten Thread asynchron suchen mit verringerter Priorität so das man das Programm weiter normal benutzen kann, das hat ja schließlich keine Eile, solange die Datei am Ende wieder gefunden wird.
Darauf aufbauend: Such-Heuristiken, man muss ja nicht immer gleich auf ganz C:\ bzw. / mit Suchen anfangen.
 
BeBur schrieb:
Betriebssysteme halten doch sowieso such-indizes vor für ihre eigene Suche vor. Ich würde also gar nicht erst selber suchen, sondern mich da einklinken.
ich höre ;) aber ich shcätze mal das kann nicht parallel mit dem MFT-Ansatz laufen den ich gelesen habe, richtig?

Also ich persönlich habe 4 Festplatten und müsste sie alle rekursiv durchsuchen und ich kann auch nicht sagen auf welcher der Festplatten der Musik-Track existert
 
Ein Player wird eine Datei nicht ohne Zutun modifizieren. Wenn doch, dann würde ich den Player in Frage stellen.

Ausnahmen sind halt richtige Medienverwaltungstools wie iTunes.
 
Man könnte das vielleicht sogar einfach so machen, wie so ziemlich alle Programme: Nur explizit ausgewählte Ordner tracken lassen von dem Programm und die Erkenntniss, dass Nutzer nicht andauernd ihre MP3 Dateien verschieben und vergessen, wo sie liegen.
 
also kann man sagen dass es unmöglich ist dass sich audio-files ohne explizites und bewusstes zutun des eigentümers ändern?

ich rede ja nicht von sowas wie audio-bearbeitung sondern ... kA irgendwelche metadaten die in den File-Headern versteckt sind. ich kenne mich da nicht so aus.
 
Unmöglich ist nichts. Schon im Windows Explorer kannst du Multimedia-Dateien verändern... Deshalb wie gesagt: Es hängt von der Software ab und was du damit machst.
 
  • Gefällt mir
Reaktionen: Kokujou
Verwende das Programm doch erst mal. Und schau dir dann an, welche bzw. ob überhaupt Probleme auftreten. Der Thread ist im Prinzip ein gutes Beispiel für feature creep.
 
Wenn dann eine Datei nicht mehr gefunden wird gib halt eine Fehlermeldung aus, mit der Option ueber einen klassischen Open Dialog einen neuen Ordner anzugeben.

Zum Vergleich: Bei Steam muss man ja, wenn man seine Library verschoben hat, den Ordner auch neu einbinden.
 
Ranayna schrieb:
Wenn dann eine Datei nicht mehr gefunden wird gib halt eine Fehlermeldung aus, mit der Option ueber einen klassischen Open Dialog einen neuen Ordner anzugeben.

Zum Vergleich: Bei Steam muss man ja, wenn man seine Library verschoben hat, den Ordner auch neu einbinden.
wäre auch ne möglichkeit aber für batch-files wäre es ziemlich convenient dass sie einfach selbst ermittelt werden.

man könnte invalide file auch einfach löschen und dann halt den kompletten musik-ordner neu einlesen, die files skippen die hash-wise schon existieren und hätte es auch geschafft... aber dann müsste man auch erstmal den hash für jedes eingegebene File laden.
 
Kokujou schrieb:
also kann man sagen dass es unmöglich ist dass sich audio-files ohne explizites und bewusstes zutun des eigentümers ändern?
So lange Du als einziger "Eigentümer" auf die Dateien zugreifst, mag das funktioniert.

Mal davon abgesehen, dass ich es schon fraglich finde, auf längere Zeit einzig von einem lokalen Verschieben (auf dem PC/auf der HDD auszugehen). Damit fällt für mich ein Tracking über die MFT oder eine OS-Datenbank aus.

Eigene Indizierung per Dateiname, Dateigröße und "last modified date" (Änderungsdatum) reicht in aller Regel, wenn man sicher ist, dass niemand eine Datei ändert (nicht nur Trojaner lassen grüßen).

Das dann zusammen mit der Prüfsumme, um auch sicher zu stellen, dass sich die Datei auf Dauer nicht durch BitRot geändert hat. Nun gut, man könnte auch ZFS oder BTRFS (o.Ä.) als Dateisystem nutzen und diesem die Prüfung überlassen.

Mit viel Glück, passenden Admin-Rechten und "gebastel" kommt man u.U. auch an die Prüfsummen des Dateisystems und könnte damit die Prüfsummenerstellung beschleunigen.

Alternativ könntet Du auch anstatt MD5 auf Blake3 (o.Ä.) umsteigen, dessen Berechnung soll schneller sein. Das ändert aber nichts daran, dass Du die zu prüfenden Dateien zwangsweise komplett einlesen musst (was bei HDD und parallelem Zugriff nicht performant ist).
 
  • Gefällt mir
Reaktionen: Kokujou
stimmt schon... ich hab gerade die hashs eingelesen und bei nur 800 Tracks hat es gut ne Minute gedauert...
dateiname würde ich nicht indizieren... bedenke wenn man das file mal umbenennt, kommt öfter vor besonders weil ich viel garbage habe XD File Size und File Content würde ich als einzige Content-Indikatoren zu Rate ziehen.

Aber genau dieses bitRot macht mir ein wenig Sorgen. Ich hab eingie Serien die beschädigt sind. vielleicht bei der Übertragung verreckt, vielleicht auf älteren platten "Angerottet" deswegen würde ich beim Vergleich der File-Size tatsächlich eine Spanne hinzufügen.

was meint ihr, was für ein wert wäre denn ausreichend? Reichen 100B um Bitrot auszukontern? oder sollte ich lieber auf KB Basis arbeiten?
 
Kokujou schrieb:
stimmt schon... ich hab gerade die hashs eingelesen und bei nur 800 Tracks hat es gut ne Minute gedauert...
Der initiale Aufwand dürfte meist zu vernachlässigen sein. Aufwändig ist die Suche, insb. wenn Du schreibst:
Kokujou schrieb:
dateiname würde ich nicht indizieren... bedenke wenn man das file mal umbenennt,
Und schon ist man bei der höchst individuellen Einstufung. Meine Archive (egal ob Bilder, Videos oder auch Musik) bleiben unangetastet.

Prüfe ich über die DB den Datenbestand auf Vorhandensein, dann läuft das so:
  • Dateiname/Dateigröße/Änderungsdatum/Verzeichnis sind identisch: Datei müsste die sein, welche indiziert wurde
  • Dateiname/Dateigröße/Änderungsdatum passen zu exakt einer bekannten Datei in der DB: DB aktualisieren, Datei ist vorhanden
  • sonst wird die Prüfsumme bechnet, danach in der DB gesucht und falls die dort gefundene Datei nicht auf der HDD liegt, die DB aktualisiert. Falls es die Datei oder die Prüfsumme in der DB mehrmals gibt muss ich endgültig von SHA256 auf andere Prüfsummen umsteigen.

Das ganze könnte man noch mit Dateigröße/Ändeurngsdatum/Verzeichnis (also ohne Dateiname) erweitern um sich die Berechnung der Prüfsumme dabei zu sparen. Aber eine irgendwo auf der HDD gefundene Datei, die nur beim Änderungsdatum und der Dateigröße mit der gesuchten überein stimmt, sagt bei meinen Datenbeständen nichts über den Inhalt aus (auch nicht bei gleicher Dateierweiterung).

Meine Kameras speichern oft nur sekundengenaue Zeitstempel, was bei 8 Bilder/Sekunde zwar 8 Dateinamen aber einen identischen Zeitstempel ergibt (und mit Pech auch identische Dateigrößen).

Zusätzlich gibt es bei mir Prüfläufe, bei denen ich alle Prüfsummen auf der HDD berechne und mit den Prüfsummen in der DB vergleiche. Das dauert bei 2-4 TB (u.U. auch noch auf der USB2-HDD) dann halt ein paar Stunden.

Kokujou schrieb:
Aber genau dieses bitRot macht mir ein wenig Sorgen.
Dann macht es erst recht Sinn, die Daten nicht nur an einer Stelle prüfen zu können, sonden, z.B. in der selben Ablagestruktur auf unterschiedlichen Medien (interne SSD und ext. HDD).

Bei mir stimmen die Verzeichnisstrukturen lokal, auf dem NAS und im Backup überein, nur das Basisverzeihcnis unterscheidet sich.

Kokujou schrieb:
Ich hab eingie Serien die beschädigt sind. vielleicht bei der Übertragung verreckt, vielleicht auf älteren platten "Angerottet" deswegen würde ich beim Vergleich der File-Size tatsächlich eine Spanne hinzufügen.
Die Dateigröße ist dafür kein Kriterium, damit prüfst Du nur, ob die FAT/MFT noch ok ist. Wenn sich beim Kopieren die Dateigröße (und nicht der Inhalt) ändert, dann sollte das jede Kopiersoftware melden, weil sie Blöcke/Sektoren nicht lesen kann.

Bitrot kann irgendeine Stelle der Datei betreffen. Beliebt und vielen "älteren Semestern" sicher noch bekannt sind selbstgebrannte CDs/DVDs. Dort ist die FAT oft noch ok, Dateien im Inneren des Mediums meist auch. Kommt man in den Außenbereich, ist die Datei oder einzelne Sektoren davon nicht mehr zu lesen (obwohl das Dateisystem etwas anderes behauptet).

Zur Prüfung von BitRot/Kopierfehlern/Verschlüsselung durch Trojaner hilft nur die vollständige Prüfung der Datei gegen die früher erzeugte Prüfsumme. Oder ein Dateisystem, das diese Prüfung auf Sektor/Blockebene selber erledigt.

Die einzige Stelle, an der ich eine Toleranz einführen musste, war beim Änderungsdatum. Samba/CIFS auf Linux-Basis (egal ob mit ETX4 oder BTRFS) ist dort zu ungenau und liefert damit schlechtere Zeitstempel wie NTFS. Da es aber nur ein paar ms/ns betrifft, ist dies für die Archivierung meist irrelevant.
 
Zuletzt bearbeitet:
also vielleicht zur Klarifikation ich hätte mir den Flow nämlich so vorgestellt:

Zuerst mal batch uploade ich sämtliche Dateien. Dabei werden für jede Datei Prüfsumme und Dateigröße Berechnet

Wenn jetzt jemand 2mal denselben Ordner hochladen will hole ich mir sämtliche bekannte hashes zusammen mit dem Dateipfad der in der DB liegt. So gucke ich ob Einträge schon existieren und überspringe sie beim batch-upload. Wenn sich hier aber bei gleichem Hash der Dateipfad geändert hat gucke ich a) ob der alte Dateiopfad noch valide ist. wenn ja werfe ich nen fehler sodass man das überprüfen muss. b) ob der neue Pfad valide ist. Wenn er valide ist und der alte nicht wird es überschrieben.

Bis hierhin müsste das schonmal die meisten Fälle auskontern denn wie ihr schon alle sagtet normalerweise hat man ja einen Ordner für sowas.

Nun aber zum Thema. Wenn jetzt mal festgestellt wurde dass files nicht abspielbar sind oder so dann könnte der nutzer - in dem fall ich - einen job triggern der die files überprüft. wenn nun ein invalides file gefunden wird muss das wieder eingelesen werden und wenn das aus irgendeinem grund vom basisordner nicht vom default ordner möglich ist läuft die tiefe Festplattensuche.

Und hier würde ich dann nicht den Dateinamen nehmen denn den verändere auch ich manchmal. besonders wenn ich zu Faul bin ihn beim ersten Mal richtig zu wählen. Beispiel: ich hab die datei konvertiert und beim Ausgabe-File wurde dann sowas wie _1 oder _Copy angefügt. Irgendwann komm ich drauf und - fwop. Name futsch. :)

also könnte ich dann die gesamte festplatte mit dem Vorgeschlagenen MFT Ansatz durchsuchen und erstmal ganz primitiv nach files filtern die eine Dateigröße besitzen die etwa mit meiner Datei übereinstimmt. Dann hole ich mir von diesen hoffentlich abzählbar wenigen noch den Hash und alles ist gut. Änderungsdatum - keine Ahnung. Ich weiß gar nicht wird das nicht sogar schon beim verschieben gesetzt? Oder war das das Erstelldatum? naja... in jedem fall, wenn man den dateinamen ändert müsste es spätestens geändert werden. ich glaube die einzigen infos die ich indizieren sollte sind die die eng mit dem dateiinhalt verknüpft sind.
Ergänzung ()

jetzt wo ich so drüber nachwende wäre der such-algorithmus vermutlich auch beim manuellen update besser... überlegen wir mal wir haben 800 files deren hash wir indizieren nur um 2-3 kaputte files zu ersetzen...
aber wie ich selbst ausprobiert hat hält sich das uach noch im Minuten-Bereich auf. trotzdem ist auch das ne Idee
 
Ich frag mich grade, warum du die Datein auf dem normalen Dateisystem liegen lässt wo jeder da dran rumpfuschen kann. Ich würde das ganze zb in S3 (beispielsweise mit https://min.io/ ) speichern, dann hast du die ganzen Probleme nicht mehr.
 
Zurück
Oben