Как отслеживать изменения файлов и знать, какие изменения были внесены

Я работаю над проектом Java, где мне нужно отслеживать файлы в определенном каталоге и получать уведомления, когда в один из файлов вносятся изменения, этого можно добиться с помощью WatchService, Кроме того, я хочу знать, какие изменения были внесены, например: "символы 10–15 были удалены", "в индексе добавлено 13 символов" abcd ""... Я готов принять любое решение, даже на основе языка c Мониторинг файловой системы. Я также хочу избежать решения diff, чтобы не хранить один и тот же файл 2 раза, а для сложности алгоритма это занимает много времени для больших файлов. Спасибо за помощь.:)

1 ответ

Если вы используете Linux, то следующий код обнаружит изменения в длине файла, вы можете легко расширить это для обновления модификаций.

Поскольку вы не хотите хранить два файла, невозможно определить, какие символы были изменены, если либо уменьшена длина файла (потерянные символы не могут быть найдены), либо файл был изменен где-то посередине

#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 используйте и используйте это как свой крючок и запишите изменения. Я не пробовал это, но нет никаких причин, почему это не должно работать

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