<commit_before>#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <linux/limits.h>
#include <linux/major.h>

#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>

#include "files.h"
#include "file-ids.h"
#include "files-reg.h"
#include "file-lock.h"
#include "image.h"
#include "list.h"
#include "util.h"
#include "util-pie.h"
#include "lock.h"
#include "sockets.h"
#include "pstree.h"
#include "tty.h"
#include "pipes.h"
#include "fifo.h"
#include "eventfd.h"
#include "eventpoll.h"
#include "fsnotify.h"
#include "signalfd.h"
#include "namespaces.h"
#include "tun.h"
#include "timerfd.h"
#include "imgset.h"
#include "fs-magic.h"
#include "proc_parse.h"
#include "cr_options.h"

#include "parasite.h"
#include "parasite-syscall.h"

#include "protobuf.h"
#include "protobuf/fs.pb-c.h"
#include "protobuf/ext-file.pb-c.h"

#include "plugin.h"

#define FDESC_HASH_SIZE	64
static struct hlist_head file_desc_hash[FDESC_HASH_SIZE];

int prepare_shared_fdinfo(void)
{
	int i;

	for (i = 0; i < FDESC_HASH_SIZE; i++)
		INIT_HLIST_HEAD(&file_desc_hash[i]);

	return 0;
}

void file_desc_init(struct file_desc *d, u32 id, struct file_desc_ops *ops)
{
	INIT_LIST_HEAD(&d->fd_info_head);
	INIT_HLIST_NODE(&d->hash);

	d->id	= id;
	d->ops	= ops;
}

int file_desc_add(struct file_desc *d, u32 id, struct file_desc_ops *ops)
{
	file_desc_init(d, id, ops);
	hlist_add_head(&d->hash, &file_desc_hash[id % FDESC_HASH_SIZE]);
	return 0; /* this is to make tail-calls in collect_one_foo look nice */
}

struct file_desc *find_file_desc_raw(int type, u32 id)
{
	struct file_desc *d;
	struct hlist_head *chain;

	chain = &file_desc_hash[id % FDESC_HASH_SIZE];
	hlist_for_each_entry(d, chain, hash)
		if (d->ops->type == type && d->id == id)
			return d;

	return NULL;
}

static inline struct file_desc *find_file_desc(FdinfoEntry *fe)
{
	return find_file_desc_raw(fe->type, fe->id);
}

/*
 * A file may be shared between several file descriptors. E.g
 * when doing a fork() every fd of a forker and respective fds
 * of the child have such. Another way of getting shared files
 * is by dup()-ing them or sending them via unix sockets in
 * SCM_RIGHTS message.
 *
 * We restore this type of things in 3 steps (states[] below)
 *
 * 1. Prepare step.
 *    Select which task will create the file (open() one, or
 *    call any other syscall for than (socket, pipe, etc.). All
 *    the others, that share one, create unix sockets under the
 *    respective file descriptor (transport socket).
 * 2. Open step.
 *    The one who creates the file (the 'master') creates one,
 *    then creates one more unix socket (transport) and sends the
 *    created file over this socket to the other recepients.
 * 3. Receive step.
 *    Those, who wait for the file to appear, receive one via
 *    the transport socket, then close the socket and dup() the
 *    received file descriptor into its place.
 *
 * There's the 4th step in the states[] array -- the post_open
 * one. This one is not about file-sharing resolving, but about
 * doing something with a file using it's 'desired' fd. The
 * thing is that while going the 3-step process above, the file
 * may appear in variuos places in the task's fd table, and if
 * we want to do something with it's _final_ descriptor value,
 * we should wait for it to appear there. So the post_open is
 * called when the file is finally set into its place.
 */

struct fdinfo_list_entry *file_master(struct file_desc *d)
{
	if (list_empty(&d->fd_info_head)) {
		pr_err("Empty list on file desc id %#x(%d)\n", d->id,
				d->ops ? d->ops->type : -1);
		BUG();
	}

	return list_first_entry(&d->fd_info_head,
			struct fdinfo_list_entry, desc_list);
}

void show_saved_files(void)
{
	int i;
	struct file_desc *fd;

	pr_info("File descs:\n");
	for (i = 0; i < FDESC_HASH_SIZE; i++)
		hlist_for_each_entry(fd, &file_desc_hash[i], hash) {
			struct fdinfo_list_entry *le;

			pr_info(" `- type %d ID %#x\n", fd->ops->type, fd->id);
			list_for_each_entry(le, &fd->fd_info_head, desc_list)
				pr_info("   `- FD %d pid %d\n", le->fe->fd, le->pid);
		}
}

