[v2,12/15] unix: Send senders fds of packets in receive queue to receiver

Submitted by Kirill Tkhai on May 27, 2016, 1:07 p.m.

Details

Message ID 146435445582.31234.17012768676111457029.stgit@pro
State Rejected
Series "Support for packet's msg_name in receive queue of promiscous DGRAM sockets"
Headers show

Commit Message

Kirill Tkhai May 27, 2016, 1:07 p.m.
Promiscuous receive queues should be restored by the queue owner,
since there is no a existing queuer for it.

This patch makes a socket, who have sent a packet to a promiscous
queue, send its file descriptor to the queue's owner. Promiscous
sockets, who want its fd, after previous patches are already linked
in unix_sk_info::receivers list.

Promiscous queue's owner receive the fds in receive_unix_sk() and
store them in unix_sk_info::foreign_fds massive.

To receive a sender, transport socket must be dupped. We do that
to free a fd, which is used for real socket we open.

Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
---
 criu/sk-unix.c |  135 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 128 insertions(+), 7 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/sk-unix.c b/criu/sk-unix.c
index fc3cb44..c14c8c4 100644
--- a/criu/sk-unix.c
+++ b/criu/sk-unix.c
@@ -833,7 +833,10 @@  int fix_external_unix_sockets(void)
 struct unix_sk_info {
 	UnixSkEntry *ue;
 	struct list_head list;
-	struct list_head receivers;
+	union {
+		struct list_head receivers;
+		int *foreign_fds;
+	};
 	char *name;
 	char *name_dir;
 	unsigned flags;
@@ -855,6 +858,7 @@  struct unix_sk_info {
 	u32 queuer;
 
 	u32 nr_senders;
+	int transport_fd;
 };
 
 #define USK_PAIR_MASTER		0x1
@@ -1052,7 +1056,7 @@  static int unixsk_should_open_transport(FdinfoEntry *fe,
 	struct unix_sk_info *ui;
 
 	ui = container_of(d, struct unix_sk_info, d);
-	return ui->flags & USK_PAIR_SLAVE;
+	return (ui->flags & USK_PAIR_SLAVE) || ui->nr_senders;
 }
 
 static int open_unixsk_pair_master(struct unix_sk_info *ui)
@@ -1142,6 +1146,8 @@  static int open_unixsk_pair_slave(struct unix_sk_info *ui)
 
 static int open_unixsk_standalone(struct unix_sk_info *ui)
 {
+	struct fdinfo_list_entry *fle;
+	FdinfoEntry *fe;
 	int sk;
 
 	pr_info("Opening standalone socket (id %#x ino %#x peer %#x)\n",
@@ -1197,7 +1203,7 @@  static int open_unixsk_standalone(struct unix_sk_info *ui)
 
 		close(sks[1]);
 		sk = sks[0];
-	} else if (ui->ue->type == SOCK_DGRAM && !ui->queuer) {
+	} else if (ui->ue->type == SOCK_DGRAM && !ui->queuer && !ui->nr_senders) {
 		struct sockaddr_un addr;
 		int sks[2];
 
@@ -1276,12 +1282,79 @@  static int open_unixsk_standalone(struct unix_sk_info *ui)
 	if (restore_socket_opts(sk, ui->ue->opts))
 		return -1;
 
+	if (ui->nr_senders) {
+		fle = xzalloc(sizeof(*fle));
+		fe = xzalloc(sizeof(*fe));
+		fle->fe = fe;
+
+		ui->transport_fd = find_unused_fd(&rsti(current)->used, -1);
+		fe->fd = ui->transport_fd;
+		pr_info("Found unused fd=%d for transport\n", ui->transport_fd);
+		collect_used_fd(fle, rsti(current));
+
+		if (ui->transport_fd == sk) {
+			sk = dup(sk);
+			if (sk < 0) {
+				pr_err("Can't dup\n");
+				return -1;
+			}
+		}
+
+		fle = file_master(&ui->d);
+		ui->transport_fd = dup2(fle->fe->fd, ui->transport_fd);
+		if (ui->transport_fd < 0) {
+			pr_err("Can't dup\n");
+			return -1;
+		}
+		close(fle->fe->fd);
+	}
+
 	return sk;
 }
 
+static int send_sk_to_receivers(int sk, struct unix_sk_info *ui)
+{
+	struct sk_ino *si, *tmp;
+	int tsk, ret = 0;
+
+	tsk = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (tsk < 0) {
+		pr_perror("Can't make transport socket");
+		return -1;
+	}
+
+	list_for_each_entry_safe(si, tmp, &ui->receivers, list) {
+		struct unix_sk_info *receiver;
+		struct fdinfo_list_entry *fle;
+
+		pr_info("Sending fd=%d to ino=%d\n", sk, si->ino);
+
+		receiver = find_unix_sk_by_ino(si->ino);
+		if (!receiver) {
+			pr_err("Can't find receiver: ino=%u", si->ino);
+			ret = -1;
+			break;
+		}
+
+		fle = file_master(&receiver->d);
+
+		if (send_fd_to_peer(sk, fle, tsk)) {
+			pr_err("Can't send sk to receiver\n");
+			ret = -1;
+			break;
+		}
+		list_del(&si->list);
+	}
+
+	close(tsk);
+
+	return ret;
+}
+
 static int open_unix_sk(struct file_desc *d)
 {
 	struct unix_sk_info *ui;
+	int sk, err = 0;
 
 	ui = container_of(d, struct unix_sk_info, d);
 
@@ -1289,13 +1362,60 @@  static int open_unix_sk(struct file_desc *d)
 
 	if (inherited_fd(d, &unixsk_fd)) {
 		ui->ue->uflags |= USK_INHERIT;
-		return unixsk_fd;
+		sk = unixsk_fd;
 	} else if (ui->flags & USK_PAIR_MASTER)
-		return open_unixsk_pair_master(ui);
+		sk =  open_unixsk_pair_master(ui);
 	else if (ui->flags & USK_PAIR_SLAVE)
-		return open_unixsk_pair_slave(ui);
+		sk = open_unixsk_pair_slave(ui);
 	else
-		return open_unixsk_standalone(ui);
+		sk = open_unixsk_standalone(ui);
+
+	if (sk >= 0 && !list_empty(&ui->receivers))
+		err = send_sk_to_receivers(sk, ui);
+	if (err < 0) {
+		close(sk);
+		sk = -1;
+	}
+
+	return sk;
+}
+
+static int receive_unix_sk(struct file_desc *d, int fd)
+{
+	struct fdinfo_list_entry *fle;
+	struct unix_sk_info *ui;
+	int i, sk, ret = 0;
+
+	ui = container_of(d, struct unix_sk_info, d);
+	if (!ui->nr_senders)
+		return 0;
+
+	BUG_ON(ui->ue->type != SOCK_DGRAM);
+	BUG_ON(!list_empty(&ui->receivers));
+
+	ui->foreign_fds = xmalloc(ui->nr_senders * sizeof(int));
+	if (!ui->foreign_fds)
+		return -1;
+
+	for (i = 0; i < ui->nr_senders; i++) {
+		sk = recv_fd(ui->transport_fd);
+		if (sk < 0) {
+			pr_err("Can't recv fd\n");
+			ret = -1;
+			break;
+		}
+
+		pr_info("Received foreign fd=%d\n", sk);
+		ui->foreign_fds[i] = sk;
+	}
+
+	fle = find_used_fd(&rsti(current)->used, ui->transport_fd);
+	list_del(&fle->used_list);
+	xfree(fle->fe);
+	xfree(fle);
+	close(ui->transport_fd);
+
+	return ret;
 }
 
 static char *socket_d_name(struct file_desc *d, char *buf, size_t s)
@@ -1316,6 +1436,7 @@  static char *socket_d_name(struct file_desc *d, char *buf, size_t s)
 static struct file_desc_ops unix_desc_ops = {
 	.type = FD_TYPES__UNIXSK,
 	.open = open_unix_sk,
+	.receive = receive_unix_sk,
 	.post_open = post_open_unix_sk,
 	.want_transport = unixsk_should_open_transport,
 	.name = socket_d_name,