Supprimer un dossier (apparemment) infiniment récursif

46

D'une manière ou d'une autre, l'une de nos anciennes boîtes Server 2008 (et non R2) a développé un dossier apparemment infiniment récursif. Cela nuit à nos sauvegardes, car l’agent de sauvegarde tente de rentrer dans le dossier et ne revient jamais.

La structure du dossier ressemble à ceci:

C:\Storage\Folder1
C:\Storage\Folder1\Folder1
C:\Storage\Folder1\Folder1\Folder1
C:\Storage\Folder1\Folder1\Folder1\Folder1

... etc. C'est comme un de ces ensembles de Mandelbrot avec lesquels nous jouions tous dans les années 90.

J'ai essayé:

  • Suppression de l'explorateur. Oui, je suis optimiste.
  • RMDIR C:\Storage\Folder1 /Q/S - cela retourne The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - Cela passe dans les dossiers pendant quelques minutes avant que robocopy.exe ne se bloque.

Quelqu'un peut-il suggérer un moyen de tuer ce dossier pour de bon?

KenD
la source
1
J'essaierais /MIRplutôt: cela ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1peut aussi valoir la peine de courir un chkdskpeu pour rire.
Jscott
1
/MIRsemblait durer plus longtemps, mais a finalement été bombardé aussi ("robocopy a cessé de fonctionner"). J'ai un peu peur de faire ça chkdsk; c'est un assez vieux serveur et je crains que ce problème ne soit révélateur de problèmes de système de fichiers plus importants ...
KenD
7
Essayez de démarrer à partir d’un CD d’essai Linux (Ubuntu / Centos / Fedora / ...) et d’enlever le dossier.
Guntram Blohm soutient Monica le
2
@ KenD Si vous suspectez des problèmes de corruption du système de fichiers, vous devez certainement essayer de réparer le système de fichiers en premier. Essayer des astuces de suppression de répertoire peut aggraver les choses.
Jscott
1
Depuis (d'après votre réponse ci-dessous), le répertoire n'était pas infini, mais très profond. Si vous aviez installé CygWin ou UnxUtils , vous pourriez utiliser findpour supprimer le premier répertoire en profondeur:find Storage/Folder1 -depth -exec rmdir {} \;
Johnny

Réponses:

45

Merci à tous pour les conseils utiles.

Bien loin du territoire de StackOverflow, j'ai résolu le problème en mélangeant cet extrait de code C #. Il utilise la bibliothèque Delimon.Win32.IO qui traite spécifiquement des problèmes d’accès aux chemins de fichiers longs.

Au cas où cela pourrait aider quelqu'un d'autre, voici le code - il a traversé les ~ 1600 niveaux de récursion avec lesquels j'avais été bloqué et a pris environ 20 minutes pour les supprimer tous.

using System;
using Delimon.Win32.IO;

namespace ConsoleApplication1
{
    class Program
    {
        private static int level;
        static void Main(string[] args)
        {
            // Call the method to delete the directory structure
            RecursiveDelete(new DirectoryInfo(@"\\server\\c$\\storage\\folder1"));
        }

        // This deletes a particular folder, and recurses back to itself if it finds any subfolders
        public static void RecursiveDelete(DirectoryInfo Dir)
        {
            level++;
            Console.WriteLine("Now at level " +level);
            if (!Dir.Exists)
                return;

            // In any subdirectory ...
            foreach (var dir in Dir.GetDirectories())
            {
                // Call this method again, starting at the subdirectory
                RecursiveDelete(dir);
            }

            // Finally, delete the directory, and any files below it
            Dir.Delete(true);
            Console.WriteLine("Deleting directory at level " + level);
            level--;
        }
    }
}
KenD
la source
2
J'ai essayé, même en utilisant la version de Delimon de .Delete(au lieu de la System.IOversion normale ), et bien qu'il ne lève aucune exception, il ne semble rien faire. Certes, la récursivité utilisant la méthode ci-dessus a pris un temps fou et .Deleten'a mordu que 5 à 10 secondes. Peut-être a-t-il grignoté quelques annuaires puis abandonné?
KenD
4
Avez-vous déjà compris comment cela s'était passé? Cela ressemble à une faute de programme utilisateur mal écrit.
Parthian Shot
8
Récursant dans une fonction 1600 fois? Territoire de débordement pile en effet!
Aleksandr Dubinsky
2
En passant, les dossiers étaient-ils remplis? Si vous pouvez déterminer à quels intervalles les dossiers récursifs ont été créés et multiplier ce nombre par le nombre de récursions, vous obtiendrez (espérons-le) un moment approximatif du début de ce problème ...
Get-HomeByFiveOClock Le
8
Wow, content que vous ayez fini par résoudre ce problème. Pour votre information, le correctif officiel pris en charge par Microsoft pour ce type de situation est le suivant: "reformater le volume". Oui, sérieusement. : /
HopelessN00b
25

Pourrait être un point de jonction récursif. Une telle chose peut être créée avec junctionun utilitaire de fichiers et de disques de Sysinternals .

mkdir c:\Hello
junction c:\Hello\Hello c:\Hello

Et vous pouvez maintenant descendre sans fin c: \ Hello \ Hello \ Hello .... (jusqu'à atteindre MAX_PATH, 260 caractères pour la plupart des commandes mais 32 767 caractères pour certaines fonctions de l'API Windows).

Une liste de répertoires indique qu'il s'agit d'une jonction:

C:\>dir c:\hello
 Volume in drive C is DR1
 Volume Serial Number is 993E-B99C

 Directory of c:\hello

12/02/2015  08:18 AM    <DIR>          .
12/02/2015  08:18 AM    <DIR>          ..
12/02/2015  08:18 AM    <JUNCTION>     hello [\??\c:\hello]
               0 File(s)              0 bytes
               3 Dir(s)  461,591,506,944 bytes free

C:\>

Pour supprimer, utilisez l'utilitaire de jonction:

junction -d c:\Hello\Hello
Brian
la source
4
Malheureusement, un des DIRrépertoires me montre tout simplement les répertoires - rien ne
laisse présager de
2
Pouvez-vous faire une double vérification rapide avec junction -s C:\Storage\Folder1 ?
Brian
3
No reparse points found:(
KenD
3
Je me demande ce qui a créé un tel désordre de sous-répertoires réels.
Brian
2
Utilisez dir /apour voir '<JONCTION>' sans spécifier le nom spécifique.
Chloé
16

Pas une réponse, mais je n'ai pas assez de représentant pour un commentaire.

Une fois, j'ai résolu ce problème sur un disque alors énorme de 500 Mo FAT16 sur un système MS-DOS. J'ai utilisé le débogage DOS pour vider et analyser manuellement la table des répertoires. J'ai ensuite retourné un bit pour marquer le répertoire récursif comme supprimé. Mon exemplaire de «Référence des programmeurs DOS» de Dettman et Wyatt m'a montré la voie.

Je suis toujours excessivement fier de cela. Je serais étonné et terrifié s’il existe un outil polyvalent aussi puissant sur les volumes FAT32 ou NTFS. La vie était plus simple à l'époque.

Richard
la source
8
Je dirais que vous êtes à juste titre fier de cela.
Mfinni
3
+1 avoir un représentant sur moi. Belle solution.
Chris Thornton
3
Vous pouvez maintenant supprimer la première phrase de votre réponse.
AL
Ce n'est pas une réponse. C'est l'histoire d'un système d'exploitation différent et d'un système de fichiers différent. En plus de ne pas aider à résoudre ce problème (NT, NTFS), il n’aiderait même pas les personnes ayant le même problème (DOS, FAT16) car il ne contient en réalité aucun détail.
Andrew Medico
@ Andrew Medico: Je suis d'accord avec vous, d'où ma première phrase. Mais je vous dis où trouver l'information pour résoudre le problème légèrement lié.
Richard
8

Java peut également traiter de longs chemins de fichiers. Et cela peut aussi être beaucoup plus rapide. Ce code (que j'ai copié de la documentation de l'API Java) supprime une structure de répertoires profonds de 1 600 niveaux en environ 1 seconde (sous Windows 7, Java 8.0) et ne présente aucun risque de débordement de pile car il n'utilise pas de récursion.

import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;

public class DeleteDir {

  static void deleteDirRecur(Path dir) throws IOException {
    Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
         @Override
         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
             throws IOException
         {
             Files.delete(file);
             return FileVisitResult.CONTINUE;
         }
         @Override
         public FileVisitResult postVisitDirectory(Path dir, IOException e)
             throws IOException
         {
             if (e == null) {
                 Files.delete(dir);
                 return FileVisitResult.CONTINUE;
             } else {
                 throw e;
             }
         }
     });
  }

  public static void main(String[] args) throws IOException {
    deleteDirRecur(Paths.get("C:/Storage/Folder1"));
  }
}
SpiderPig
la source
3
Je te deteste. Vous m'obligez à relancer une réponse qui implique l'utilisation de Java, et maintenant je me sens tout sale. J'ai besoin d'une douche.
HopelessN00b
Mes excuses. J'espère que vous guérirez éventuellement de votre traumatisme. Mais un langage développé par Microsoft (c #) est-il vraiment bien meilleur?
SpiderPig
5
Je suis avant tout un gars sous Windows ces jours-ci, alors oui, oui. Je suis peut-être aussi amer et partial envers Java en raison de la nécessité de maintenir 5 versions différentes et différentes de JRE sur près de 1 000 clients dans l'environnement de mon employeur, dont l'une remonte à 2009, en raison de la présence de logiciels malveillants. ... euh, les progiciels "d'entreprise" que nous utilisons pour les applications critiques.
HopelessN00b
6

