Невозможно перечислить содержимое / удалить каталог (linux ext3)

Система CentOS5 x86_64, полностью обновленная.

У меня есть папка, которой нет в списке (ls просто зависает, ест память до тех пор, пока она не будет уничтожена). Размер каталога составляет почти 500 КБ:

root@server [/home/user/public_html/domain.com/wp-content/uploads/2010/03]# stat .
  File: `.'
  Size: 458752          Blocks: 904        IO Block: 4096   directory
Device: 812h/2066d      Inode: 44499071    Links: 2
Access: (0755/drwxr-xr-x)  Uid: ( 3292/ user)   Gid: ( 3287/ user)
Access: 2012-06-29 17:31:47.000000000 -0400
Modify: 2012-10-23 14:41:58.000000000 -0400
Change: 2012-10-23 14:41:58.000000000 -0400

Я могу видеть имена файлов, если я использую ls -1f, но он просто повторяет те же 48 файлов до бесконечности, каждый из которых имеет не-ascii символы где-то в имени файла:

La-critic\363-al-servicio-la-privacidad-300x160.jpg

Когда я пытаюсь получить доступ к файлам (скажем, скопировать или удалить их), я получаю сообщения, подобные следующим:

lstat("/home/user/public_html/domain.com/wp-content/uploads/2010/03/Sebast\355an-Pi\361era-el-balc\363n-150x120.jpg", 0x7fff364c52c0) = -1 ENOENT (No such file or directory)

Я попытался изменить код, найденный на этой странице руководства, и изменил код, чтобы вызывать unlink для каждого файла. Я получаю ту же ошибку ENOENT от вызова unlink:

unlink("/home/user/public_html/domain.com/wp-content/uploads/2010/03/Marca-naci\363n-Madrid-150x120.jpg") = -1 ENOENT (No such file or directory)

Я также установил "прикосновение", взял системные вызовы, которые он создает, и реплицировал их, затем попытался отсоединить полученный файл по имени. Это работает нормально, но папка по-прежнему содержит запись с тем же именем после завершения операции, и программа работает в течение произвольно долгого времени (через 5 минут вывод данных strace составил 20 ГБ, и я остановил процесс).

Я нахожусь в тупике, я действительно предпочел бы не переводить эту производственную машину (сотни клиентов) в автономный режим для fsck файловой системы, но я склоняюсь к тому, чтобы это был единственный вариант на данный момент. Если бы кто-нибудь успешно использовал другие методы удаления файлов (по номеру инода, я могу получить их с кодом getdents), я бы с удовольствием их услышал.

(Да, я пробовал find . -inum <inode> -exec rm -fv {} \; и у него все еще есть проблема с отменой ссылки, возвращающей ENOENT)

Для тех, кто заинтересован, вот разница между кодом этой страницы руководства и моим. Я не беспокоился о проверке ошибок на malloc и т. Д., Потому что я ленивый, и это единовременно:

root@server [~]# diff -u listdir-orig.c listdir.c 
--- listdir-orig.c      2012-10-23 15:10:02.000000000 -0400
+++ listdir.c   2012-10-23 14:59:47.000000000 -0400
@@ -6,6 +6,7 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
+#include <string.h>

 #define handle_error(msg) \
        do { perror(msg); exit(EXIT_FAILURE); } while (0)
@@ -17,7 +18,7 @@
    char           d_name[];
 };

-#define BUF_SIZE 1024
+#define BUF_SIZE 1024*1024*5

 int main(int argc, char *argv[])
 {
@@ -26,11 +27,16 @@
    struct linux_dirent *d;
    int bpos;
    char d_type;
+   int deleted;
+   int file_descriptor;

    fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
    if (fd == -1)
        handle_error("open");

+   char* full_path;
+   char* fd_path;
+
    for ( ; ; ) {
        nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
        if (nread == -1)
@@ -55,7 +61,24 @@
           printf("%4d %10lld  %s\n", d->d_reclen,
                   (long long) d->d_off, (char *) d->d_name);
           bpos += d->d_reclen;
+          if ( d_type == DT_REG )
+          {
+              full_path = malloc(strlen((char *) d->d_name) + strlen(argv[1]) + 2); //One for the /, one for the \0
+              strcpy(full_path, argv[1]);
+              strcat(full_path, (char *) d->d_name);
+
+              //We're going to try to "touch" the file.
+              //file_descriptor = open(full_path, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666);
+              //fd_path = malloc(32); //Lazy, only really needs 16
+              //sprintf(fd_path, "/proc/self/fd/%d", file_descriptor);
+              //utimes(fd_path, NULL);
+              //close(file_descriptor);
+              deleted = unlink(full_path);
+               if ( deleted == -1 ) printf("Error unlinking file\n");
+              break; //Break on first try
+          }
        }
+       break; //Break on first try
    }

    exit(EXIT_SUCCESS);

3 ответа

Вот простой способ определить, нуждается ли файловая система в восстановлении или, по крайней мере, понять, насколько велик ущерб...

Загрузите (бесплатную) утилиту для создания снимков R1Soft/Idera Hot Copy. Это модуль RPM и ядра, который предоставляет моментальные снимки файловой системы Linux при копировании при записи без необходимости использования LVM и т. Д.

Допустим, ваши файловые системы выглядят так:

Filesystem            Size  Used Avail Use% Mounted on
/dev/sda2              12G  4.4G  7.0G  39% /
tmpfs                  14G     0   14G   0% /dev/shm
/dev/sda1             291M  166M  110M  61% /boot
/dev/sda3             9.9G  2.5G  7.0G  26% /usr
/dev/sdb1             400G  265G  135G  67% /home

Вы можете использовать Hot Copy для снимка /home без монтирования получившейся файловой системы... Затем запустите fsck чтобы увидеть, есть ли какие-либо проблемы.

hcp --skip-mount /dev/sdb1
fsck -a /dev/hcp1

Это может сэкономить вам время перезагрузки и помочь оценить серьезность перед планированием времени простоя клиента.

В долгосрочной перспективе я бы просто использовал XFS в качестве файловой системы... Но это уже другая тема...

Я предполагаю, что вы делаете это в файловой системе ACTIVE. Таким образом, возможно, что пока вы выполняете поиск и тому подобное, файл был удален, прежде чем он мог быть обработан поиском. Это было бы хорошо.

То, что я мог бы сделать, чтобы получить список файлов, это НЕ использовать ls. ls пытается выполнить сортировку, и с каталогом такого размера потребуется довольно много времени, чтобы получить список и затем отсортировать его.

Что я делаю в таких случаях (как пользователь root):

 find dirname -ls >outfile

Если вы хотите удалить что-либо в зависимости от времени:

 find dirname -type f -mtime +60 -print0 | xargs -0 rm -f

Кстати, -0 и -print0 являются опциями в Linux, так что имена файлов со "специальными" символами правильно передаются в xargs. Выше, конечно, удаляет файлы, которые были изменены БОЛЬШЕ, чем 60 дней до этого.

Не используйте "find... -exec rm -fv {} \;" вместо этого используйте "find... -delete"

Другие вопросы по тегам