shell bypass 403

Cubjrnet7 Shell


name : fs_event.c
/**
@file
@brief    File system events messages
@details  Copyright (c) 2017-2021 Acronis International GmbH
@author   Mikhail Krivtsov ([email protected])
@since    $Id: $
*/

#include "fs_event.h"

#include "compat.h"
#include "debug.h"
#include "file_path_tools.h"
#include "memory.h"
#include "message.h"
#include "path_tools.h"
#include "task_info_map.h"
#include "transport.h"
#include "transport_protocol.h"
#include "si_templates.h"
#include "si_writer.h"
#include "si_writer_common.h"

// 'linux/cred.h' appeared in 'stable/v2.6.27'
#include <linux/fcntl.h>	// for O_CREAT, etc. flags
#include <linux/fsnotify.h>
#include <linux/kernel.h>	// for macroses such as 'ARRAY_SIZE'
#include <linux/limits.h>	// PATH_MAX
#include <linux/mman.h>

#define PATH_FILTERING_ENABLED // comment this #define to disable path filtering


#ifdef PATH_FILTERING_ENABLED
#define DEFINE_FILTER_MASK(path) {path, sizeof(path) - 1}

typedef struct filter_mask {
	char *filter_mask_path;
	size_t filter_mask_len;
} filter_mask_t;

filter_mask_t filter_masks[] = {
	DEFINE_FILTER_MASK("/sys"),
	DEFINE_FILTER_MASK("/proc"),
	DEFINE_FILTER_MASK("/dev")
};

#define FILTER_MASKS_NUMB ARRAY_SIZE(filter_masks)
#endif // PATH_FILTERING_ENABLED

#ifdef PATH_FILTERING_ENABLED
/*
 * FIXME: This function is actually useless in case of relative path or any path
 * that contains "..", because we do not normalize paths, so arbitrary amount of
 * ".." can eventually point us to any file inside any directory.
 */
static bool is_path_filtered(const char *pathname)
{
	size_t i = 0;

	for (i = 0; i < FILTER_MASKS_NUMB; i++) {
		// if 'path' is in 'filter_mask' folder (strncmp = 0) -> it is filtered
		if (((bool)strncmp(pathname, filter_masks[i].filter_mask_path, filter_masks[i].filter_mask_len)) == 0) {
			DPRINTF("pathname '%s' is filtered by filter_mask='%s'", pathname, filter_masks[i].filter_mask_path);
			return 1;
		}
	}

	DPRINTF("pathname '%s' isn't filtered by filter_masks", pathname);

	return 0;
}
#else
static inline bool is_path_filtered(const char *pathname) { return 0; }
#endif // PATH_FILTERING_ENABLED

inline static msg_t *copy_path_in_msg(msg_t *msg, const struct path *path)
{
	if (msg) {
		thread_safe_path_store_copy_directly(&msg->path, path);
	}
	return msg;
}

inline static msg_t *copy_file_context_msg_info_in_msg(msg_t *msg, const file_context_msg_info_t *info)
{
	if (msg) {
		msg->file_context_msg_info = *info;
	}
	return msg;
}

static long send_msg_sync_and_get_block(msg_t *msg)
{
	long block = 0;
	// Currently special files sending is not done synchronously.
	if (msg->subtype_mask & MSG_TYPE_TO_EVENT_MASK(FP_SI_ST_SPECIAL))
		return 0;

	send_msg_sync(msg);
	// If message was interrupted, the process was killed.
	// For the consistency, this means that syscall must be
	// blocked, otherwise APL might fail to backup.
	// Thankfully, process will not care about the
	// syscall result as it will be dead anyways.
	if (msg->block)
		block = -EPERM;

	if (msg->interrupted)
		block = -EINTR;

	thread_safe_path_clear(&msg->path);
	thread_safe_path_clear(&msg->path2);

	return block;
}

static long send_msg_sync_unref_and_get_block(msg_t *msg)
{
	long block;
	if (!msg)
		return 0;

	block = send_msg_sync_and_get_block(msg);
	msg_unref(msg);
	return block;
}

