Removendo uma pasta (aparentemente) infinitamente recursiva

46

De alguma forma, uma de nossas antigas caixas do Server 2008 (não o R2) desenvolveu uma pasta aparentemente infinitamente recorrente. Isso está atrapalhando nossos backups, pois o agente de backup tenta recuar para a pasta e nunca mais retorna.

A estrutura da pasta se parece com:

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

... e assim por diante. É como um daqueles sets de Mandelbrot com os quais brincávamos nos anos 90.

Eu tentei:

  • Excluindo-o do Explorer. Sim, sou otimista.
  • RMDIR C:\Storage\Folder1 /Q/S - isso retorna The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - ele percorre as pastas por alguns minutos antes de o robocopy.exe travar.

Alguém pode sugerir uma maneira de eliminar esta pasta definitivamente?

KenD
fonte
1
Eu tentaria /MIR: ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1também pode valer a pena dar uma chkdskrisadinha.
jscott
1
/MIRpareceu durar mais tempo, mas acabou sendo bombardeado ("a robocopia parou de funcionar"). Estou com um pouco de medo de fazer um chkdsk; este é um servidor muito velho e eu estou preocupado que este problema é indicativo de problemas maiores do sistema de arquivos ...
Kend
7
Tente inicializar a partir de um CD de avaliação da área de trabalho do Linux (Ubuntu / Centos / Fedora / ...) e remova a pasta de lá.
Guntram Blohm apoia Monica
2
@ KenD Se você suspeitar de problemas de corrupção do sistema de arquivos, certamente tente primeiro reparar o sistema de arquivos. Tentar truques de remoção de diretório pode piorar as coisas.
jscott
1
Como (a partir da sua resposta abaixo), o diretório não era infinito, apenas muito profundo, se você tivesse o CygWin ou o UnxUtils instalado, você poderia findfazer uma remoção profunda do primeiro diretório:find Storage/Folder1 -depth -exec rmdir {} \;
Johnny

Respostas:

45

Obrigado a todos pelos conselhos úteis.

Indo bem ao território do StackOverflow, resolvi o problema usando este trecho de código C #. Ele usa a biblioteca Delimon.Win32.IO que aborda especificamente problemas ao acessar caminhos de arquivos longos.

Caso isso possa ajudar alguém, aqui está o código - ele superou os ~ 1600 níveis de recursão com os quais eu estava de alguma forma preso e levou cerca de 20 minutos para removê-los.

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
fonte
2
Eu tentei, mesmo usando a versão Delimon de .Delete(em vez da System.IOversão normal ), e embora não tenha lançado uma exceção, não pareceu fazer nada. Certamente a recursão usando o método acima levou séculos e .Deletesó mastigou as coisas por 5 a 10 segundos. Talvez tenha quebrado alguns diretórios e depois tenha desistido?
Kend
4
Você já descobriu como isso aconteceu no começo? Parece uma falha de algum programa realmente mal escrito do usuário.
Parthian Shot
8
Recorrendo a uma função 1600 vezes? Stack overflow território de fato!
Aleksandr Dubinsky
2
Apenas como um aparte, as pastas foram preenchidas por alguma coisa? Se você pode determinar em intervalos quais as pastas recursiva foram criados e multiplicar isso pelo número de recursions, você vai (espero) obter um prazo aproximada de quando este problema começou ...
Get-HomeByFiveOClock
8
Uau, que bom que você acabou resolvendo isso. Para sua informação, a correção oficial suportada pela Microsoft para esse tipo de situação é "reformatar o volume". Sim, sério. : /
HopelessN00b
25

Pode ser um ponto de junção recursivo. Uma coisa dessas pode ser criada com junctionum utilitário de arquivo e disco da Sysinternals .

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

E agora você pode descer sem parar c: \ Hello \ Hello \ Hello .... (até MAX_PATH ser atingido, 260 caracteres para a maioria dos comandos, mas 32.767 caracteres para algumas funções da API do Windows).

Uma lista de diretórios mostra que é uma junção:

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:\>

Para excluir, use o utilitário de junção:

