Вопрос: Удаление (по-видимому) бесконечно рекурсивной папки


Так или иначе, один из наших старых серверов Server 2008 (не R2) разработал, казалось бы, бесконечно рекурсивную папку. Это играет с havock с нашими резервными копиями, так как агент резервного копирования пытается записаться в папку и никогда не возвращается.

Структура папок выглядит примерно так:

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

... и так далее. Это как один из тех Множества Мандельброта мы привыкли играть в 90-х годах.

Я пробовал:

  • Удаление его из проводника. Да, я оптимист.
  • RMDIR C:\Storage\Folder1 /Q/S - это возвращает The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - это происходит через папки за пару минут до сбоя robocopy.exe.

Может ли кто-нибудь предложить способ убить эту папку навсегда?


45
2018-02-12 11:12


Источник


Я бы попробовал /MIR вместо: ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1 также может стоить chkdsk просто для хихиканья. - jscott
/MIR казалось, продлился дольше, но в итоге тоже выскочил («robocopy перестала работать»). Я немного боюсь сделать chkdsk; это довольно старый сервер, и я беспокоюсь, что эта проблема свидетельствует о больших проблемах с файловой системой ... - KenD
Попробуйте загрузиться с пробного компакт-диска для Linux (Ubuntu / Centos / Fedora / ...) и удалить папку с этого места. - Guntram Blohm
@KenD Если вы подозреваете проблемы с повреждением файловой системы, вы должны сначала попробовать сначала восстановить файловую систему. Пытаться удалить трюки с каталогами могут ухудшить ситуацию. - jscott
Поскольку (из вашего ответа ниже), каталог не был бесконечным, очень глубоким, если бы вы CygWin или UnxUtils установленный, вы можете использовать find выполнить удаление первой папки глубины: find Storage/Folder1 -depth -exec rmdir {} \; - Johnny


Ответы:


Спасибо всем за полезный совет.

Износясь в область StackOverflow, я решил проблему, сбив этот фрагмент кода C #. Он использует Delimon.Win32.IO библиотеку, которая специально решает проблемы, связанные с длинными файловыми путями.

На всякий случай, это может помочь кому-то другому, вот код - он прошел через 1600 уровней рекурсии, которые я как-то застрял, и потребовалось около 20 минут, чтобы удалить их всех.

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

45
2018-02-12 18:44



Я пробовал, даже используя версию Delimon .Delete (вместо обычного System.IO версия), и хотя он не выдавал исключения, он ничего не делал. Конечно, рекурсия с использованием вышеприведенного метода занимает возраст, и .Delete только пережевывали вещи на 5-10 секунд. Может быть, он откололся от нескольких каталогов, а затем сдался? - KenD
Вы когда-нибудь выясняли, как это произошло? Похоже, что какая-то действительно плохо написанная ошибка программы пользовательской программы. - Parthian Shot
Рекурсия в функцию 1600 раз? Переполнение стека территория действительно! - Aleksandr Dubinsky
Так же, как в стороне, были ли папки, населенные чем-нибудь? Если вы можете определить, с каких интервалов были созданы рекурсивные папки и умножить их на количество рекурсий, вы, надеюсь, получите приблизительный таймфрейм, когда эта проблема началась ... - Get-HomeByFiveOClock
Вау, рад, что ты решил это решить. FYI, официальное исправление Microsoft для такого рода ситуаций - «переформатировать громкость». Да, серьезно. : / - HopelessN00b


Может быть точкой рекурсивного соединения. Такая вещь может быть создана с помощью junction файловая и дисковая утилита из Sysinternals,

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

И теперь вы можете бесконечно спуститься c: \ Hello \ Hello \ Hello .... (ну до достижения MAX_PATH, для большинства команд используется 260 символов, но 32 767 символов для некоторых функций Windows API).

Список каталогов показывает, что это соединение:

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

Для удаления используйте утилиту соединения:

junction -d c:\Hello\Hello

25
2018-02-12 13:28