Vous n'avez pas besoin de longs chemins d'accès si vous entrez chdirdans le répertoire et utilisez simplement des chemins d'accès relatifs à rmdir.

Ou, si vous avez un shell POSIX installé, ou portez-le à l’équivalent DOS:

# untested code, didn't bother actually testing since the OP already solved the problem.

while [ -d Folder1 ]; do
    mv Folder1/Folder1/Folder1/Folder1  tmp # repeat more times to work in larger batches
    rm -r Folder1     # remove the first several levels remaining after moving the main tree out
    # then repeat to end up with the remaining big tree under the original name
    mv tmp/Folder1/Folder1/.../Folder1 Folder1 
    rm -r tmp
done

(Utiliser une variable shell pour savoir où vous l'avez renommé pour la condition de boucle constitue l'autre alternative pour dérouler la boucle comme je l'ai fait ici.)

Cela évite la surcharge de temps processeur de la solution de KenD, qui oblige le système d'exploitation à parcourir l'arborescence du haut au ntroisième niveau à chaque fois qu'un nouveau niveau est ajouté, en vérifiant les autorisations, etc. Il a donc une sum(1, n) = n * (n-1) / 2 = O(n^2)complexité temporelle. Les solutions qui réduisent une partie du début de la chaîne doivent l'être O(n), sauf si Windows doit traverser une arborescence lors du changement de nom de son répertoire parent. (Linux / Unix non.) Des solutions chdirallant jusqu'au bas de l'arborescence et utilisant des chemins relatifs à partir de là, supprimant les répertoires lors de la chdirsauvegarde, devraient également l'être O(n), à condition que le système d'exploitation n'ait pas besoin de vérifier tous vos fichiers. répertoires parents chaque appel système, lorsque vous faites quelque chose sur CD.

find Folder1 -depth -execdir rmdir {} +exécutera rmdir tant qu’il sera CDé dans le répertoire le plus profond. Ou en fait, l' -deleteoption de find fonctionne sur les répertoires et implique -depth. Alors find Folder1 -deletedevrait faire exactement la même chose, mais plus rapidement. Ouais, GNU find sur Linux descend en parcourant un répertoire, CD-ROM dans des sous-répertoires avec des chemins relatifs, puis rmdiravec un chemin relatif, puis chdir(".."). Il ne réanalyse pas les répertoires lors d'une ascension, il consomme donc de la O(n)RAM.

C'était vraiment une approximation: stracemontre qu'il utilise EFFECTIVEMENT unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR), open("..", O_DIRECTORY|...)et fchdir(the fd from opening the directory), avec un tas d' fstatappels mélangés aussi. Mais l'effet est le même si l'arborescence de répertoires n'est pas modifiée pendant que find est en cours d'exécution.

