Sindbad~EG File Manager

Current Path : /usr/src/file_protector-1.1-1494/
Upload File :
Current File : //usr/src/file_protector-1.1-1494/task_info_map.c

/**
@file
@brief    Process (aka task) info storage
@details  Copyright (c) 2017-2021 Acronis International GmbH
@author   Ivan Matveev (ivan.matveev@acronis.com)
@since    $Id: $
*/

#include "task_info_map.h"

#include "compat.h"
#include "debug.h"
#include "memory.h"

#include <linux/mm.h>   // get_task_exe_file()

typedef struct task_info_map_s {
	spinlock_t spinlock;
	struct rb_root root;

	spinlock_t exited_list_spinlock;
	struct list_head exited_list;
} task_info_map_t;
static task_info_map_t task_info_map;

const char *task_status_to_string(task_status_t status)
{
	switch (status)
	{
#define CASE_TS_RETURN(t) case TS_##t: return #t
	CASE_TS_RETURN(UNKNOWN);
	CASE_TS_RETURN(IGNORE);
	CASE_TS_RETURN(WHITE);
	CASE_TS_RETURN(BLACK);
	CASE_TS_RETURN(GREY);
#undef CASE_TS_RETURN
	default: return "?";
	}
}

static task_info_t *task_info_init(task_info_t *task_info, pid_t pid)
{
	DPRINTF("task_info=%p pid=%i", task_info, pid);
	RB_CLEAR_NODE(&task_info->rb_node);
	task_info->pid = pid;

	atomic_set(&task_info->ref_cnt, 1);
	spin_lock_init(&task_info->spinlock);

	task_info->status = TS_UNKNOWN;
	/*
		struct path {
			struct vfsmount *mnt;
			struct dentry *dentry;
		};
	*/
	task_info->exe_path = (struct path){};

	INIT_LIST_HEAD(&task_info->exited_list_item);

	return task_info;
}

static task_info_t *task_info_new(pid_t pid)
{
	task_info_t *task_info = mem_alloc0(sizeof(task_info_t));
	if (task_info) {
		task_info_init(task_info, pid);
	}
	return task_info;
}

task_info_t *task_info_ref(task_info_t *task_info)
{
	atomic_inc(&task_info->ref_cnt);
	return task_info;
}

static void task_info_free(task_info_t *task_info)
{
	DPRINTF("task_info=%p", task_info);
	// Note: 'path_put(&path={})' is safe
	path_put(&task_info->exe_path);
	mem_free(task_info);
}

void task_info_unref(task_info_t *task_info)
{
	DPRINTF("pid=%d ref_cnt=%d", task_info->pid, atomic_read(&task_info->ref_cnt));
	if (atomic_dec_and_test(&task_info->ref_cnt)) {
		task_info_free(task_info);
	}
}

int task_info_map_init(void)
{
	DPRINTF("");
	spin_lock_init(&task_info_map.spinlock);
	task_info_map.root = RB_ROOT;

	spin_lock_init(&task_info_map.exited_list_spinlock);
	INIT_LIST_HEAD(&task_info_map.exited_list);
	return 0;
}

void task_info_map_down(void)
{
	task_info_map_clear();
}

void task_info_map_clear(void)
{
	struct rb_root root;
	struct rb_node *node;

	DPRINTF("");
	task_info_map_delete_exited();

	spin_lock(&task_info_map.spinlock);

	root = task_info_map.root;
	task_info_map.root = RB_ROOT;

	spin_unlock(&task_info_map.spinlock);

	node = root.rb_node;
	while (node) {
		task_info_t *task_info = rb_entry(node, task_info_t, rb_node);
		rb_erase(&task_info->rb_node, &root);
		task_info_unref(task_info);
		node = root.rb_node;
	}
}

task_info_t *task_info_lookup(pid_t pid)
{
	struct rb_node *node;
	task_info_t *task_info = NULL;

	DPRINTF("pid=%d", pid);
	spin_lock(&task_info_map.spinlock);

	node = task_info_map.root.rb_node;
	while (node) {
		task_info_t *node_task_info = rb_entry(node, task_info_t, rb_node);
		pid_t node_pid = node_task_info->pid;

		if (pid < node_pid) {
			node = node->rb_left;
		} else if (pid > node_pid) {
			node = node->rb_right;
		} else {
			task_info = task_info_ref(node_task_info);
			break;
		}
	}

	spin_unlock(&task_info_map.spinlock);
	DPRINTF_RATELIMITED("task_info=%p pid=%d", task_info, pid);
	return task_info;
}

