如何监视文件修改并了解所做的更改

时间:2015-03-30 08:21:43

标签: java filesystems file-monitoring

我正在开发一个Java项目,我需要监视某个目录中的文件,并在其中一个文件发生更改时收到通知,这可以使用WatchService来实现。此外,我想知道做了哪些更改,例如:“删除了字符10到15”,“添加了索引13个字符'abcd'”......我愿意甚至基于c语言采取任何解决方案监视文件系统。 我还想避免diff解决方案以避免存储相同的文件2次,并且对于算法的复杂性,大文件需要花费很多时间。 谢谢你的帮助。 :)

1 个答案:

答案 0 :(得分:2)

如果您使用的是Linux,则以下代码将检测文件长度的变化,您可以轻松扩展此更新以更新修改。

因为您不想保留两个文件,如果文件长度减少(无法找到丢失的字符)或文件在中间某处被更改,则无法确定哪些字符被更改< / p>

#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char** argv)
{
    int fd = open("test", O_RDONLY);
    int length = lseek(fd, 0, SEEK_END);
    while (1)
    {
        int new_length;
        close(fd);
        open("test", O_RDONLY);
        sleep(1);
        new_length = lseek(fd, 0, SEEK_END);
        printf("new_length = %d\n", new_length);
        if (new_length != length)
            printf ("Length changed! %d->%d\n", length, new_length);
        length=new_length;
    }
}

[编辑]
由于作者接受了对此任务的内核更改,因此对vfs_write的以下更改应该可以解决问题:

#define MAX_DIFF_LENGTH 128
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
    char old_content[MAX_DIFF_LENGTH+1];
    char new_content[MAX_DIFF_LENGTH+1];
    ssize_t ret;

    if (!(file->f_mode & FMODE_WRITE))
        return -EBADF;
    if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write))
        return -EINVAL;
    if (unlikely(!access_ok(VERIFY_READ, buf, count)))
        return -EFAULT;

    ret = rw_verify_area(WRITE, file, pos, count);
    if (___ishay < 20)
    {
        int i;
        int length = count > MAX_DIFF_LENGTH ? MAX_DIFF_LENGTH : count;
        ___ishay++;
        vfs_read(file, old_content, length, pos);
        old_content[length] = 0;
        new_content[length] = 0;
        memcpy(new_content, buf, length);
        printk(KERN_ERR"[___ISHAY___]Write request for file named: %s count: %d pos: %lld:\n", 
                file->f_path.dentry->d_name.name,
                count,
                *pos);
        printk(KERN_ERR"[___ISHAY___]New content (replacement) <%d>:\n", length);

        for (i=0;i<length;i++)
        {
            printk("[0x%02x] (%c)", new_content[i], (new_content[i] > 32 && new_content[i] < 127) ? 
                    new_content[i] : 46);
            if (length+1 % 10 == 0)
                printk("\n");
        }
        printk(KERN_ERR"[___ISHAY___]Old content (on file now):\n");
        for (i=0;i<length;i++)
        {
            printk("[0x%02x] (%c)", old_content[i], (old_content[i] > 32 && old_content[i] < 127) ? 
                    old_content[i] : 46);
            if (length+1 % 10 == 0)
                printk("\n");
        }

    }
    if (ret >= 0) {
        count = ret;
        if (file->f_op->write)
            ret = file->f_op->write(file, buf, count, pos);
        else
            ret = do_sync_write(file, buf, count, pos);
        if (ret > 0) {
            fsnotify_modify(file);
            add_wchar(current, ret);
        }
        inc_syscw(current);
    }

    return ret;
}

说明:
vfs_write是处理文件写入请求的函数,因此这是我们最好的中心钩子,用于在文件发生之前捕获文件的修改请求。
vfs_write接受写操作的文件,文件位置,缓冲区和长度,因此我们知道该写操作将替换该文件的哪个部分,以及将替换它的数据。

因为我们知道文件的哪个部分会被改变,所以我在实际写入之前添加了vfs_read调用,以便在内存中保留我们即将溢出的文件部分。

这应该是获得所需内容的良好起点,我做了以下简化,因为这只是一个例子:

  • 缓冲区静态分配最多128个字节(应动态分配并保护内存分配不会在大量写入请求上浪费太多内存)
  • 应检查文件长度,并且读取缓冲区应参考此检查,即使写入溢出超出文件结束的长度,当前代码也会打印读取缓冲区
  • 输出目前转到dmesg。更好的实现是在debugfs中保持循环缓冲区可访问,可能使用poll选项
  • 当前代码捕获写入所有文件,我确定这不是你想要的......

[EDIT2]
忘了提到这个函数所在的位置,它位于内核树中的fs/read_write.c

[EDIT3] 还有另一种可能的解决方案,让您知道要监视哪个程序,并且没有静态链接的libc是使用LD_PRELOAD来覆盖write函数并将其用作您的钩子并记录变化。我没有试过这个,但是没有理由不这样做