系统是CentOS5 x86_64,完全最新。
我有一个无法列出的文件夹(ls 只是挂起,占用内存直到被杀死)。目录大小接近 500k:
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。我从 unlink 调用中得到了相同的 ENOENT 错误:
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)
我还 strace 了“touch”,抓取了它所执行的系统调用并复制了它们,然后尝试按名称取消链接生成的文件。这很有效,但在操作完成并且程序运行任意长时间后,文件夹仍包含同名条目(strace 输出在 5 分钟后达到 20GB,我停止了该过程)。
我对这个问题感到困惑,我真的不想让这台生产机器(数百名客户)离线来 fsck 文件系统,但我倾向于这是目前唯一的选择。如果有人使用其他方法成功删除文件(通过 inode 编号,我可以使用 getdents 代码获取这些文件),我很乐意听听他们的看法。
(是的,我尝试过了find . -inum <inode> -exec rm -fv {} \;
,但仍然存在 unlink 返回 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);
答案1
我假设您正在 ACTIVE 文件系统上执行此操作。因此,当您执行 find 等操作时,文件可能在 find 处理之前就被删除了。这没问题。
我可能要获取文件列表的做法是不要使用 ls。ls 会尝试进行排序,而对于那么大的目录,仅获取列表然后对其进行排序就需要很长时间。
在这种情况下,我以 root 用户身份执行的操作是:
find dirname -ls >outfile
如果你想根据时间删除某些内容:
find dirname -type f -mtime +60 -print0 | xargs -0 rm -f
顺便说一句,-0 和 -print0 是 Linux 中的选项,以便将带有“特殊”字符的文件名正确传递给 xargs。上面的命令当然会删除 60 天前修改过的文件。
答案2
这是一个确定文件系统是否需要修复的简单方法,或者至少了解损坏的严重程度......
下载(免费)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
您可以使用热复制来制作快照,/home
而无需挂载生成的文件系统...然后运行fsck
以查看是否存在任何问题。
hcp --skip-mount /dev/sdb1
fsck -a /dev/hcp1
这可以节省您重新启动的时间并帮助您在安排客户端停机之前评估严重性。
从长远来看,我只会使用 XFS 作为文件系统...但那是另一个话题...
答案3
不要使用“找到...-exec rm-fv {} \;“ 使用 ”查找...-删除“ 反而