特定のディレクトリ内のファイルを監視し、ファイルの1つに変更が加えられるたびに通知を受ける必要があるJavaプロジェクトに取り組んでいます。これはWatchService
. さらに、どのような変更が加えられたのか知りたいです。例えば、「10 から 15 の文字が削除された場所」、「インデックスに 13 文字の 'abcd' が追加された」などです。ファイルシステムの監視。また、同じファイルを 2 回保存することを避けるために diff ソリューションを回避したいと考えています。また、アルゴリズムが複雑なため、大きなファイルの場合は時間がかかります。ご協力ありがとう御座います。:)
1 に答える
Linux を使用している場合、次のコードはファイル長の変更を検出します。これを簡単に拡張して変更を更新できます。
2 つのファイルを保持したくないため、ファイルの長さが短くなった場合 (失われた文字が見つからない場合)、またはファイルが途中で変更された場合、どの文字が変更されたかを知る方法はありません。
#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 に送られます。より良い実装は、おそらく poll オプションを使用して、debugfs で循環バッファーにアクセスできるようにすることです。
- 現在のコード キャプチャはすべてのファイルへの書き込みをキャプチャしますが、それはあなたが望むものではないと確信しています...
[編集 2]
この関数がどこにあるかを忘れていfs/read_write.c
ました。カーネル ツリーの下にあります。
[EDIT3]監視するプログラムがわかっている場合、およびlibcが静的にリンクされていない場合は、LD_PRELOADを使用して関数をオーバーライドし、write
それをフックとして使用して変更を記録する別の解決策があります。私はこれを試していませんが、動作しない理由はありません