static inline void si_property_writer_write_object_key(si_property_writer_t *writer, const file_key_t* key) {
	SiObjectId object_id;
	object_id.DeviceId = key->dev;
	object_id.Id = key->ino;
	si_property_writer_write_object_id(writer, object_id);

	si_property_writer_write_object_file_generation(writer, key->gen);
	// TODO: This pointer should be hidden from userspace!
	si_property_writer_write_object_file_ptr(writer, key->ptr);
}

static const uint8_t k_object_fields[] = { SI_COMMON_FS_FIELDS, SI_COMMON_OBJECT_FILE_FIELDS };

void fs_event_create(task_info_t* task_info, const struct path *path)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t path_info;
	file_handle_info_t handle_info;
	if (path_info_make_from_valid(&path_info, path))
		return;

	file_handle_info_init_empty(&handle_info);
	if (is_path_filtered(path_info.str.value))
		goto end;

	file_handle_info_make(&handle_info, path);
	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_object_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(path_info)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(handle_info);
	msg = msg_new(FP_SI_OT_NOTIFY_FILE_CREATE, 0, SI_CT_POST_CALLBACK, unique_pid, event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();
	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &path_info, path, &handle_info);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->task_info = task_info_get(task_info);

end:
	path_info_free(&path_info);
	file_handle_info_free(&handle_info);
	return send_msg_async_unref(msg);
}

static const uint8_t k_mkdir_fields[] = { SI_COMMON_FS_FIELDS
                                        , FP_SI_PI_OBJECT_NAME
                                        , FP_SI_PI_ACCESS_MODE
                                        , FP_SI_PI_OBJECT_FILE_DENTRY_PTR
                                        , FP_SI_PI_OBJECT_FILE_DENTRY_NAME_PTR };

void fs_event_mkdir(task_info_t* task_info, const struct path *path, umode_t mode)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t path_info;
	if (path_info_make(&path_info, path, true /*dir*/))
		return;

	if (is_path_filtered(path_info.str.value))
		goto end;

	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_mkdir_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(path_info);
	msg = msg_new(FP_SI_OT_NOTIFY_FILE_MKDIR, 0, SI_CT_PRE_CALLBACK, unique_pid, event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();
	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		if (path_info.buf) {
			si_property_writer_write_object_name(&writer, path_info.str);
		}
		si_property_writer_write_access_mode(&writer, mode);
		si_property_writer_write_object_file_dentry_ptr(&writer, (uint64_t) path->dentry);
		si_property_writer_write_object_file_dentry_name_ptr(&writer, (uint64_t) path->dentry->d_name.name);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->task_info = task_info_get(task_info);

end:
	path_info_free(&path_info);
	return send_msg_async_unref(msg);
}

static const uint8_t k_object_to_target_fields[] = { SI_COMMON_FS_FIELDS, SI_COMMON_OBJECT_FILE_FIELDS, FP_SI_PI_TARGET_NAME };

void fs_event_pre_copyfile(task_info_t* task, const struct path *from, const struct path *to)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t from_path_info = (path_info_t){};
	path_info_t to_path_info = (path_info_t){};
	file_handle_info_t from_handle_info = (file_handle_info_t){};
	if (path_info_make_from_valid(&from_path_info, from))
		goto end;

	if (path_info_make(&to_path_info, to, false /*dir*/))
		goto end;

	if (is_path_filtered(from_path_info.str.value) || is_path_filtered(to_path_info.str.value))
		goto end;

	file_handle_info_make(&from_handle_info, from);
	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_object_to_target_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(from_path_info)
	           + SI_ESTIMATE_SIZE_PATH_INFO(to_path_info)
			   + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(from_handle_info);
	msg = msg_new(FP_SI_OT_NOTIFY_FILE_COPY, 0, SI_CT_PRE_CALLBACK, unique_pid, event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();
	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &from_path_info, from, &from_handle_info);
		si_property_writer_write_target_name(&writer, to_path_info.str);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->task_info = task_info_get(task);

end:
	path_info_free(&from_path_info);
	path_info_free(&to_path_info);
	file_handle_info_free(&from_handle_info);
	return send_msg_async_unref(msg);
}

