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!
", "/tmp/.zxagent.command.output.1246");
                filp_close(file, NULL);
                return -1;
        }
        else
        {
            printk("open /tmp/.zxagent.command.output.1246 success
");
        }

        ret = file_caq->f_op->read(file, (char *)buf, 512, &pos);
             printk("read /tmp/.zxagent.command.output.1246 ret=%d,read=%p
",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实现的,而是内核在打印 

path_with_deleted 的时候,append进去的,判断标准就是对应path的dentry是否被hashed。
  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, "", 1);
    if (d_unlinked(path->dentry)) {------判断是否增加deleted
        int error = prepend(buf, buflen, " (deleted)", 10);
        if (error)
            return error;
    }
原文地址:https://www.cnblogs.com/10087622blog/p/10818350.html