Welcome to mirror list, hosted at ThFree Co, Russian Federation.

fdstore.c « criu - github.com/checkpoint-restore/criu.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 03afa9f1783ab5e3bfe5fb5e27b246b5c8e22612 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

#include "common/scm.h"
#include "common/lock.h"
#include "servicefd.h"
#include "fdstore.h"
#include "xmalloc.h"
#include "rst-malloc.h"
#include "log.h"
#include "util.h"
#include "cr_options.h"
#include "util-caps.h"

/* clang-format off */
static struct fdstore_desc {
	int next_id;
	mutex_t lock; /* to protect a peek offset */
} *desc;
/* clang-format on */

int fdstore_init(void)
{
	/* In kernel a bufsize has type int and a value is doubled. */
	uint32_t buf[2] = { INT_MAX / 2, INT_MAX / 2 };
	struct sockaddr_un addr;
	unsigned int addrlen;
	int rcv_opt_name;
	int snd_opt_name;
	struct stat st;
	int sk, ret;

	desc = shmalloc(sizeof(*desc));
	if (!desc)
		return -1;

	desc->next_id = 0;
	mutex_init(&desc->lock);

	sk = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
	if (sk < 0) {
		pr_perror("Unable to create a socket");
		return -1;
	}

	if (fstat(sk, &st)) {
		pr_perror("Unable to stat a file descriptor");
		close(sk);
		return -1;
	}

	if (!opts.unprivileged || has_cap_net_admin(opts.cap_eff)) {
		rcv_opt_name = SO_RCVBUFFORCE;
		snd_opt_name = SO_SNDBUFFORCE;
	} else {
		rcv_opt_name = SO_RCVBUF;
		snd_opt_name = SO_SNDBUF;
	}

	if (setsockopt(sk, SOL_SOCKET, snd_opt_name, &buf[0], sizeof(buf[0])) < 0 ||
	    setsockopt(sk, SOL_SOCKET, rcv_opt_name, &buf[1], sizeof(buf[1])) < 0) {
		pr_perror("Unable to set SO_SNDBUFFORCE/SO_RCVBUFFORCE");
		close(sk);
		return -1;
	}

	addr.sun_family = AF_UNIX;
	addrlen = snprintf(addr.sun_path, sizeof(addr.sun_path), "X/criu-fdstore-%" PRIx64 "-%" PRIx64, st.st_ino,
			   criu_run_id);
	addrlen += sizeof(addr.sun_family);

	addr.sun_path[0] = 0;

	/*
	 * This socket is connected to itself, so all messages are queued to
	 * its receive queue. Here we are going to use this socket to store
	 * file descriptors. For that we need to send a file descriptor in
	 * a queue and remember its sequence number. Then we can set SO_PEEK_OFF
	 * to get a file descriptor without dequeuing it.
	 */
	if (bind(sk, (struct sockaddr *)&addr, addrlen)) {
		pr_perror("Unable to bind a socket");
		close(sk);
		return -1;
	}
	if (connect(sk, (struct sockaddr *)&addr, addrlen)) {
		pr_perror("Unable to connect a socket");
		close(sk);
		return -1;
	}

	ret = install_service_fd(FDSTORE_SK_OFF, sk);
	if (ret < 0)
		return -1;

	return 0;
}

int fdstore_add(int fd)
{
	int sk = get_service_fd(FDSTORE_SK_OFF);
	int id, ret;

	mutex_lock(&desc->lock);

	ret = send_fd(sk, NULL, 0, fd);
	if (ret) {
		pr_perror("Can't send fd %d into store", fd);
		mutex_unlock(&desc->lock);
		return -1;
	}

	id = desc->next_id++;

	mutex_unlock(&desc->lock);

	return id;
}

int fdstore_get(int id)
{
	int sk, fd;

	sk = get_service_fd(FDSTORE_SK_OFF);
	if (sk < 0) {
		pr_err("Cannot get FDSTORE_SK_OFF fd\n");
		return -1;
	}

	mutex_lock(&desc->lock);
	if (setsockopt(sk, SOL_SOCKET, SO_PEEK_OFF, &id, sizeof(id))) {
		mutex_unlock(&desc->lock);
		pr_perror("Unable to a peek offset");
		return -1;
	}

	if (__recv_fds(sk, &fd, 1, NULL, 0, MSG_PEEK) < 0) {
		mutex_unlock(&desc->lock);
		pr_perror("Unable to get a file descriptor with the %d id", id);
		return -1;
	}
	mutex_unlock(&desc->lock);

	return fd;
}