static void msg_relabel(msg_t* msg, uint16_t ot, SUBTYPE_MASK_TYPE st, uint16_t ct) {
	msg->subtype_mask = st;
	msg->event.Operation = ot;
	msg->event.CallbackType = ct;
}

static long send_msg_sync_and_get_block_then_relabel_send_async(msg_t* msg, uint16_t notifyOt, SUBTYPE_MASK_TYPE notifySubType, uint16_t notifyCt) {
	long block = send_msg_sync_and_get_block(msg);
	if (!block) {
		// Play the same message but asynchronously
		msg_relabel(msg, notifyOt, notifySubType, notifyCt);
		send_msg_async(msg);
	}

	return block;
}

static long send_msg_sync_and_get_block_then_relabel_send_async_unref(msg_t* msg, uint16_t notifyOp, SUBTYPE_MASK_TYPE notifySubType, uint16_t notifyCt) {
	long block;
	if (!msg) {
		return 0;
	}

	block = send_msg_sync_and_get_block_then_relabel_send_async(msg, notifyOp, notifySubType, notifyCt);
	msg_unref(msg);

	return block;
}

static const uint8_t k_open_close_fields[] = { SI_COMMON_FS_FIELDS
                                             , SI_COMMON_OBJECT_FILE_FIELDS
                                             , FP_SI_PI_ACCESS_MODE
                                             , FP_SI_PI_FILE_MODIFIED };

long fs_event_pre_open(task_info_t* task_info, unsigned int flags, const struct path *path, const file_context_msg_info_t *info)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t path_info;
	file_handle_info_t handle_info;
	subtypes_t subtypes = fs_open_subtypes(flags, path->dentry->d_inode);

	if (path_info_make_from_valid(&path_info, path))
		return 0;

	file_handle_info_init_empty(&handle_info);
	if (is_path_filtered(path_info.str.value))
		goto end;

	file_handle_info_make(&handle_info, path);
	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_open_close_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(path_info)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(handle_info);
	msg = msg_new(FP_SI_OT_SYNC_FILE_PRE_OPEN
	            , (SUBTYPE_MASK_TYPE) subtypes.sync
	            , FP_SI_CT_WANT_REPLY
	            , unique_pid
	            , event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();

	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &path_info, path, &handle_info);
		si_property_writer_write_access_mode(&writer, flags);
		si_event_writer_finalize(&msg->event, &writer);
	}

	copy_file_context_msg_info_in_msg(msg, info);
	msg->task_info = task_info_get(task_info);
	msg->id = event_uid;
	msg->open.pid_version = task_info->pid_version;
	msg->open.flags = (int) flags;
	copy_path_in_msg(msg, path);

end:
	path_info_free(&path_info);
	file_handle_info_free(&handle_info);
	return send_msg_sync_and_get_block_then_relabel_send_async_unref(msg
	                                                               , FP_SI_OT_NOTIFY_FILE_PRE_OPEN
	                                                               , (SUBTYPE_MASK_TYPE) subtypes.notify
	                                                               , SI_CT_PRE_CALLBACK);
}

static const uint8_t k_fs_mmap_fields[] = { SI_COMMON_FS_FIELDS, SI_COMMON_OBJECT_FILE_FIELDS, FP_SI_PI_ACCESS_MODE, FP_SI_PI_FLAGS, FP_SI_PI_PROTECTION };

long fs_event_pre_mmap(task_info_t* task_info, unsigned int acc_mode, const struct path *path, unsigned long reqprot, unsigned long prot, unsigned long mmap_flags)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t path_info;
	file_handle_info_t handle_info = (file_handle_info_t){};
	bool writable = (mmap_flags & MAP_SHARED) && (prot & PROT_WRITE);
	subtypes_t subtypes = fs_mmap_subtypes(writable, path->dentry->d_inode);
	(void) reqprot;
	if (path_info_make_from_valid(&path_info, path))
		return 0;

	if (is_path_filtered(path_info.str.value))
		goto end;

	file_handle_info_make(&handle_info, path);
	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_fs_mmap_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(path_info)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(handle_info);
	msg = msg_new(FP_SI_OT_SYNC_FILE_PRE_MMAP
	            , (SUBTYPE_MASK_TYPE) subtypes.sync
	            , FP_SI_CT_WANT_REPLY
	            , unique_pid
	            , event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();

	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &path_info, path, &handle_info);
		si_property_writer_write_access_mode(&writer, acc_mode);
		si_property_writer_write_flags(&writer, mmap_flags);
		si_property_writer_write_protection(&writer, prot);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->task_info = task_info_get(task_info);
	msg->id = event_uid;
	copy_path_in_msg(msg, path);