/*
 * The gen_id thing is used to optimize the comparison of shared files.
 * If two files have different gen_ids, then they are different for sure.
 * If it matches, we don't know it and have to call sys_kcmp().
 *
 * The kcmp-ids.c engine does this trick, see comments in it for more info.
 */

static u32 make_gen_id(const struct fd_parms *p)
{
	return ((u32)p->stat.st_dev) ^ ((u32)p->stat.st_ino) ^ ((u32)p->pos);
}

int do_dump_gen_file(struct fd_parms *p, int lfd,
		const struct fdtype_ops *ops, struct cr_img *img)
{
	FdinfoEntry e = FDINFO_ENTRY__INIT;
	int ret = -1;

	e.type	= ops->type;
	e.id	= make_gen_id(p);
	e.fd	= p->fd;
	e.flags = p->fd_flags;

	ret = fd_id_generate(p->pid, &e, p);
	if (ret == 1) /* new ID generated */
		ret = ops->dump(lfd, e.id, p);

	if (ret < 0)
		return ret;

	pr_info("fdinfo: type: 0x%2x flags: %#o/%#o pos: 0x%8"PRIx64" fd: %d\n",
		ops->type, p->flags, (int)p->fd_flags, p->pos, p->fd);

	return pb_write_one(img, &e, PB_FDINFO);
}

int fill_fdlink(int lfd, const struct fd_parms *p, struct fd_link *link)
{
	int len;

	link->name[0] = '.';

	len = read_fd_link(lfd, &link->name[1], sizeof(link->name) - 1);
	if (len < 0) {
		pr_err("Can't read link for pid %d fd %d\n", p->pid, p->fd);
		return -1;
	}

	link->len = len + 1;
	return 0;
}

static int fill_fd_params(struct parasite_ctl *ctl, int fd, int lfd,
				struct fd_opts *opts, struct fd_parms *p)
{
	int ret;
	struct statfs fsbuf;
	struct fdinfo_common fdinfo = { .mnt_id = -1, .owner = ctl->pid.virt };

	if (fstat(lfd, &p->stat) < 0) {
		pr_perror("Can't stat fd %d", lfd);
		return -1;
	}

	if (fstatfs(lfd, &fsbuf) < 0) {
		pr_perror("Can't statfs fd %d", lfd);
		return -1;
	}

	if (parse_fdinfo_pid(ctl->pid.real, fd, FD_TYPES__UND, NULL, &fdinfo))
		return -1;

	p->fs_type	= fsbuf.f_type;
	p->ctl		= ctl;
	p->fd		= fd;
	p->pos		= fdinfo.pos;
	p->flags	= fdinfo.flags;
	p->mnt_id	= fdinfo.mnt_id;
	p->pid		= ctl->pid.real;
	p->fd_flags	= opts->flags;

	fown_entry__init(&p->fown);

	pr_info("%d fdinfo %d: pos: 0x%16"PRIx64" flags: %16o/%#x\n",
		ctl->pid.real, fd, p->pos, p->flags, (int)p->fd_flags);

	ret = fcntl(lfd, F_GETSIG, 0);
	if (ret < 0) {
		pr_perror("Can't get owner signum on %d", lfd);
		return -1;
	}
	p->fown.signum = ret;

	if (opts->fown.pid == 0)
		return 0;

	p->fown.pid	 = opts->fown.pid;
	p->fown.pid_type = opts->fown.pid_type;
	p->fown.uid	 = opts->fown.uid;
	p->fown.euid	 = opts->fown.euid;

	return 0;
}

static const struct fdtype_ops *get_misc_dev_ops(int minor)
{
	switch (minor) {
	case TUN_MINOR:
		return &tunfile_dump_ops;
	};

	return NULL;
}