edit: Juste pour le plaisir, j’ai essayé ceci sous GNU / Linux (Ubuntu 14.10, sur un processeur Core2Duo de première génération à 2,4 GHz, sur un système de fichiers XFS sur un lecteur WD 2.5TB Green Power (WD25EZRS)).

time mkdir -p $(perl -e 'print "annoyingfoldername/" x 2000, "\n"')

real    0m1.141s
user    0m0.005s
sys     0m0.052s

find annoyingfoldername/ | wc
   2000    2000 38019001  # 2k lines / 2k words / 38M characters of text


ll -R annoyingfoldername
... eventually
ls: cannot access ./annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername: File name too long
total 0
?????????? ? ? ? ?            ? annoyingfoldername

time find annoyingfoldername -delete

real    0m0.054s
user    0m0.004s
sys     0m0.049s

# about the same for normal rm -r,
# which also didn't fail due to long path names

(mkdir -p crée un répertoire et tous les composants de chemin manquants).

Oui, vraiment 0,05 seconde pour 2k opérations rmdir. xfs est assez bon pour regrouper les opérations de métadonnées dans le journal, car elles ont corrigé les opérations de métadonnées comme étant lentes il y a 10 ans.

Sur ext4, create prenait 0m0.279s, supprimer avec find prenait toujours 0m0.074s.