end:
	path_info_free(&path_info);
	file_handle_info_free(&handle_info);
	return send_msg_sync_and_get_block_then_relabel_send_async_unref(msg
	                                                               , FP_SI_OT_NOTIFY_FILE_PRE_MMAP
	                                                               , (SUBTYPE_MASK_TYPE) subtypes.notify
	                                                               , SI_CT_PRE_CALLBACK);
}

void fs_event_pre_close(task_info_t* task_info, unsigned int flags, const struct path *path)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t path_info;
	file_handle_info_t handle_info = (file_handle_info_t){};
	subtypes_t subtypes = fs_close_subtypes(flags & (O_WRONLY | O_RDWR), path->dentry->d_inode);
	bool file_modified = false;
	file_context_info_t info = {0};

	make_file_context_info_with_inode(&info, path->dentry->d_inode, make_unique_pid(current));
	// file is modified by this process
	if (check_update_file_modify_cache(&info))
	{
		file_modified = true;
	}

	file_handle_info_init_empty(&handle_info);
	if (path_info_make_from_valid(&path_info, path))
		return;

	if (is_path_filtered(path_info.str.value))
		goto end;

	file_handle_info_make(&handle_info, path);
	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_open_close_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(path_info)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(handle_info);
	msg = msg_new(FP_SI_OT_SYNC_FILE_PRE_CLOSE
	            , (SUBTYPE_MASK_TYPE) subtypes.sync
	            , FP_SI_CT_WANT_REPLY
	            , unique_pid
	            , event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();

	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &path_info, path, &handle_info);
		si_property_writer_write_access_mode(&writer, flags);
		si_property_writer_write_file_modified(&writer, file_modified);

		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->id = event_uid;
	copy_path_in_msg(msg, path);
	msg->task_info = task_info_get(task_info);

end:
	path_info_free(&path_info);
	file_handle_info_free(&handle_info);
	// TODO DK: This should be asynchronous but with 'reply'?
	// TODO DK: This is problematic with current 'thread_safe' path approach
	return (void) send_msg_sync_and_get_block_then_relabel_send_async_unref(msg
	                                                                      , FP_SI_OT_NOTIFY_FILE_PRE_CLOSE
	                                                                      , (SUBTYPE_MASK_TYPE) subtypes.notify
	                                                                      , SI_CT_PRE_CALLBACK);
}

static const uint8_t k_fs_rw_mini_fields[] = { SI_COMMON_FS_FIELDS, SI_COMMON_OBJECT_MINI_FILE_FIELDS, FP_SI_PI_OBJECT_REGION, FP_SI_PI_ACCESS_MODE };

void fs_event_pre_full_read(task_info_t* task_info, const file_key_t* key, unsigned int f_flags, loff_t offset, size_t count)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	const uint32_t event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_fs_rw_mini_fields);
	msg_t *msg = NULL;
	SiRegion region;
	msg = msg_new(FP_SI_OT_NOTIFY_FILE_FULL_READ, 0, SI_CT_PRE_CALLBACK, unique_pid, event_size);
	if (!msg)
		return;

	event_uid = transport_global_sequence_next();
	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_key(&writer, key);
		si_property_writer_write_access_mode(&writer, f_flags);
		region.Start = offset;
		region.Length = count;
		si_property_writer_write_object_region(&writer, region);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->id = event_uid;
	msg->task_info = task_info_get(task_info);

	return send_msg_async_unref(msg);
}

