df空间满,du找不到文件的问题
最近看了一下问题:
df -h Filesystem Size Used Avail Use% Mounted on rootfs 271G 267G 2.2G 100% /
根分区满了,du 找不到占用这么大的文件。lsof查看delete文件,发现了罪魁祸首,
lsof |grep -i delete |grep -i zxagent sleep 4500 root 2w REG 8,2 1459 7399418 /tmp/.zxagent.command.output.-218666128 (deleted) sleep 6081 root 2w REG 8,2 202907880341 7399434 /tmp/.zxagent.command.output.-218666128 (deleted) chk_usr_m 23846 root 2w REG 8,2 202907880341 7399434 /tmp/.zxagent.command.output.-218666128 (deleted) netif_inf 23871 root 2w REG 8,2 202907880341 7399434 /tmp/.zxagent.command.output.-218666128 (deleted) sleep 25497 root 2w REG 8,2 202907880341 7399434 /tmp/.zxagent.command.output.-218666128 (deleted)----200G sh 30440 root 2w REG 8,2 1459 7399418 /tmp/.zxagent.command.output.-218666128 (deleted) uss_jbodm 31946 root 2w REG 8,2 1459 7399418 /tmp/.zxagent.command.output.-218666128 (deleted)
这个200G,不一定多落盘了,但根据周期性落盘来推算,这个占空间大肯定是没跑了,查看对应的fd,
ls -alrt /proc/23846/fd/* lrwx------ 1 root root 64 Apr 30 19:20 /proc/23846/fd/4 -> socket:[451533622] lr-x------ 1 root root 64 Apr 30 19:20 /proc/23846/fd/255 -> /home/zxcdn/ottcache/bin/chk_usr_mem.sh l-wx------ 1 root root 64 Apr 30 19:20 /proc/23846/fd/2 -> /tmp/.zxagent.command.output.-218666128 (deleted) l-wx------ 1 root root 64 Apr 30 19:20 /proc/23846/fd/1 -> /dev/null lr-x------ 1 root root 64 Apr 30 19:20 /proc/23846/fd/0 -> /dev/null
对应的fd是2,查看一下里面什么内容:
more /proc/23846/fd/2 chmod 777 start.sh ./start.sh dos2unix: converting file /home/iTool/UPDATE_20190415164111/2/update/start_ott.sh to UNIX format ... dos2unix: converting file /home/iTool/UPDATE_20190415164111/2/update/start.sh to UNIX format ... dos2unix: converting file /home/iTool/UPDATE_20190415164111/2/update/start_zmss.sh to UNIX format ... ###start /home/zxcdn/ottcache/superinit.sh &################# Add execute right to script...
看起来是一些错误日志。
tail -f /proc/23846/fd/2 2019-05-05 18:06:54 Watching proccess[/ZMSS/ZMSSComAgent]<3622> was abnormal exited, restart it. 2019-05-05 18:06:54 Proccess [/ZMSS/ZMSSComAgent]<3623> normal exiting, exit code[1] 2019-05-05 18:06:54 Watching proccess[/ZMSS/ZMSSComAgent]<3623> was abnormal exited, restart it. 2019-05-05 18Could not read configuration file [/ZMSS/etc/ZMSSComAgent/etc_memlog/log4cxx.conf]. Ignoring configuration file [/ZMSS/etc/ZMSSComAgent/etc_memlog/log4cxx.conf]. Use uss_badchunkget. Use uss_tiermode_query. Use DFS File System Interface. log4cxx_SetThreshold <3><ERROR> Cannot change owner of /var/ZMSS to root:root, err<-1>!
从时间点看,案发当时还在打印错误日志。
如果只是这么看,那么就不记录这个问题了。问题是,我脑抽地想通过内核模块查看这个文件,会报错。
如果使用vmtouch去查看这个文件占用内存的大小,会发现报错,报文件不存在,所以写了一个内核模块去查看它的缓存页数:
21081455 /tmp/.zxagent.command.output.-218666128
结果显示,这个文件有21081455 个页缓存在内存中,这个大概是84个G,文件本身大小为200G,所以是部分缓存了,
那如果不通过fd查看,怎么查看这个文件的内容呢,要查看这个内容,内核中通过如下方式来读取和写文件:
打开文件 struct file *filp_open(const char *filename, int flags, int mode) 读文件 ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) 写文件 ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) 关闭文件 int filp_close(struct file *filp, fl_owner_t id)
但要注意的是,需要
file_caq = filp_open("/tmp/.zxagent.command.output.1246", O_RDWR | O_CREAT | O_TRUNC, 0644); if ( !file ) { printk("file open(%s) failed!\n", "/tmp/.zxagent.command.output.1246"); filp_close(file, NULL); return -1; } else { printk("open /tmp/.zxagent.command.output.1246 success\r\n"); } ret = file_caq->f_op->read(file, (char *)buf, 512, &pos); printk("read /tmp/.zxagent.command.output.1246 ret=%d,read=%p\r\n",ret,file_caq->f_op->aio_read);
打印如下:
[12833325.784972] pages_total = 21081455(82349M),HZ=250 [12833325.785019] open /tmp/.zxagent.command.output.1246 success [12833325.785022] read /tmp/.zxagent.command.output.1246 ret=-14,read=ffffffff810fd420
看来是read失败了,这个主要是因为权限检查,然后修改为如下:
old_fs = get_fs(); set_fs(KERNEL_DS); ret = vfs_read(file_caq, (char *)buf_caq, 511, &pos); set_fs(old_fs);
发现不再报错,但是读取的数据为空,也就是read返回0。
然后再手工ls一下这个文件,发现确实创建了一个新的文件,为空,文件名是 /tmp/.zxagent.command.output.1246,当然inode号和之前delete 的文件名不一样。
那就把 对应文件的打开的时候的O_CREAT给去掉,干脆全部改成O_READONLY
file_caq = filp_open("/tmp/.zxagent.command.output.1246", O_RDWR | O_CREAT | O_TRUNC, 0644); 这行修改为 file_caq = filp_open("/tmp/.zxagent.command.output.1246", O_RDONLY, 0644);
结果crash了,打印的crash提示为:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000026
经过反汇编查看,发现因为file_caq虽然不为NULL,但是返回的是-2,所以导致 :
if ( !file )这个判断失效了,导致打印了open success。
ret = file_caq->f_op->read(file, (char *)buf, 512, &pos);
这行出现crash了,因为 file_caq的值为-2,加上f_op是偏移,一开始为了这个26的偏移还看了一会,后来才想通是因为-2 + 0x28 才等于0x26。
所以把NULL的判断改为 if (IS_ERR(file_caq)),再进行测试,发现文件打开失败,返回的错误是-2.
那么返回-2就是说,文件不存在,因为我去掉了create标志,自然报错了。所以标记为delete的文件,如果使用file_open去打开也是不行的,会报文件不存在的。
那么要获取这个文件,只能在内核态从inode入手,找到对应的address_space,然后再把radix_tree中的page 拷贝出来了,此为后话。
绕了半圈,费了劲,没什么收获。不过顺便确定了下lsof看到的deleted,并不是lsof实现的,而是内核在打印
2 ffff882b9020ad00 ffff882cd153f680 ffff882f79b0f5f0 CHR /dev/pts/26 3 ffff8859f47ac700 ffff885f79a69380 ffff882dee933d48 REG /home/caq/caq.txt-----------这个是测试用的deleted的文件 crash> dentry.d_hash ffff885f79a69380 d_hash = { next = 0x0, pprev = 0x0 } crash> dentry.d_hash ffff882cd153f680 d_hash = { next = 0x0, pprev = 0xffffc9000ff81f60 }
判断方式:
static inline int hlist_bl_unhashed(const struct hlist_bl_node *h) { return !h->pprev; } static inline int d_unhashed(struct dentry *dentry) { return hlist_bl_unhashed(&dentry->d_hash); } static int path_with_deleted(const struct path *path, const struct path *root, char **buf, int *buflen) { prepend(buf, buflen, "\0", 1); if (d_unlinked(path->dentry)) {------判断是否增加deleted int error = prepend(buf, buflen, " (deleted)", 10); if (error) return error; }