/**
@file
@brief File system events messages
@details Copyright (c) 2017-2021 Acronis International GmbH
@author Mikhail Krivtsov (mikhail.krivtsov@acronis.com)
@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_fsids(si_property_writer_t *writer) {
si_property_writer_write_fsuid(writer, get_current_fsuid_compat());
si_property_writer_write_fsgid(writer, get_current_fsgid_compat());
}
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);
}
|