Peter Cordes
la source
est devenu curieux et a essayé sur Linux. Il se trouve que les outils GNU standard conviennent tous aux longs chemins, car ils récriment l’arbre au lieu d’essayer de faire des appels système avec des longs chemins gigantesques. Même mkdir va bien quand vous lui passez un chemin de 38k sur la ligne de commande!
Peter Cordes
0

J'ai rencontré le même problème que certaines applications Java avec un désordre de dossiers de plus de 5000 répertoires et j'ai écrit un programme qui vous aidera à supprimer ce dossier. Le code source complet se trouve dans ce lien:

https://imanolbarba.net/gitlab/imanol/DiREKT

Il a tout supprimé après un moment, mais il a réussi à faire le travail. J'espère que cela aidera les gens qui (comme moi) rencontrent le même problème frustrant.

Imanol Barba Sabariego
la source
S'il vous plaît, ne postez pas de réponses avec lien uniquement. Vous devriez mettre les informations les plus importantes du lien dans le message lui-même et fournir le lien pour référence.
Frederik Nielsen
Oh désolé, c'est un programme, et je ne voulais vraiment pas poster TOUT le code source ici ... Je pense qu'il est assez clair que j'ai écrit un programme et que je l'héberge en suivant ce lien, et la motivation et tout est en place la réponse, donc il me semble assez clair que ce n'est pas une réponse uniquement liée au lien, néanmoins, je préciserai (plus clairement) qu'il s'agit d'un logiciel destiné à être exécuté pour résoudre ce problème
Imanol Barba Sabariego
0

Moi aussi, je l’avais sur un système autonome Windows 10. C: \ Utilisateur \ Nom \ Répéter \ Répéter \ Répéter \ Répéter \ Répéter \ Répéter \ Répéter \ Répéter \ Répéter apparemment à l'infini.

Je pouvais naviguer à l’aide de Windows ou d’Invite de commandes jusqu’à la cinquantième, sans plus. Je ne pouvais pas le supprimer, ni cliquer dessus, etc.

C étant ma langue, j’ai finalement écrit un programme avec une boucle d’appels système, qui se répètent jusqu’à leur échec. Vous pouvez le faire dans n’importe quelle langue, même en batch DOS. J'ai créé un répertoire appelé tmp et déplacé Repeat \ Repeat dans celui-ci, supprimé le dossier Repeat maintenant vide et déplacé tmp \ Repeat dans le dossier actuel. Encore et encore!

 while (times<2000)
 {
  ChkSystem("move Repeat\\Repeat tmp");
  ChkSystem("rd Repeat");
  ChkSystem("move tmp\\Repeat Repeat");
  ++times;
  printf("Removed %d nested so far.\n", times);
 }

ChkSystem lance simplement un appel system () et vérifie la valeur de retour, en s'arrêtant en cas d'échec.

Fait important, il a échoué à plusieurs reprises. Je pensais que mon programme ne fonctionnait peut-être pas ou qu'il était infiniment long après tout. Cependant, j'avais déjà eu cela auparavant avec des appels système, avec des choses qui ne se synchronisaient pas, alors j'ai juste relancé le programme et il a continué là où il s'était arrêté, alors ne pensez pas tout de suite que votre programme ne fonctionne pas. Donc au total, après l'avoir exécuté environ 20 fois, il les a tous effacés. Au total, il s'agissait à l'origine d'environ 1280 dossiers. Aucune idée de ce qui l'a causé. Fou.

DenM
la source