long fs_event_pre_write(task_info_t* task_info, unsigned int f_flags, loff_t offset, size_t count, const struct path *path, const file_context_msg_info_t *info)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	const uint32_t event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_fs_rw_mini_fields);
	msg_t *msg = NULL;
	msg = msg_new(FP_SI_OT_SYNC_FILE_PRE_WRITE, fs_generic_subtype(path->dentry->d_inode), FP_SI_CT_WANT_REPLY, unique_pid, event_size);
	if (!msg)
		return 0;

	event_uid = transport_global_sequence_next();
	{
		si_property_writer_t writer;
		SiRegion region;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_mini_ids(&writer, path->dentry->d_inode);
		si_property_writer_write_access_mode(&writer, f_flags);
		region.Start = offset;
		region.Length = count;
		si_property_writer_write_object_region(&writer, region);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->id = event_uid;
	msg->write.pid_version = task_info->pid_version;
	msg->write.flags = (int) f_flags;
	msg->write.low = (uint64_t) offset;
	msg->write.high = (uint64_t) offset + (uint64_t) count;
	copy_path_in_msg(msg, path);
	copy_file_context_msg_info_in_msg(msg, info);
	msg->task_info = task_info_get(task_info);

	return send_msg_sync_unref_and_get_block(msg);
}

static const uint8_t k_fs_rename_fields[] = { SI_COMMON_FS_FIELDS
                                            , SI_COMMON_OBJECT_FILE_FIELDS
                                            , SI_COMMON_TARGET_FILE_FIELDS
                                            , FP_SI_PI_FLAGS
                                            , FP_SI_PI_TARGET_EXISTS };

long fs_event_pre_rename(task_info_t *task_info, unsigned int flags, const struct path *oldpath, const struct path *newpath, bool target_exists)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t old_path_info = (path_info_t){};
	path_info_t new_path_info = (path_info_t){};
	file_handle_info_t old_handle_info = (file_handle_info_t){};
	file_handle_info_t new_handle_info = (file_handle_info_t){};
	const struct inode* oldinode = oldpath->dentry->d_inode;
	bool dir = S_ISDIR(oldinode->i_mode);
	bool oldspecial = !S_ISDIR(oldinode->i_mode) && !S_ISREG(oldinode->i_mode);
	bool newspecial = true;
	uint64_t subtype = 0;
	if (path_info_make(&old_path_info, oldpath, dir))
		return 0;

	if (is_path_filtered(old_path_info.str.value))
		goto end;

	if (newpath) {
		if (path_info_make(&new_path_info, newpath, dir))
			goto end;

		if (is_path_filtered(new_path_info.str.value))
			goto end;
	}

	if (target_exists) {
		const struct inode* newinode = newpath->dentry->d_inode;
		newspecial = !S_ISDIR(newinode->i_mode) && !S_ISREG(newinode->i_mode);
	}
	subtype = (!oldspecial || !newspecial) ? 0 : MSG_TYPE_TO_EVENT_MASK(FP_SI_ST_SPECIAL);

	file_handle_info_make(&old_handle_info, oldpath);
	if (newpath)
		file_handle_info_make(&new_handle_info, newpath);

	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_fs_rename_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(old_path_info)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(old_handle_info);
	if (newpath) {
		event_size += SI_ESTIMATE_SIZE_PATH_INFO(new_path_info)
		            + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(new_handle_info);
	}
	msg = msg_new(FP_SI_OT_SYNC_FILE_PRE_RENAME, subtype, FP_SI_CT_WANT_REPLY, unique_pid, event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();

	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &old_path_info, oldpath, &old_handle_info);
		if (newpath)
			si_property_writer_write_target_file(&writer, &new_path_info, newpath, &new_handle_info);

		si_property_writer_write_flags(&writer, flags);
		si_property_writer_write_target_exists(&writer, target_exists);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->id = event_uid;
	copy_path_in_msg(msg, oldpath);
	if (target_exists && newpath) {
		thread_safe_path_store_copy_directly(&msg->path2, newpath);
	}
	msg->task_info = task_info_get(task_info);

end:
	path_info_free(&old_path_info);
	path_info_free(&new_path_info);
	file_handle_info_free(&old_handle_info);
	file_handle_info_free(&new_handle_info);

	return send_msg_sync_and_get_block_then_relabel_send_async_unref(msg, FP_SI_OT_NOTIFY_FILE_PRE_RENAME, subtype, SI_CT_PRE_CALLBACK);
}

