Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
deepin-anything (7.0.61) unstable; urgency=medium

* feat(kernelmod): track file close-write events
* feat(daemon): handle ACT_CLOSE_WRITE_FILE event for index update
* fix(kernelmod): add missing parentheses in cmp_event_path macro

-- wangrong <wangrong@uniontech.com> Mon, 24 Aug 2026 14:13:12 +0800

deepin-anything (7.0.60) unstable; urgency=medium

* fix(daemon): ensure daemon exit within 3 seconds via TimeoutStopSec
Expand Down
1 change: 1 addition & 0 deletions src/daemon/include/core/base_event_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class base_event_handler
void add_index_delay(std::string path);
void remove_index_delay(std::string path);
void update_index_delay(std::string src, std::string dst);
void update_index_delay(std::string path);
void scan_index_delay(std::string path);
void recursive_update_index_delay(std::string src, std::string dst);
void init_scan_index_delay(std::string path);
Expand Down
5 changes: 5 additions & 0 deletions src/daemon/include/core/file_index_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ class file_index_manager {
/// @param new_path The new path to be added to the index.
bool update_index(const std::string& old_path, const std::string& new_path);

/// @brief Update the index for a file that has been written and closed.
/// Skips the update if the target file does not exist.
/// @param path The full path of the modified file.
bool update_index(const std::string& path);

/// Commit all changes to the index
bool commit(index_status status);

Expand Down
6 changes: 6 additions & 0 deletions src/daemon/src/core/base_event_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ void base_event_handler::update_index_delay(std::string src, std::string dst) {
jobs_push(std::move(src), anything::index_job_type::update, std::move(dst));
}

void base_event_handler::update_index_delay(std::string path) {
jobs_push(std::move(path), anything::index_job_type::update);
}

void base_event_handler::scan_index_delay(std::string path) {
jobs_push(std::move(path), anything::index_job_type::scan);
}
Expand Down Expand Up @@ -243,6 +247,8 @@ void base_event_handler::eat_job(const anything::index_job& job) {
case anything::index_job_type::update:
if (job.dst) {
ret = index_manager_.update_index(job.src, *job.dst);
} else {
ret = index_manager_.update_index(job.src);
}
break;
case anything::index_job_type::scan:
Expand Down
11 changes: 10 additions & 1 deletion src/daemon/src/core/default_event_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ bool default_event_handler::prepare_event(fs_event *fs_evt,
case ACT_NEW_FOLDER:
case ACT_DEL_FILE:
case ACT_DEL_FOLDER:
case ACT_CLOSE_WRITE_FILE:
out->act = fs_evt->act;
out->src = fs_evt->src;
out->dst = "";
Expand Down Expand Up @@ -252,7 +253,12 @@ bool default_event_handler::prepare_event(fs_event *fs_evt,
}

void default_event_handler::filter_event(fs_event *fs_evt) {
[[maybe_unused]] const char* act_names[] = {"file_created", "link_created", "symlink_created", "dir_created", "file_deleted", "dir_deleted", "file_renamed", "dir_renamed"};
[[maybe_unused]] const char* act_names[] = {
"file_created", "link_created", "symlink_created", "dir_created",
"file_deleted", "dir_deleted",
"file_renamed", "dir_renamed", "file_renamed_from", "file_renamed_to", "dir_renamed_from", "dir_renamed_to",
"mount", "unmount",
"file_close_write" };

fs_event_with_full_path event;
if (!prepare_event(fs_evt, &event))
Expand All @@ -275,6 +281,9 @@ void default_event_handler::filter_event(fs_event *fs_evt) {
// Do not check for the existence of files; we trust the kernel module.
convert_event_path_to_origin_path(event.src, *src_indexing_item);
add_index_delay(std::move(event.src));
} else if (event.act == ACT_CLOSE_WRITE_FILE) {
convert_event_path_to_origin_path(event.src, *src_indexing_item);
update_index_delay(std::move(event.src));
} else if (event.act == ACT_DEL_FILE || event.act == ACT_DEL_FOLDER) {
convert_event_path_to_origin_path(event.src, *src_indexing_item);
remove_index_delay(std::move(event.src));
Expand Down
23 changes: 23 additions & 0 deletions src/daemon/src/core/file_index_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,29 @@ bool file_index_manager::update_index(const std::string& old_path, const std::st
return ret;
}

bool file_index_manager::update_index(const std::string& path) {
auto record = make_file_record(path, pinyin_processor_, file_type_mapping_);
if (record.modify_time == 0) {
spdlog::debug("Skip updating index for non-existent file: {}", path);
return true;
}

bool ret = false;

try {
auto doc = create_document(record);
writer_->updateDocument(newLucene<Term>(FULL_PATH_FIELD, StringUtils::toUnicode(path)), doc);
spdlog::debug("Updated index for {}", path);
ret = true;
} catch (const LuceneException& e) {
spdlog::error("Failed to update index for {}: {}", path, StringUtils::toUTF8(e.getError()));
} catch (const std::exception& e) {
spdlog::error("Failed to update index for {}: {}", path, e.what());
}

return ret;
}

bool check_index_corrupted(const std::string& index_directory) {
try {
FSDirectoryPtr dir = FSDirectory::open(StringUtils::toUnicode(index_directory));
Expand Down
14 changes: 12 additions & 2 deletions src/kernelmod/event_merge.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
--events_number;\
}

#define cmp_event_path(e1, e2) e1->dev != e2->dev || strcmp(e1->path, e2->path)
#define cmp_event_path(e1, e2) ((e1)->dev != (e2)->dev || strcmp((e1)->path, (e2)->path))

/*
* merge rules
Expand Down Expand Up @@ -84,6 +84,7 @@
*
* new(X) + del(X) => remove new(X)
* ren_fr(X) + ren_to(Y) + del(Y) => update ren_fr(X) to del(X), remove ren_to(Y)
* close_write(X) + del(X) => remove close_write(X), merge fail
*
* merge success, remove cur, else add to list
*/
Expand Down Expand Up @@ -112,6 +113,10 @@

REMOVE_ENTRY(p, e);
return MERGE_OK;
} else if (ACT_CLOSE_WRITE_FILE == e->action) {
if (!cmp_event_path(e, cur))
REMOVE_ENTRY(p, e);
return MERGE_FAIL;
}
return MERGE_FAIL;
}
Expand All @@ -121,6 +126,7 @@
*
* new(X) + ren_fr(X) + ren_to(Y) => update ren_to(Y) to new(Y), remove new(X)
* ren_fr(X) + ren_to(Y) + ren_fr(Y) + ren_to(Z) => update ren_fr(X) to del(X), update ren_to(Z) to new(Z), remove ren_to(Y)
* close_write(X) + ren_fr(X) => remove close_write(X), merge fail
*
* merge success, remove cur, else add to list
*/
Expand Down Expand Up @@ -156,6 +162,10 @@

REMOVE_ENTRY(p, e);
return MERGE_OK;
} else if (ACT_CLOSE_WRITE_FILE == e->action) {
if (!cmp_event_path(e, cur))
REMOVE_ENTRY(p, e);
return MERGE_FAIL;
}
return MERGE_FAIL;
}
Expand Down Expand Up @@ -191,7 +201,7 @@
return MERGE_FAIL;
}

static merge_action_fn_t action_merge_fns[] = {merge_new_file, merge_new_file, merge_new_file, 0, merge_del_file, 0, 0, 0, merge_rename_from_file, merge_rename_to_file, 0, 0, 0, 0};
static merge_action_fn_t action_merge_fns[] = {merge_new_file, merge_new_file, merge_new_file, 0, merge_del_file, 0, 0, 0, merge_rename_from_file, merge_rename_to_file, 0, 0, 0, 0, 0};

Check warning on line 204 in src/kernelmod/event_merge.c

View workflow job for this annotation

GitHub Actions / cppcheck

Parameter 'cur' can be declared as pointer to const. However it seems that 'merge_rename_from_file' is a callback function, if 'cur' is declared with const you might also need to cast function pointer(s).

Check warning on line 204 in src/kernelmod/event_merge.c

View workflow job for this annotation

GitHub Actions / cppcheck

Parameter 'cur' can be declared as pointer to const. However it seems that 'merge_del_file' is a callback function, if 'cur' is declared with const you might also need to cast function pointer(s).

Check warning on line 204 in src/kernelmod/event_merge.c

View workflow job for this annotation

GitHub Actions / cppcheck

Parameter 'cur' can be declared as pointer to const. However it seems that 'merge_new_file' is a callback function, if 'cur' is declared with const you might also need to cast function pointer(s).

/*
* notify policy
Expand Down
1 change: 1 addition & 0 deletions src/kernelmod/vfs_change_consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
#define ACT_RENAME_TO_FOLDER 11
#define ACT_MOUNT 12
#define ACT_UNMOUNT 13
#define ACT_CLOSE_WRITE_FILE 14
6 changes: 5 additions & 1 deletion src/kernelmod/vfs_fsnotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ static void on_file_op(int action, struct inode *p_inode, const unsigned char *f
}
}

#define TARGET_EVENT (FS_DELETE | FS_UNMOUNT_DIR | FS_MOUNT_DIR | FS_CREATE | FS_MOVED_FROM | FS_MOVED_TO)
#define TARGET_EVENT (FS_DELETE | FS_UNMOUNT_DIR | FS_MOUNT_DIR | FS_CREATE | FS_MOVED_FROM | FS_MOVED_TO | FS_CLOSE_WRITE)

static inline void fsnotify_event_handler(struct inode *to_tell, __u32 mask, const unsigned char *file_name, u32 cookie)
{
Expand All @@ -194,6 +194,10 @@ static inline void fsnotify_event_handler(struct inode *to_tell, __u32 mask, con
case FS_UNMOUNT_DIR:
on_unmount(file_name);
break;
case FS_CLOSE_WRITE:
if (!(mask & FS_ISDIR))
on_file_op(ACT_CLOSE_WRITE_FILE, to_tell, file_name, cookie);
break;
default:
break;
}
Expand Down
37 changes: 36 additions & 1 deletion src/kernelmod/vfs_kretprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
#include <linux/list.h>
#include <linux/uaccess.h>
#include <linux/namei.h>
#include <linux/version.h>

Check warning on line 15 in src/kernelmod/vfs_kretprobes.c

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <linux/version.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <linux/atomic.h>

Check warning on line 16 in src/kernelmod/vfs_kretprobes.c

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <linux/atomic.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <linux/err.h>

Check warning on line 17 in src/kernelmod/vfs_kretprobes.c

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <linux/err.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <linux/fsnotify_backend.h>

Check warning on line 18 in src/kernelmod/vfs_kretprobes.c

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <linux/fsnotify_backend.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include "arg_extractor.h"
#include "vfs_change_consts.h"
Expand Down Expand Up @@ -520,9 +521,43 @@

DECL_CMN_KRP(vfs_rename);

static int on___fput_ent(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct file *file = (struct file *)get_arg(regs, 1);
struct vfs_event *event;
struct inode *inode = file_inode(file);

if (unlikely(!file))
return 1;

if (inode && S_ISDIR(inode->i_mode))
return 1;

if (!(file->f_mode & FMODE_OPENED))
return 1;

if (!(file->f_mode & FMODE_WRITE))
return 1;

if (common_vfs_ent(&event, file->f_path.dentry))
return 1;

event->action = ACT_CLOSE_WRITE_FILE;
vfs_changed_entry(event);
return 0;
}

static struct kretprobe __fput_krp = {
.entry_handler = on___fput_ent,
.handler = NULL,
.data_size = 0,
.maxactive = 64,
.kp.symbol_name = "__fput",
};

static struct kretprobe *vfs_krps[] = {&do_mount_krp, &sys_umount_krp, &vfs_create_krp,
&vfs_unlink_krp, &vfs_mkdir_krp, &vfs_rmdir_krp, &vfs_symlink_krp, &vfs_link_krp,
&vfs_rename_krp, &security_inode_create_krp
&vfs_rename_krp, &security_inode_create_krp, &__fput_krp
};

int init_vfs_kretprobes(void *vfs_changed_func)
Expand Down
86 changes: 38 additions & 48 deletions src/server/event-dispatcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,96 +150,86 @@ static gboolean convert_fs_event(ServerEventDispatcher *dispatcher,
{
dispatch_fs_event_init(out);

if (event->act == ACT_MOUNT || event->act == ACT_UNMOUNT) {
g_debug("%s: %s",
event->act == ACT_MOUNT ? "Mount a device" : "Unmount a device",
event->src);
mount_info_update(dispatcher->mount_info);
return TRUE;
}

gchar *root = NULL;
if (event->act < ACT_MOUNT) {
out->device_id = makedev(event->major, event->minor);

const gchar *mount_point = mount_info_get_device_mount_point(
dispatcher->mount_info, out->device_id);
if (!mount_point) {
g_debug("Unknown device: %u, dev: %u:%u, path: %s, cookie: %u",
+event->act, event->major, +event->minor,
event->src, event->cookie);
return TRUE;
}
root = g_strdup(mount_point);
if (g_strcmp0(root, "/") == 0) {
g_free(root);
root = NULL;
}
}

switch (event->act) {
case ACT_NEW_FILE:
case ACT_NEW_SYMLINK:
case ACT_NEW_LINK:
case ACT_NEW_FOLDER:
case ACT_DEL_FILE:
case ACT_DEL_FOLDER:
case ACT_CLOSE_WRITE_FILE:
out->act = event->act;
out->src = g_strdup(event->src);
out->dst = g_strdup("");
break;

case ACT_RENAME_FROM_FILE:
case ACT_RENAME_FROM_FOLDER:
g_hash_table_insert(dispatcher->rename_from,
GUINT_TO_POINTER(event->cookie),
g_strdup(event->src));
g_free(root);
return TRUE;

case ACT_RENAME_TO_FILE:
case ACT_RENAME_TO_FOLDER: {
gpointer from_src = g_hash_table_lookup(dispatcher->rename_from,
GUINT_TO_POINTER(event->cookie));
if (from_src) {
out->act = (event->act == ACT_RENAME_TO_FILE)
? ACT_RENAME_FILE : ACT_RENAME_FOLDER;
out->cookie = event->cookie;
out->dst = g_strdup(event->src);
out->src = g_strdup((const gchar *)from_src);
g_hash_table_remove(dispatcher->rename_from,
GUINT_TO_POINTER(event->cookie));
} else {
if (!from_src) {
g_debug("Rename-to without matching rename-from (cookie=%u)",
event->cookie);
g_free(root);
return TRUE;
}
out->act = (event->act == ACT_RENAME_TO_FILE)
? ACT_RENAME_FILE : ACT_RENAME_FOLDER;
out->cookie = event->cookie;
out->src = g_strdup((const gchar *)from_src);
out->dst = g_strdup(event->src);
g_hash_table_remove(dispatcher->rename_from,
GUINT_TO_POINTER(event->cookie));
break;
}

case ACT_RENAME_FILE:
case ACT_RENAME_FOLDER:
g_warning("Unsupported file action: %u", +event->act);
g_free(root);
g_warning("Unsupported file action: %u", (guint)event->act);
return TRUE;

case ACT_MOUNT:
case ACT_UNMOUNT:
g_debug("%s: %s",
event->act == ACT_MOUNT ? "Mount a device" : "Unmount a device",
event->src);
mount_info_update(dispatcher->mount_info);
return TRUE;

default:
g_warning("Unknown file action: %u", +event->act);
g_free(root);
g_warning("Unknown file action: %u", (guint)event->act);
return TRUE;
}

out->device_id = makedev(event->major, event->minor);
const gchar *mount_point = mount_info_get_device_mount_point(
dispatcher->mount_info, out->device_id);
if (!mount_point) {
g_debug("Unknown device: %u, dev: %u:%u, path: %s, cookie: %u",
(guint)event->act, event->major, (guint)event->minor,
event->src, event->cookie);
return TRUE;
}

if (root) {
if (g_strcmp0(mount_point, "/") != 0) {
if (out->src) {
gchar *new_src = g_strconcat(root, out->src, NULL);
gchar *prefixed = g_strconcat(mount_point, out->src, NULL);
g_free(out->src);
out->src = new_src;
out->src = prefixed;
}
if (out->dst && out->dst[0] != '\0') {
gchar *new_dst = g_strconcat(root, out->dst, NULL);
gchar *prefixed = g_strconcat(mount_point, out->dst, NULL);
g_free(out->dst);
out->dst = new_dst;
out->dst = prefixed;
}
}

g_free(root);
return FALSE;
}

Expand Down
Loading