junction -d c:\Hello\Hello
Brian
fonte
4
Infelizmente, DIRapenas me mostra simples diretórios ole - nenhum sinal de cruzamentos Receio
Kend
2
Você pode fazer uma verificação rápida rápida junction -s C:\Storage\Folder1 ?
Brian
3
No reparse points found:( #
12779 KenD
3
Eu me pergunto o que criou uma confusão de subdiretórios reais.
Brian
2
Use dir /apara ver '<JUNCTION>' sem especificar o nome específico.
Chloe
16

Não é uma resposta, mas não tenho representante suficiente para comentar.

Certa vez, corrigi esse problema em um enorme disco de 500 MB FAT16 em um sistema MS-DOS. Eu usei a depuração do DOS para despejar e analisar manualmente a tabela de diretórios. Em seguida, virei um pouco para marcar o diretório recursivo como excluído. Minha cópia do Dettman e Wyatt 'DOS Programmers' Reference 'me mostrou o caminho.

Ainda estou despreocupadamente orgulhoso disso. Eu ficaria surpreso e aterrorizado se houver alguma ferramenta de uso geral que tenha tanto poder sobre os volumes FAT32 ou NTFS. A vida era mais simples naquela época.

Richard
fonte
8
Eu diria que você está justificadamente orgulhoso disso.
Mfni #
3
+1 tem algum representante em mim. Ótima solução.
Chris Thornton
3
Agora você pode excluir a primeira frase da sua resposta.
AL
Esta não é uma resposta. É uma história sobre um sistema operacional diferente e um sistema de arquivos diferente. Além de não ajudar em nada esse problema (NT, NTFS), ele nem ajudaria alguém com o mesmo problema (DOS, FAT16), porque na verdade não contém nenhum detalhe.
Andrew Medico
@ Andrew Medico: Eu concordo com você, daí a minha primeira frase. Mas eu digo a você onde encontrar as informações para resolver o problema ligeiramente relacionado.
Richard
8

Java também pode lidar com caminhos de arquivo longos. E pode fazê-lo muito mais rápido também. Esse código (que copiei da documentação da API Java) excluirá uma estrutura de diretórios profundos de 1600 níveis em cerca de 1 segundo (no Windows 7, Java 8.0) e sem risco de estouro de pilha, pois na verdade não usa recursão.

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"));
  }
}
Porco Aranha
fonte
3
Te odeio. Você me forçou a aprovar uma resposta que envolve o uso de Java e agora me sinto toda suja. Eu preciso de um banho.
HopelessN00b
Me desculpe. Espero que você se recupere eventualmente do seu trauma. Mas uma linguagem desenvolvida pela Microsoft (c #) é realmente muito melhor?
21415 SpiderPig
5
Eu sou basicamente um cara do Windows hoje em dia, então sim, sim, é. Também posso ser amargo e tendencioso em relação ao Java por ter de manter cinco versões diferentes e específicas do JRE em quase 1.000 clientes no ambiente do meu empregador, um dos quais remonta a 2009, por conta da porcaria ... er, malware ... suítes de software "corporativos" que usamos para aplicativos críticos para os negócios.
HopelessN00b
6

Você não precisa de nomes de caminho longos se chdirentrar no diretório e usar apenas caminhos relativos para rmdir.

Ou, se você tiver um shell POSIX instalado, ou portá-lo para o equivalente do 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

(Usar uma variável shell para rastrear onde você a renomeou para a condição de loop é a outra alternativa para desenrolar o loop como eu fiz lá.)

Isso evita a sobrecarga da CPU da solução da KenD, que força o sistema operacional a atravessar a árvore do topo para o nquinto nível toda vez que um novo nível é adicionado, verificando permissões etc. Por isso, possui sum(1, n) = n * (n-1) / 2 = O(n^2)complexidade de tempo. As soluções que separam um pedaço do início da cadeia devem ser O(n), a menos que o Windows precise percorrer uma árvore ao renomear seu diretório pai. (Linux / Unix não.) Soluções que chdirvão chdiraté a parte inferior da árvore e usam caminhos relativos a partir daí, removendo diretórios à medida que eles fazem backup, também devem ser O(n), assumindo que o sistema operacional não precise verificar todas as suas diretórios-pai a cada chamada do sistema, quando você faz coisas enquanto está em CD em algum lugar.

find Folder1 -depth -execdir rmdir {} +executará o rmdir enquanto estiver em CD no diretório mais profundo. Ou, na verdade, a -deleteopção do find funciona em diretórios e implica -depth. Então, find Folder1 -deletedeve fazer exatamente a mesma coisa, mas mais rápido. Sim, a localização do GNU no Linux desce, varrendo um diretório, colocando CD em subdiretórios com caminhos relativos e, em seguida, rmdircom um caminho relativo chdir(".."). Ele não verifica novamente os diretórios enquanto ascende, portanto consumiria O(n)RAM.

Isso foi realmente uma aproximação: stracemostra que realmente utiliza unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR), open("..", O_DIRECTORY|...)e fchdir(the fd from opening the directory), com um monte de fstatchamadas misturados também. Mas o efeito é o mesmo se a árvore de diretórios não for modificada enquanto a localização estiver em execução.

edit: Tentei fazer isso no GNU / Linux (Ubuntu 14.10, em uma CPU Core2Duo de primeira geração a 2,4 GHz, em um sistema de arquivos XFS em uma unidade 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 cria um diretório e quaisquer componentes de caminho ausentes).

Sim, realmente 0,05 segundos para 2k rmdir ops. O xfs é muito bom em agrupar operações de metadados juntos no diário, uma vez que eles corrigiam operações de metadados sendo lentas como há 10 anos.

No ext4, a criação levou 0m0.279s, a exclusão com localização ainda levou 0m0.074s.

Peter Cordes
fonte
ficou curioso e tentei no Linux. Acontece que as ferramentas GNU padrão são boas para caminhos longos, porque elas recuam pela árvore em vez de tentar fazer chamadas de sistema com caminhos longos gigantes. Mesmo mkdir é bom quando você passa um caminho de 38k na linha de comando!
Peter Cordes
0

Corri para o mesmo problema com uma confusão de pastas com mais de 5000 diretórios que algum aplicativo Java fazia e escrevi um programa que o ajudará a remover esta pasta. Todo o código fonte está neste link:

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

Ele removeu tudo depois de um tempo, mas conseguiu fazer o trabalho, espero que ajude as pessoas que (como eu) enfrentam o mesmo problema frustrante

Imanol Barba Sabariego
fonte
Não poste respostas somente para links. Você deve colocar as informações mais importantes do link na própria publicação e fornecer o link para referência.
Frederik Nielsen
Ah, desculpe, é um programa, e eu realmente não queria postar TODO o código-fonte aqui ... Acho bem claro que escrevi um programa e estou hospedando-o seguindo este link, e a motivação e tudo estão presentes a resposta, por isso parece bastante claro para mim que não é uma única ligação respostas, nontheless, eu vou especificar (mais claramente) que é um software destinado a ser executado para resolver este problema
Imanol Barba Sabariego
0

Eu também tive isso, em um sistema Windows 10 independente. C: \ Usuário \ Nome \ Repita \ Repita \ Repita \ Repita \ Repita \ Repita \ Repita aparentemente até o infinito.

Eu poderia navegar usando o Windows ou o Prompt de Comando até o 50º e não mais. Não consegui excluí-lo ou clicar nele, etc.

C é a minha linguagem e, por fim, escrevi um programa com um loop de chamadas do sistema, que se repetem até que falhem. Você pode fazer isso em qualquer idioma, mesmo em lotes DOS. Criei um diretório chamado tmp e movi Repeat \ Repeat para ele, excluí a pasta Repeat agora vazia e movi tmp \ Repeat novamente para a pasta atual. Repetidamente!

 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);
 }

O ChkSystem apenas executa uma chamada system () e verifica o valor de retorno, parando se falhar.

Importante, ele falhou várias vezes. Pensei que talvez meu programa não estivesse funcionando ou que fosse infinitamente longo, afinal. No entanto, eu já tive isso antes com chamadas do sistema, com coisas não sincronizando, então apenas executei o programa novamente e ele continuou de onde parou, portanto, não pense imediatamente que seu programa não está funcionando. Então, no total, depois de executá-lo cerca de 20 vezes, ele limpou todos eles. No total, havia cerca de 1280 pastas de profundidade originalmente. Não faço ideia do que causou isso. Louco.

DenM
fonte