static int dump_chrdev(struct fd_parms *p, int lfd, struct cr_img *img)
{
	int maj = major(p->stat.st_rdev);
	const struct fdtype_ops *ops;

	switch (maj) {
	case MEM_MAJOR:
		ops = &regfile_dump_ops;
		break;
	case MISC_MAJOR:
		ops = get_misc_dev_ops(minor(p->stat.st_rdev));<commit_msg>files: Adjust file position for /dev/kmsg<commit_after>#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <linux/limits.h>
#include <linux/major.h>

#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>

#include "files.h"
#include "file-ids.h"
#include "files-reg.h"
#include "file-lock.h"
#include "image.h"
#include "list.h"
#include "util.h"
#include "util-pie.h"
#include "lock.h"
#include "sockets.h"
#include "pstree.h"
#include "tty.h"
#include "pipes.h"
#include "fifo.h"
#include "eventfd.h"
#include "eventpoll.h"
#include "fsnotify.h"
#include "signalfd.h"
#include "namespaces.h"
#include "tun.h"
#include "timerfd.h"
#include "imgset.h"
#include "fs-magic.h"
#include "proc_parse.h"
#include "cr_options.h"

#include "parasite.h"
#include "parasite-syscall.h"

#include "protobuf.h"
#include "protobuf/fs.pb-c.h"
#include "protobuf/ext-file.pb-c.h"

#include "plugin.h"

#define FDESC_HASH_SIZE	64
static struct hlist_head file_desc_hash[FDESC_HASH_SIZE];

int prepare_shared_fdinfo(void)
{
	int i;

	for (i = 0; i < FDESC_HASH_SIZE; i++)
		INIT_HLIST_HEAD(&file_desc_hash[i]);

	return 0;
}

void file_desc_init(struct file_desc *d, u32 id, struct file_desc_ops *ops)
{
	INIT_LIST_HEAD(&d->fd_info_head);
	INIT_HLIST_NODE(&d->hash);

	d->id	= id;
	d->ops	= ops;
}

int file_desc_add(struct file_desc *d, u32 id, struct file_desc_ops *ops)
{
	file_desc_init(d, id, ops);
	hlist_add_head(&d->hash, &file_desc_hash[id % FDESC_HASH_SIZE]);
	return 0; /* this is to make tail-calls in collect_one_foo look nice */
}

struct file_desc *find_file_desc_raw(int type, u32 id)
{
	struct file_desc *d;
	struct hlist_head *chain;

	chain = &file_desc_hash[id % FDESC_HASH_SIZE];
	hlist_for_each_entry(d, chain, hash)
		if (d->ops->type == type && d->id == id)
			return d;

	return NULL;
}

static inline struct file_desc *find_file_desc(FdinfoEntry *fe)
{
	return find_file_desc_raw(fe->type, fe->id);
}

/*
 * A file may be shared between several file descriptors. E.g
 * when doing a fork() every fd of a forker and respective fds
 * of the child have such. Another way of getting shared files
 * is by dup()-ing them or sending them via unix sockets in
 * SCM_RIGHTS message.
 *
 * We restore this type of things in 3 steps (states[] below)
 *
 * 1. Prepare step.
 *    Select which task will create the file (open() one, or
 *    call any other syscall for than (socket, pipe, etc.). All
 *    the others, that share one, create unix sockets under the
 *    respective file descriptor (transport socket).
 * 2. Open step.
 *    The one who creates the file (the 'master') creates one,
 *    then creates one more unix socket (transport) and sends the
 *    created file over this socket to the other recepients.
 * 3. Receive step.
 *    Those, who wait for the file to appear, receive one via
 *    the transport socket, then close the socket and dup() the
 *    received file descriptor into its place.
 *
 * There's the 4th step in the states[] array -- the post_open
 * one. This one is not about file-sharing resolving, but about
 * doing something with a file using it's 'desired' fd. The
 * thing is that while going the 3-step process above, the file
 * may appear in variuos places in the task's fd table, and if
 * we want to do something with it's _final_ descriptor value,
 * we should wait for it to appear there. So the post_open is
 * called when the file is finally set into its place.
 */

struct fdinfo_list_entry *file_master(struct file_desc *d)
{
	if (list_empty(&d->fd_info_head)) {
		pr_err("Empty list on file desc id %#x(%d)\n", d->id,
				d->ops ? d->ops->type : -1);
		BUG();
	}

	return list_first_entry(&d->fd_info_head,
			struct fdinfo_list_entry, desc_list);
}

void show_saved_files(void)
{
	int i;
	struct file_desc *fd;

	pr_info("File descs:\n");
	for (i = 0; i < FDESC_HASH_SIZE; i++)
		hlist_for_each_entry(fd, &file_desc_hash[i], hash) {
			struct fdinfo_list_entry *le;

			pr_info(" `- type %d ID %#x\n", fd->ops->type, fd->id);
			list_for_each_entry(le, &fd->fd_info_head, desc_list)
				pr_info("   `- FD %d pid %d\n", le->fe->fd, le->pid);
		}
}

/*
 * The gen_id thing is used to optimize the comparison of shared files.
 * If two files have different gen_ids, then they are different for sure.
 * If it matches, we don't know it and have to call sys_kcmp().
 *
 * The kcmp-ids.c engine does this trick, see comments in it for more info.
 */

static u32 make_gen_id(const struct fd_parms *p)
{
	return ((u32)p->stat.st_dev) ^ ((u32)p->stat.st_ino) ^ ((u32)p->pos);
}

int do_dump_gen_file(struct fd_parms *p, int lfd,
		const struct fdtype_ops *ops, struct cr_img *img)
{
	FdinfoEntry e = FDINFO_ENTRY__INIT;
	int ret = -1;

	e.type	= ops->type;
	e.id	= make_gen_id(p);
	e.fd	= p->fd;
	e.flags = p->fd_flags;

	ret = fd_id_generate(p->pid, &e, p);
	if (ret == 1) /* new ID generated */
		ret = ops->dump(lfd, e.id, p);

	if (ret < 0)
		return ret;

	pr_info("fdinfo: type: 0x%2x flags: %#o/%#o pos: 0x%8"PRIx64" fd: %d\n",
		ops->type, p->flags, (int)p->fd_flags, p->pos, p->fd);

	return pb_write_one(img, &e, PB_FDINFO);
}

int fill_fdlink(int lfd, const struct fd_parms *p, struct fd_link *link)
{
	int len;

	link->name[0] = '.';

	len = read_fd_link(lfd, &link->name[1], sizeof(link->name) - 1);
	if (len < 0) {
		pr_err("Can't read link for pid %d fd %d\n", p->pid, p->fd);
		return -1;
	}

	link->len = len + 1;
	return 0;
}

static int fill_fd_params(struct parasite_ctl *ctl, int fd, int lfd,
				struct fd_opts *opts, struct fd_parms *p)
{
	int ret;
	struct statfs fsbuf;
	struct fdinfo_common fdinfo = { .mnt_id = -1, .owner = ctl->pid.virt };

	if (fstat(lfd, &p->stat) < 0) {
		pr_perror("Can't stat fd %d", lfd);
		return -1;
	}

	if (fstatfs(lfd, &fsbuf) < 0) {
		pr_perror("Can't statfs fd %d", lfd);
		return -1;
	}

	if (parse_fdinfo_pid(ctl->pid.real, fd, FD_TYPES__UND, NULL, &fdinfo))
		return -1;

	p->fs_type	= fsbuf.f_type;
	p->ctl		= ctl;
	p->fd		= fd;
	p->pos		= fdinfo.pos;
	p->flags	= fdinfo.flags;
	p->mnt_id	= fdinfo.mnt_id;
	p->pid		= ctl->pid.real;
	p->fd_flags	= opts->flags;

	fown_entry__init(&p->fown);

	pr_info("%d fdinfo %d: pos: 0x%16"PRIx64" flags: %16o/%#x\n",
		ctl->pid.real, fd, p->pos, p->flags, (int)p->fd_flags);

	ret = fcntl(lfd, F_GETSIG, 0);
	if (ret < 0) {
		pr_perror("Can't get owner signum on %d", lfd);
		return -1;
	}
	p->fown.signum = ret;

	if (opts->fown.pid == 0)
		return 0;

	p->fown.pid	 = opts->fown.pid;
	p->fown.pid_type = opts->fown.pid_type;
	p->fown.uid	 = opts->fown.uid;
	p->fown.euid	 = opts->fown.euid;

	return 0;
}

static const struct fdtype_ops *get_misc_dev_ops(int minor)
{
	switch (minor) {
	case TUN_MINOR:
		return &tunfile_dump_ops;
	};

	return NULL;
}

static const struct fdtype_ops *get_mem_dev_ops(struct fd_parms *p, int minor)
{
	const struct fdtype_ops *ops = NULL;

	switch (minor) {
	case 11:
		/*
		 * If /dev/kmsg is opened in write-only mode the file position
		 * should not be set up upon restore, kernel doesn't allow that.
		 */
		if ((p->flags & O_ACCMODE) == O_WRONLY && p->pos == 0)
			p->pos = -1ULL;
		/*
		 * Fallthrough.
		 */
	default:
		ops = &regfile_dump_ops;
		break;
	};

	return ops;
}

static int dump_chrdev(struct fd_parms *p, int lfd, struct cr_img *img)
{
	int maj = major(p->stat.st_rdev);
	const struct fdtype_ops *ops;

	switch (maj) {
	case MEM_MAJOR:
		ops = get_mem_dev_ops(p, minor(p->stat.st_rdev));
		break;
	case MISC_MAJOR:
		ops = get_misc_dev_ops(minor(p->stat.st_rdev));
{'fraction_non_alphanumeric': 0.1486745012298442, 'fraction_numerical': 0.008745558895873189, 'mean_word_length': 3.7943006878480183, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 175, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '838129', 'n_tokens_mistral': 6216, 'n_tokens_neox': 5931, 'n_words': 1677}