К сожалению, DIR просто показывает мне простые справочники ole - никаких признаков каких-либо контактов, которые я боюсь - KenD
Можете ли вы выполнить быструю двойную проверку с помощью junction -s C:\Storage\Folder1  ? - Brian
No reparse points found :( - KenD
Интересно, что создавало такой беспорядок фактических подкаталогов. - Brian
использование dir /a для просмотра '<JUNCTION>' без указания конкретного имени. - Chloe


Не ответ, но у меня недостаточно комментариев для комментария.

Я однажды исправил эту проблему на огромном 500 МБ диске FAT16 в системе MS-DOS. Я использовал отладку DOS для ручной сбрасывания и анализа из таблицы каталогов. Затем я перевернул один бит, чтобы пометить рекурсивный каталог как удаленный. Моя копия «Справочника программистов DOS» Детта и Уайта показала мне дорогу.

Я все еще горжусь этим. Я был бы удивлен и испуган, если бы существовал какой-либо универсальный инструмент, обладающий такой мощью по томам FAT32 или NTFS. Тогда жизнь была проще.


16
2018-02-13 14:06



Я бы сказал, что вы оправданно гордимся этим. - mfinni
+1 У меня есть репутация. Хорошее решение. - Chris Thornton
Теперь вы можете удалить первое предложение своего ответа. - A.L
Это не ответ. Это история о другой операционной системе и другой файловой системе. Помимо того, что эта проблема (NT, NTFS) не помогает, она даже не поможет кому-то с той же проблемой (DOS, FAT16), потому что на самом деле она не содержит никаких подробностей. - Andrew Medico
@ Энди Медико: Я согласен с тобой, поэтому мое первое предложение. Но я расскажу вам, где найти информацию, чтобы решить проблему, связанную с небольшими проблемами. - Richard


Java также может работать с длинными файловыми путями. И это может сделать это намного быстрее. Этот код (который я скопировал из документации Java API) удалит структуру каталогов на уровне уровня 1600 примерно за 1 секунду (под Windows 7, Java 8.0) и без риска переполнения стека, поскольку на самом деле не используется рекурсия.

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

8
2018-02-14 17:35



Я ненавижу тебя. Вы заставили меня ответить на вопрос, связанный с использованием Java, и теперь я чувствую себя грязным. Мне нужен душ. - HopelessN00b
Мои извинения. Надеюсь, вы в конце концов выздоравлите от своей травмы. Но язык, разработанный Microsoft (c #), действительно намного лучше? - SpiderPig
В наши дни я в первую очередь парень с Windows, так что да, да. Я также могу быть горьким и предвзятым по отношению к Java из-за необходимости поддерживать 5 конкретных, разных версий JRE почти у 1000 клиентов в среде моего работодателя, одна из которых датируется 2009 годом, из-за дерьма ... er, вредоносного ПО ... uh, «enterprise-y», которые мы используем для критически важных для бизнеса приложений. - HopelessN00b


Вам не нужны длинные пути, если вы chdir в каталог и просто используйте относительные пути к rmdir,

Или, если у вас установлена ​​оболочка POSIX или переносится на эквивалент 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

(Использование переменной оболочки для отслеживания, где вы переименовали ее для условия цикла, является другой альтернативой для разворачивания цикла, как я там делал).

Это позволяет избежать накладных расходов процессора на решение KenD, что вынуждает ОС пересекать дерево сверху вниз nго уровня каждый раз, когда добавляется новый уровень, проверка разрешений и т. д. sum(1, n) = n * (n-1) / 2 = O(n^2)временная сложность. Решения, которые устраняют кусок от начала цепи, должны быть O(n), если Windows не должна пересекать дерево при переименовании родительского каталога. (Linux / Unix не делает.) Решения, которые chdir вплоть до дна дерева и использовать оттуда относительные пути, удаляя каталоги, поскольку они chdir резервное копирование, также должно быть O(n), предполагая, что ОС не обязательно будет проверять все ваши родительские каталоги каждый системный вызов, когда вы делаете что-то в то время как CDed где-то.

find Folder1 -depth -execdir rmdir {} + будет запускать rmdir, а CDed - в самый глубокий каталог. Или, на самом деле, найти -delete опция работает в каталогах и подразумевает -depth, Так find Folder1 -delete должен делать то же самое, но быстрее. Да, GNU найти на Linux сходит, сканируя каталог, CDing в подкаталоги с относительными путями, затем rmdir с относительным путем, то chdir(".."), Он не сканирует каталоги при восхождении, поэтому он будет потреблять O(n) ОЗУ.

Это было действительно приближение: strace показывает, что НАСТОЯТЕЛЬНО использует unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR), open("..", O_DIRECTORY|...), а также fchdir(the fd from opening the directory), с кучей fstat звонки тоже смешиваются. Но эффект тот же, если дерево каталогов не изменяется, пока поиск выполняется.

edit: Просто для ударов я попробовал это на GNU / Linux (Ubuntu 14.10, на процессоре Core2Duo первого поколения с частотой 2,4 ГГц, в файловой системе XFS на WD 2.5TB Green Power drive (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 создает каталог и любые отсутствующие компоненты пути).

Да, действительно 0,05 секунды для 2k rmdir ops. xfs неплохо разбирает операции метаданных вместе в журнале, так как они фиксировали операции метаданных как медленные, как 10 лет назад.

На ext4, create занял 0m0.279s, удалить с помощью find все равно взял 0m0.074s.


6
2018-02-15 05:40



стало любопытно и попробовал это на Linux. Оказывается, стандартные инструменты GNU - все в порядке с длинными путями, потому что они рекурсируют вниз по дереву, вместо того, чтобы пытаться делать системные вызовы с гигантскими длинными путями. Даже mkdir в порядке, когда вы передаете ему путь 38k в командной строке! - Peter Cordes


Я столкнулся с той же проблемой с более чем 500-страничным файловым хранилищем в папке, которое использовало какое-то приложение Java, и я написал программу, которая поможет вам удалить эту папку. Весь исходный код находится в этой ссылке:

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

Он удалил все это через некоторое время, но ему удалось выполнить эту работу, я надеюсь, что это помогает людям, которые (как я), сталкиваются с тем же расстраивающим вопросом


0
2017-08-08 18:35



Пожалуйста, не отправляйте ссылки только на ответы. Вы должны поместить самую важную информацию из ссылки в самом сообщении и предоставить ссылку для справки. - Frederik Nielsen
Извините, это программа, и я действительно не хочу публиковать ВСЕ исходный код здесь ... Я думаю, что это довольно ясный что я написал программу, и я размещаю ее по этой ссылке, а мотивация и все в ответе, поэтому мне кажется довольно понятным, что это не ответ на связь, но, тем не менее, я укажу (более четко) что это программное обеспечение, предназначенное для решения этой проблемы - Imanol Barba Sabariego


У меня тоже было это на автономной системе Windows 10. C: \ User \ Name \ Repeat \ Repeat \ Repeat \ Repeat \ Repeat \ Repeat \ Repeat, казалось бы, бесконечности.

Я мог бы перемещаться с помощью Windows или командной строки примерно до 50-го, а не дальше. Я не мог удалить его или щелкнуть по нему и т. Д.

C - мой язык, поэтому в конце концов я написал программу с циклом системных вызовов, которые повторяются до тех пор, пока они не сработают. Вы могли бы сделать это на любом языке, хотя и в партии DOS. Я создал каталог под названием tmp и переместил Repeat \ Repeat в это, удалил теперь пустую папку Repeat и переместил tmp \ Repeat обратно в текущую папку. Снова и снова!

 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 запускает вызов system () и проверяет возвращаемое значение, останавливаясь, если оно не выполнено.

Важно отметить, что он не удался несколько раз. Я думал, что, возможно, моя программа не работает, или что это бесконечно долго. Тем не менее, у меня было это раньше с системными вызовами, с вещами, которые не синхронизируются, поэтому я снова запускал программу и продолжал ее с того места, где она была остановлена, поэтому не сразу думайте, что ваша программа не работает. Таким образом, в общей сложности, после запуска около 20 раз, он очистил их все. Всего изначально было около 1280 папок. Не знаю, что вызвало это. Псих.


0
2018-03-31 16:24