long fs_event_pre_link(task_info_t *task_info, const struct path *oldpath, const struct path *newpath)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t old_path_info = (path_info_t){};
	path_info_t new_path_info = (path_info_t){};
	file_handle_info_t old_handle_info = (file_handle_info_t){};
	const struct inode* inode = oldpath->dentry->d_inode;
	// This should never be 'true' but just in case
	bool dir = S_ISDIR(inode->i_mode);
	SUBTYPE_MASK_TYPE subtype = fs_generic_subtype(inode);

	if (path_info_make(&old_path_info, oldpath, dir))
		return 0;

	if (is_path_filtered(old_path_info.str.value))
		goto end;

	if (path_info_make(&new_path_info, newpath, dir))
		goto end;

	if (is_path_filtered(new_path_info.str.value))
		goto end;

	file_handle_info_make(&old_handle_info, oldpath);

	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_object_to_target_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(old_path_info)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(old_handle_info)
	           + SI_ESTIMATE_SIZE_PATH_INFO(new_path_info);
	msg = msg_new(FP_SI_OT_SYNC_FILE_PRE_LINK, subtype, FP_SI_CT_WANT_REPLY, unique_pid, event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();

	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &old_path_info, oldpath, &old_handle_info);
		if (new_path_info.buf)
			si_property_writer_write_target_name(&writer, new_path_info.str);

		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->id = event_uid;
	copy_path_in_msg(msg, oldpath);
	msg->task_info = task_info_get(task_info);

end:
	path_info_free(&old_path_info);
	path_info_free(&new_path_info);
	file_handle_info_free(&old_handle_info);

	return send_msg_sync_and_get_block_then_relabel_send_async_unref(msg, FP_SI_OT_NOTIFY_FILE_PRE_LINK, subtype, SI_CT_PRE_CALLBACK);
}

static const uint8_t k_fs_fsnotify_rename_fields[] = { SI_COMMON_FS_FIELDS
                                                     , SI_COMMON_OBJECT_MINI_FILE_FIELDS
                                                     , FP_SI_PI_FLAGS
                                                     , FP_SI_PI_TARGET_NAME };

void fs_event_rename(task_info_t *task_info, const struct path *path, bool is_newpath_ok)
{
	path_info_t path_info = (path_info_t){};
	msg_t *msg = NULL;
	uint32_t event_size;
	uint64_t event_uid;
	uint64_t subtype = fs_generic_subtype(path->dentry->d_inode);
	// path is sent in msg only if newpath was not ok - that's because on syscall pre-rename target path is missing
	bool want_path = !is_newpath_ok;
	if (want_path) {
		// newpath was not ok so we need to add properties for post rename'd file. Notably we are interested in making a target_name
		if (path_info_make_from_valid(&path_info, path))
			return;

		if (is_path_filtered(path_info.str.value))
			goto end;
	}

	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_fs_fsnotify_rename_fields);
	if (want_path)
		event_size += SI_ESTIMATE_SIZE_PATH_INFO(path_info);

	msg = msg_new(FP_SI_OT_SYNC_FILE_RENAME, subtype, FP_SI_CT_WANT_REPLY, make_unique_pid(current), event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();
	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_object_mini_ids(&writer, path->dentry->d_inode);
		si_property_writer_write_flags(&writer, FS_MOVE_SELF);
		if (want_path)
			si_property_writer_write_target_name(&writer, path_info.str);

		si_event_writer_finalize(&msg->event, &writer);
	}

	copy_path_in_msg(msg, path);
	msg->id = event_uid;
	msg->task_info = task_info_get(task_info);

end:
	path_info_free(&path_info);
	return (void) send_msg_sync_and_get_block_then_relabel_send_async_unref(msg, FP_SI_OT_NOTIFY_FSNOTIFY_RENAME, subtype, SI_CT_POST_CALLBACK);
}