static task_info_t *task_info_map_insert(task_info_t *new_task_info)
{
	pid_t pid = new_task_info->pid;
	struct rb_node *parent = NULL;
	struct rb_node **link;

	DPRINTF_RATELIMITED("new_task_info=%p pid=%i", new_task_info, pid);
	spin_lock(&task_info_map.spinlock);

	link = &(task_info_map.root.rb_node);
	while (*link) {
		task_info_t *node_task_info;
		pid_t node_pid;

		parent = *link;
		node_task_info = rb_entry(parent, task_info_t, rb_node);
		node_pid = node_task_info->pid;

		if (pid < node_pid) {
			link = &parent->rb_left;
		} else if (pid > node_pid) {
			link = &parent->rb_right;
		} else {
			// collision
			DPRINTF_RATELIMITED("collision");
			task_info_ref(node_task_info);
			spin_unlock(&task_info_map.spinlock);
			return node_task_info;
		}
	}
	// do 'inc' for 'task_info_map.root'
	task_info_ref(new_task_info);
	rb_link_node(&new_task_info->rb_node, parent, link);
	rb_insert_color(&new_task_info->rb_node, &task_info_map.root);
	DPRINTF_RATELIMITED("inserted");

	spin_unlock(&task_info_map.spinlock);
	return new_task_info;
}

// Warning: May block!
task_info_t *task_info_get(pid_t pid)
{
	task_info_t *task_info;
	task_info_t *new_task_info;

	DPRINTF_RATELIMITED("pid=%d", pid);
	task_info = task_info_lookup(pid);
	if (task_info) {
		DPRINTF_RATELIMITED("pid=%i is already in the map (task_info=%p)", pid, task_info);
		return task_info;
	}

	new_task_info = task_info_new(pid);
	if (!new_task_info) {
		DPRINTF_RATELIMITED("out of memory");
		return NULL;
	}

	task_info = task_info_map_insert(new_task_info);
	if (task_info != new_task_info) {
		// collision
		DPRINTF_RATELIMITED("collision");
		task_info_unref(new_task_info);
	}
	return task_info;
}

int task_info_map_del(pid_t pid)
{
	task_info_t *task_info;

	DPRINTF("pid=%d", pid);
	task_info = task_info_lookup(pid);
	if (!task_info) {
		WPRINTF("pid=%d is missing in the map", pid);
		return -ENOENT;
	}

	spin_lock(&task_info_map.spinlock);
	rb_erase(&task_info->rb_node, &task_info_map.root);
	spin_unlock(&task_info_map.spinlock);

	// undo 'inc' done for 'task_info_map.root'
	task_info_unref(task_info);

	// undo 'inc' done in 'task_info_lookup()'
	task_info_unref(task_info);
	DPRINTF("pid=%d", pid);
	return 0;
}

// FIXME: eliminate 'task_info_status_get()'
task_status_t task_info_status_get(pid_t pid)
{
	task_info_t *task_info;
	task_status_t status;

	task_info = task_info_lookup(pid);
	if (!task_info) {
		status = TS_UNKNOWN;
	} else {
		// FIXME: is it really necessary to protect 'task_status_t' with spinlock?
		spin_lock(&task_info->spinlock);
		status = task_info->status;
		spin_unlock(&task_info->spinlock);
		task_info_unref(task_info);
	}
	return status;
}

// Warning: May block!
int task_info_status_set(pid_t pid, task_status_t status)
{
	task_info_t *task_info;
	DPRINTF("pid=%i status=%i/%s", pid, status, task_status_to_string(status));
	task_info = task_info_get(pid);
	if (!task_info) {
		WPRINTF("'%s(pid=%i)' failure", "task_info_get", pid);
		return -ENOMEM;
	}
	// FIXME: is it really necessary to protect 'task_status_t' with spinlock?
	spin_lock(&task_info->spinlock);
	task_info->status = status;
	spin_unlock(&task_info->spinlock);
	task_info_unref(task_info);
	return 0;
}

void task_info_map_on_exit_event(pid_t tgid, pid_t pid)
{
	task_info_t *task_info = task_info_lookup(pid);
	if (!task_info) {
		DPRINTF("%u:%u is missing in 'task_info_map'", tgid, pid);
	} else {
		spin_lock(&task_info_map.exited_list_spinlock);

		if (!list_empty(&task_info->exited_list_item)) {
			WPRINTF("%u:%u is already in 'exited list'", tgid, pid);
		} else {
			// do 'inc' for 'task_info_map.exited_list'
			task_info_ref(task_info);
			list_add_tail(&task_info->exited_list_item, &task_info_map.exited_list);
			DPRINTF("%u:%u task_info=%p is added to 'exited list'", tgid, pid, task_info);
		}

		spin_unlock(&task_info_map.exited_list_spinlock);
		task_info_unref(task_info);
	}
}

void task_info_map_delete_exited(void)
{
	for (;;) {
		task_info_t *task_info;
		pid_t pid;

		spin_lock(&task_info_map.exited_list_spinlock);

		if (list_empty(&task_info_map.exited_list)) {
			spin_unlock(&task_info_map.exited_list_spinlock);
			return;
		}

		task_info = list_entry(task_info_map.exited_list.next, task_info_t, exited_list_item);
		list_del_init(&task_info->exited_list_item);

		spin_unlock(&task_info_map.exited_list_spinlock);

		pid = task_info->pid;
		DPRINTF("deleting task_info=%p with pid=%u", task_info, pid);
		task_info_map_del(pid);
		// undo 'inc' done for 'task_info_map.exited_list'
		task_info_unref(task_info);
	}
}

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists