#ifndef __CR_FILES_H__
#define __CR_FILES_H__

#include <sys/stat.h>

#include "int.h"
#include "common/compiler.h"
#include "fcntl.h"
#include "common/lock.h"
#include "common/list.h"
#include "pid.h"
#include "rst_info.h"

#include "images/fdinfo.pb-c.h"
#include "images/fown.pb-c.h"
#include "images/vma.pb-c.h"

struct parasite_drain_fd;
struct pstree_item;
struct file_desc;
struct cr_imgset;
struct rst_info;
struct parasite_ctl;

struct fd_link {
	union {
		/* Link info for generic file (path) */
		struct {
			char name[PATH_MAX];
			size_t len;
		};

		/* Link info for proc-ns file */
		struct {
			struct ns_desc *ns_d;
			unsigned int ns_kid;
		};
	};
};

struct fd_parms {
	int fd;
	off_t pos;
	unsigned int flags;
	char fd_flags;
	struct stat stat;
	pid_t pid;
	FownEntry fown;
	struct fd_link *link;
	long fs_type;
	int mnt_id;

	struct parasite_ctl *fd_ctl;
	struct parasite_drain_fd *dfds;
};

#define FD_PARMS_INIT                                                                        \
	(struct fd_parms)                                                                    \
	{                                                                                    \
		.fd = FD_DESC_INVALID, .fown = FOWN_ENTRY__INIT, .link = NULL, .mnt_id = -1, \
	}

extern int fill_fdlink(int lfd, const struct fd_parms *p, struct fd_link *link);
extern uint32_t make_gen_id(uint32_t st_dev, uint32_t st_ino, uint64_t pos);

struct file_desc;

enum {
	FLE_INITIALIZED,
	/*
	 * FLE is open (via open() or socket() or etc syscalls), and
	 * common file setting are set up (type-specific are not yet).
	 * Most possible, the master was already served out.
	 */
	FLE_OPEN,
	/*
	 * File-type specific settings and preparations are finished,
	 * and FLE is completely restored.
	 */
	FLE_RESTORED,
};

struct fdinfo_list_entry {
	struct list_head desc_list; /* To chain on  @fd_info_head */
	struct file_desc *desc;	    /* Associated file descriptor */
	struct list_head ps_list;   /* To chain  per-task files */
	struct pstree_item *task;
	FdinfoEntry *fe;
	int pid;
	u8 received : 1;
	u8 stage : 3;
	u8 fake : 1;
};

extern int inh_fd_max;

/* reports whether fd_a takes prio over fd_b */
static inline int fdinfo_rst_prio(struct fdinfo_list_entry *fd_a, struct fdinfo_list_entry *fd_b)
{
	return pid_rst_prio(fd_a->pid, fd_b->pid) || ((fd_a->pid == fd_b->pid) && (fd_a->fe->fd < fd_b->fe->fd));
}

struct file_desc_ops {
	/* fd_types from images/fdinfo.proto */
	unsigned int type;
	/*
	 * Opens a file by whatever syscall is required for that.
	 * The returned descriptor may be closed (dup2-ed to another)
	 * so it shouldn't be saved for any post-actions.
	 */
	int (*open)(struct file_desc *d, int *new_fd);
	char *(*name)(struct file_desc *, char *b, size_t s);
};

int collect_fd(int pid, FdinfoEntry *e, struct rst_info *rst_info, bool ghost);
struct fdinfo_list_entry *collect_fd_to(int pid, FdinfoEntry *e, struct rst_info *rst_info, struct file_desc *fdesc,
					bool fake, bool force_master);

u32 find_unused_file_desc_id(void);
unsigned int find_unused_fd(struct pstree_item *, int hint_fd);
struct fdinfo_list_entry *find_used_fd(struct pstree_item *, int fd);

struct file_desc {
	u32 id;				   /* File id, unique */
	struct hlist_node hash;		   /* Descriptor hashing and lookup */
	struct list_head fd_info_head;	   /* Chain of fdinfo_list_entry-s with same ID and type but different pids */
	struct file_desc_ops *ops;	   /* Associated operations */
	struct list_head fake_master_list; /* To chain in the list of file_desc, which don't
					    * have a fle in a task, that having permissions */
};

struct fdtype_ops {
	unsigned int type;
	int (*dump)(int lfd, u32 id, const struct fd_parms *p);
	int (*pre_dump)(int pid, int lfd);
};

struct cr_img;

extern int dump_my_file(int lfd, u32 *, int *type);
extern int do_dump_gen_file(struct fd_parms *p, int lfd, const struct fdtype_ops *ops, FdinfoEntry *e);
struct parasite_drain_fd;
int dump_task_files_seized(struct parasite_ctl *ctl, struct pstree_item *item, struct parasite_drain_fd *dfds);
int predump_task_files(int pid);

extern void file_desc_init(struct file_desc *d, u32 id, struct file_desc_ops *ops);
extern int file_desc_add(struct file_desc *d, u32 id, struct file_desc_ops *ops);
extern struct fdinfo_list_entry *try_file_master(struct file_desc *d);
extern struct fdinfo_list_entry *file_master(struct file_desc *d);
extern struct file_desc *find_file_desc_raw(int type, u32 id);

extern int setup_and_serve_out(struct fdinfo_list_entry *fle, int new_fd);
extern int recv_desc_from_peer(struct file_desc *d, int *fd);
extern int send_desc_to_peer(int fd, struct file_desc *d);
extern int restore_fown(int fd, FownEntry *fown);
extern int rst_file_params(int fd, FownEntry *fown, int flags);

extern void show_saved_files(void);

extern int prepare_fds(struct pstree_item *me);
extern int prepare_fd_pid(struct pstree_item *me);
extern int prepare_files(void);
extern int restore_fs(struct pstree_item *);
extern int prepare_fs_pid(struct pstree_item *);
extern int set_fd_flags(int fd, int flags);

extern struct collect_image_info files_cinfo;
#define files_collected() (files_cinfo.flags & COLLECT_HAPPENED)

extern int close_old_fds(void);
#ifndef AT_EMPTY_PATH
#define AT_EMPTY_PATH 0x1000
#endif

#define LREMAP_PARAM "link-remap"

extern int shared_fdt_prepare(struct pstree_item *item);

extern struct collect_image_info ext_file_cinfo;
extern int dump_unsupp_fd(struct fd_parms *p, int lfd, char *more, char *info, FdinfoEntry *);

extern int inherit_fd_parse(char *optarg);
extern int inherit_fd_add(int fd, char *key);
extern void inherit_fd_log(void);
extern int inherit_fd_move_to_fdstore(void);

extern int inherit_fd_lookup_id(char *id);

extern bool inherited_fd(struct file_desc *, int *fdp);

extern FdinfoEntry *dup_fdinfo(FdinfoEntry *old, int fd, unsigned flags);
int dup_fle(struct pstree_item *task, struct fdinfo_list_entry *ple, int fd, unsigned flags);

extern int open_transport_socket(void);
extern int set_fds_event(pid_t virt);
extern void wait_fds_event(void);

int find_unused_fd_pid(pid_t pid);
#endif /* __CR_FILES_H__ */