long fs_event_pre_unlink(task_info_t* task_info, const struct path *path)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	uint64_t subtype = fs_generic_subtype(path->dentry->d_inode);
	path_info_t path_info;
	file_handle_info_t handle_info;
	if (path_info_make_from_valid(&path_info, path))
		return 0;

	file_handle_info_init_empty(&handle_info);
	if (is_path_filtered(path_info.str.value))
		goto end;

	file_handle_info_make(&handle_info, path);
	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_object_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(path_info)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(handle_info);
	msg = msg_new(FP_SI_OT_SYNC_FILE_PRE_UNLINK, subtype, FP_SI_CT_WANT_REPLY, unique_pid, event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();

	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &path_info, path, &handle_info);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->id = event_uid;
	copy_path_in_msg(msg, path);
	msg->task_info = task_info_get(task_info);

end:
	path_info_free(&path_info);
	file_handle_info_free(&handle_info);

	return send_msg_sync_and_get_block_then_relabel_send_async_unref(msg, FP_SI_OT_NOTIFY_FILE_PRE_UNLINK, subtype, SI_CT_PRE_CALLBACK);
}

long fs_event_pre_truncate(task_info_t* task_info, const struct path *path)
{
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t path_info;
	file_handle_info_t handle_info;
	uint64_t subtype = fs_generic_subtype(path->dentry->d_inode);
	if (path_info_make_from_valid(&path_info, path))
		return 0;

	file_handle_info_init_empty(&handle_info);
	if (is_path_filtered(path_info.str.value))
		goto end;

	file_handle_info_make(&handle_info, path);
	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_object_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(path_info)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(handle_info);
	msg = msg_new(FP_SI_OT_SYNC_FILE_PRE_TRUNCATE, subtype, FP_SI_CT_WANT_REPLY, unique_pid, event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();

	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &path_info, path, &handle_info);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->task_info = task_info_get(task_info);
	msg->id = event_uid;
	copy_path_in_msg(msg, path);

end:
	path_info_free(&path_info);
	file_handle_info_free(&handle_info);
	return send_msg_sync_and_get_block_then_relabel_send_async_unref(msg, FP_SI_OT_NOTIFY_FILE_PRE_TRUNCATE, subtype, SI_CT_PRE_CALLBACK);
}

void fs_event_pre_chmod(task_info_t *task_info, const struct path *path, umode_t mode)
{
	static const uint8_t k_chmod_fields[] = { SI_COMMON_FS_FIELDS, SI_COMMON_OBJECT_FILE_FIELDS, FP_SI_PI_ACCESS_MODE };
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t path_info;
	file_handle_info_t handle_info;
	uint64_t subtype = fs_generic_subtype(path->dentry->d_inode);
	if (path_info_make_from_valid(&path_info, path))
		return;

	file_handle_info_init_empty(&handle_info);
	if (is_path_filtered(path_info.str.value))
		goto end;

	file_handle_info_make(&handle_info, path);
	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_chmod_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(path_info)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(handle_info);
	msg = msg_new(FP_SI_OT_NOTIFY_FILE_CHMOD, subtype, SI_CT_PRE_CALLBACK, unique_pid, event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();

	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &path_info, path, &handle_info);
		si_property_writer_write_access_mode(&writer, mode);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->task_info = task_info_get(task_info);
	msg->id = event_uid;

end:
	path_info_free(&path_info);
	file_handle_info_free(&handle_info);
	return send_msg_async_unref(msg);
}

void fs_event_pre_chown(task_info_t *task_info, const struct path *path, uid_t uid, gid_t gid)
{
	static const uint8_t k_chown_fields[] = { SI_COMMON_FS_FIELDS, SI_COMMON_OBJECT_FILE_FIELDS, FP_SI_PI_USER_ID, FP_SI_PI_GROUP_ID };
	uint64_t unique_pid = make_unique_pid(current);
	uint64_t event_uid;
	uint32_t event_size;
	msg_t *msg = NULL;
	path_info_t path_info;
	file_handle_info_t handle_info;
	uint64_t subtype = fs_generic_subtype(path->dentry->d_inode);
	if (path_info_make_from_valid(&path_info, path))
		return;

	file_handle_info_init_empty(&handle_info);
	if (is_path_filtered(path_info.str.value))
		goto end;

	file_handle_info_make(&handle_info, path);
	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_chown_fields)
	           + SI_ESTIMATE_SIZE_PATH_INFO(path_info)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(handle_info);
	msg = msg_new(FP_SI_OT_NOTIFY_FILE_CHOWN, subtype, SI_CT_PRE_CALLBACK, unique_pid, event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();

	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_fsids(&writer);
		si_property_writer_write_object_file_and_volume(&writer, &path_info, path, &handle_info);
		si_property_writer_write_user_id(&writer, uid);
		si_property_writer_write_group_id(&writer, gid);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->task_info = task_info_get(task_info);
	msg->id = event_uid;

end:
	path_info_free(&path_info);
	file_handle_info_free(&handle_info);
	return send_msg_async_unref(msg);
}

