mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-27 12:28:14 +00:00
Remove getting opts from descriptors out from scm engine, this stuff is pure criu thing, so make it collect the data. The tricky change here is that parasite code needs memory to keep fd_opts on. The memory is taken from parasite args region, which is now bigger than it used to be. But that's not a big deal, as previously this space was allocated on the parasite stack (!, but with smaller chunks). On the other hand, now we have one memcpy less, as opts are put directly into the destination buffer. travis-ci: success for files: Rework send/recv-fds to be more generic Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
124 lines
3.0 KiB
C
124 lines
3.0 KiB
C
#ifndef __sys
|
|
#error "The __sys macro is required"
|
|
#endif
|
|
|
|
#ifndef __memcpy
|
|
#error "The __memcpy macro is required"
|
|
#endif
|
|
|
|
static void scm_fdset_init_chunk(struct scm_fdset *fdset, int nr_fds,
|
|
void *data, unsigned ch_size)
|
|
{
|
|
struct cmsghdr *cmsg;
|
|
static char dummy;
|
|
|
|
fdset->hdr.msg_controllen = CMSG_LEN(sizeof(int) * nr_fds);
|
|
|
|
cmsg = CMSG_FIRSTHDR(&fdset->hdr);
|
|
cmsg->cmsg_len = fdset->hdr.msg_controllen;
|
|
|
|
if (data) {
|
|
fdset->iov.iov_base = data;
|
|
fdset->iov.iov_len = nr_fds * ch_size;
|
|
} else {
|
|
fdset->iov.iov_base = &dummy;
|
|
fdset->iov.iov_len = 1;
|
|
}
|
|
}
|
|
|
|
static int *scm_fdset_init(struct scm_fdset *fdset, struct sockaddr_un *saddr,
|
|
int saddr_len)
|
|
{
|
|
struct cmsghdr *cmsg;
|
|
|
|
BUILD_BUG_ON(sizeof(fdset->msg_buf) < (CMSG_SPACE(sizeof(int) * CR_SCM_MAX_FD)));
|
|
|
|
fdset->iov.iov_base = (void *)0xdeadbeef;
|
|
|
|
fdset->hdr.msg_iov = &fdset->iov;
|
|
fdset->hdr.msg_iovlen = 1;
|
|
fdset->hdr.msg_name = (struct sockaddr *)saddr;
|
|
fdset->hdr.msg_namelen = saddr_len;
|
|
|
|
fdset->hdr.msg_control = &fdset->msg_buf;
|
|
fdset->hdr.msg_controllen = CMSG_LEN(sizeof(int) * CR_SCM_MAX_FD);
|
|
|
|
cmsg = CMSG_FIRSTHDR(&fdset->hdr);
|
|
cmsg->cmsg_len = fdset->hdr.msg_controllen;
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
|
|
return (int *)CMSG_DATA(cmsg);
|
|
}
|
|
|
|
int send_fds(int sock, struct sockaddr_un *saddr, int len,
|
|
int *fds, int nr_fds, void *data, unsigned ch_size)
|
|
{
|
|
struct scm_fdset fdset;
|
|
int *cmsg_data;
|
|
int i, min_fd, ret;
|
|
|
|
cmsg_data = scm_fdset_init(&fdset, saddr, len);
|
|
for (i = 0; i < nr_fds; i += min_fd) {
|
|
min_fd = min(CR_SCM_MAX_FD, nr_fds - i);
|
|
scm_fdset_init_chunk(&fdset, min_fd, data, ch_size);
|
|
__memcpy(cmsg_data, &fds[i], sizeof(int) * min_fd);
|
|
|
|
ret = __sys(sendmsg)(sock, &fdset.hdr, 0);
|
|
if (ret <= 0)
|
|
return ret ? : -1;
|
|
|
|
if (data)
|
|
data += min_fd * ch_size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int recv_fds(int sock, int *fds, int nr_fds, void *data, unsigned ch_size)
|
|
{
|
|
struct scm_fdset fdset;
|
|
struct cmsghdr *cmsg;
|
|
int *cmsg_data;
|
|
int ret;
|
|
int i, min_fd;
|
|
|
|
cmsg_data = scm_fdset_init(&fdset, NULL, 0);
|
|
for (i = 0; i < nr_fds; i += min_fd) {
|
|
min_fd = min(CR_SCM_MAX_FD, nr_fds - i);
|
|
scm_fdset_init_chunk(&fdset, min_fd, data, ch_size);
|
|
|
|
ret = __sys(recvmsg)(sock, &fdset.hdr, 0);
|
|
if (ret <= 0)
|
|
return ret ? : -1;
|
|
|
|
cmsg = CMSG_FIRSTHDR(&fdset.hdr);
|
|
if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS)
|
|
return -EINVAL;
|
|
if (fdset.hdr.msg_flags & MSG_CTRUNC)
|
|
return -ENFILE;
|
|
|
|
min_fd = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
|
|
/*
|
|
* In case if kernel screwed the recipient, most probably
|
|
* the caller stack frame will be overwriten, just scream
|
|
* and exit.
|
|
*
|
|
* FIXME Need to sanitize util.h to be able to include it
|
|
* into files which do not have glibc and a couple of
|
|
* sys_write_ helpers. Meawhile opencoded BUG_ON here.
|
|
*/
|
|
BUG_ON(min_fd > CR_SCM_MAX_FD);
|
|
|
|
if (unlikely(min_fd <= 0))
|
|
return -1;
|
|
|
|
__memcpy(&fds[i], cmsg_data, sizeof(int) * min_fd);
|
|
if (data)
|
|
data += ch_size * min_fd;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|