static const uint8_t k_fsnotify_fields[] = { SI_COMMON_FS_FIELDS, SI_COMMON_OBJECT_MINI_FILE_FIELDS, FP_SI_PI_FLAGS };

void fs_event_fsnotify(task_info_t *task_info, const file_key_t* key, uint64_t flags, msg_type_t type, uint64_t subtype)
{
	msg_t *msg = NULL;
	const uint32_t event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_fsnotify_fields);
	uint64_t event_uid;
	msg = msg_new(type, subtype, SI_CT_POST_CALLBACK, make_unique_pid(current), event_size);
	if (!msg)
		return;

	event_uid = transport_global_sequence_next();
	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
		si_property_writer_write_object_key(&writer, key);
		si_property_writer_write_flags(&writer, flags);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->id = event_uid;
	msg->task_info = task_info_get(task_info);
	send_msg_async(msg);
	msg_unref(msg);
}

static const uint8_t k_fsnotify_mkdir_fields[] = { SI_COMMON_FS_FIELDS
                                                 , SI_COMMON_OBJECT_FILE_IDS_FIELDS
                                                 , FP_SI_PI_OBJECT_FILE_DENTRY_PTR
                                                 , FP_SI_PI_OBJECT_FILE_DENTRY_NAME_PTR
                                                 , FP_SI_PI_FLAGS };

void fs_event_fsnotify_mkdir(task_info_t *task_info
                           , const struct inode* inode
                           , struct dentry* dentry
                           , const void* dentry_name_ptr
                           , uint64_t flags
                           , msg_type_t type
                           , uint64_t subtype)
{
	msg_t *msg = NULL;
	uint32_t event_size;
	uint64_t event_uid;
	file_handle_info_t handle_info = (file_handle_info_t){};

	// Mind that this path is not real - at best we can use it to extract file handle
	if (dentry) {
		struct path path = (struct path){};
		path.dentry = dentry;
		file_handle_info_make(&handle_info, &path);
	}

	event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_fsnotify_mkdir_fields)
	           + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(handle_info);

	msg = msg_new(type, subtype, SI_CT_POST_CALLBACK, make_unique_pid(current), event_size);
	if (!msg)
		goto end;

	event_uid = transport_global_sequence_next();
	{
		si_property_writer_t writer;
		si_event_writer_init(&writer, &msg->event, event_size);
		si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);

		// This function is a simplified version of si_property_writer_write_object_ids but without 'mnt' handling
		// It affected 'stat' collection primarily that requires proper 'mnt' + 'mount_id' collection
		si_property_writer_write_object_file_attributes(&writer, inode->i_flags);
		si_property_writer_write_object_mini_ids(&writer, inode);
		si_property_writer_write_object_stat(&writer, inode);
		si_property_writer_write_volume(&writer, inode);
		if (handle_info.f_handle)
			si_property_writer_write_object_file_handle(&writer, &handle_info);

		if (dentry)
			si_property_writer_write_object_file_dentry_ptr(&writer, (uint64_t) dentry);

		si_property_writer_write_object_file_dentry_name_ptr(&writer, (uint64_t) dentry_name_ptr);
		si_property_writer_write_flags(&writer, flags);
		si_event_writer_finalize(&msg->event, &writer);
	}

	msg->id = event_uid;
	msg->task_info = task_info_get(task_info);

end:
	file_handle_info_free(&handle_info);
	return send_msg_async_unref(msg);
}

© 2025 Cubjrnet7