예제 #1
0
int rst_sockets_complete(struct cpt_context *ctx)
{
    int err;
    cpt_object_t *obj;

    for_each_object(obj, CPT_OBJ_SOCKET) {
        struct cpt_sock_image *sbuf;
        struct sock *sk = obj->o_obj;
        struct sock *peer;

        if (!sk) BUG();

        if (sk->sk_family != AF_UNIX)
            continue;

        sbuf = cpt_get_buf(ctx);
        err = rst_get_object(CPT_OBJ_SOCKET, obj->o_pos, sbuf, ctx);
        if (err) {
            cpt_release_buf(ctx);
            return err;
        }

        if (sbuf->cpt_next > sbuf->cpt_hdrlen)
            restore_unix_rqueue(sk, sbuf, obj->o_pos, ctx);

        cpt_release_buf(ctx);

        if (sk->sk_type == SOCK_DGRAM && unix_peer(sk) == NULL) {
            cpt_object_t *pobj;

            sbuf = cpt_get_buf(ctx);
            err = rst_get_object(CPT_OBJ_SOCKET, obj->o_pos, sbuf, ctx);
            if (err) {
                cpt_release_buf(ctx);
                return err;
            }

            if (sbuf->cpt_peer != -1) {
                pobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, sbuf->cpt_peer, ctx);
                if (pobj) {
                    peer = pobj->o_obj;
                    sock_hold(peer);
                    unix_peer(sk) = peer;
                }
            }
            cpt_release_buf(ctx);
        }
    }

    rst_orphans(ctx);

    return 0;
}
예제 #2
0
/* Find slave/master tty in image, when we already know master/slave.
 * It might be optimized, of course. */
static loff_t find_pty_pair(struct tty_struct *stty, loff_t pos, struct cpt_tty_image *pi, struct cpt_context *ctx)
{
	int err;
	loff_t sec = ctx->sections[CPT_SECT_TTY];
	loff_t endsec;
	struct cpt_section_hdr h;
	struct cpt_tty_image *pibuf;

	err = ctx->pread(&h, sizeof(h), ctx, sec);
	if (err)
		return CPT_NULL;
	if (h.cpt_section != CPT_SECT_TTY || h.cpt_hdrlen < sizeof(h))
		return CPT_NULL;
	pibuf = kmalloc(sizeof(*pibuf), GFP_KERNEL);
	if (pibuf == NULL) {
		eprintk_ctx("cannot allocate buffer\n");
		return CPT_NULL;
	}
	endsec = sec + h.cpt_next;
	sec += h.cpt_hdrlen;
	while (sec < endsec) {
		if (rst_get_object(CPT_OBJ_TTY, sec, pibuf, ctx))
			return CPT_NULL;
		if (pibuf->cpt_index == pi->cpt_index &&
		    !((pi->cpt_drv_flags^pibuf->cpt_drv_flags)&TTY_DRIVER_DEVPTS_MEM) &&
		    pos != sec && strncmp("vtty", pibuf->cpt_name, 4)) {
			pty_setup(stty, sec, pibuf, ctx);
			return sec;
		}
		sec += pibuf->cpt_next;
	}
	kfree(pibuf);
	return CPT_NULL;
}
예제 #3
0
static int
rst_sock_attr_skfilter(loff_t *pos_p, struct sock *sk, cpt_context_t *ctx)
{
    int err;
    struct sk_filter *fp, *old_fp;
    loff_t pos = *pos_p;
    struct cpt_obj_bits v;

    err = rst_get_object(CPT_OBJ_SKFILTER, pos, &v, ctx);
    if (err)
        return err;

    *pos_p += v.cpt_next;

    if (v.cpt_size % sizeof(struct sock_filter))
        return -EINVAL;

    fp = sock_kmalloc(sk, v.cpt_size+sizeof(*fp), GFP_KERNEL_UBC);
    if (fp == NULL)
        return -ENOMEM;
    atomic_set(&fp->refcnt, 1);
    fp->len = v.cpt_size/sizeof(struct sock_filter);

    err = ctx->pread(fp->insns, v.cpt_size, ctx, pos+v.cpt_hdrlen);
    if (err) {
        sk_filter_uncharge(sk, fp);
        return err;
    }

    old_fp = sk->sk_filter;
    sk->sk_filter = fp;
    if (old_fp)
        sk_filter_uncharge(sk, old_fp);
    return 0;
}
예제 #4
0
static int pty_setup(struct tty_struct *stty, loff_t pos,
		     struct cpt_tty_image *pi, struct cpt_context *ctx)
{
	stty->pgrp = NULL;
	stty->session = NULL;
	stty->packet = pi->cpt_packet;
	stty->stopped = pi->cpt_stopped;
	stty->hw_stopped = pi->cpt_hw_stopped;
	stty->flow_stopped = pi->cpt_flow_stopped;
#define TTY_BEHAVIOR_FLAGS ((1<<TTY_EXCLUSIVE)|(1<<TTY_HW_COOK_OUT)| \
				(1<<TTY_HW_COOK_IN)|(1<<TTY_PTY_LOCK))
	stty->flags &= ~TTY_BEHAVIOR_FLAGS;
	stty->flags |= pi->cpt_flags & TTY_BEHAVIOR_FLAGS;
	stty->ctrl_status = pi->cpt_ctrl_status;
	stty->winsize.ws_row = pi->cpt_ws_row;
	stty->winsize.ws_col = pi->cpt_ws_col;
	stty->winsize.ws_ypixel = pi->cpt_ws_prow;
	stty->winsize.ws_xpixel = pi->cpt_ws_pcol;
	stty->canon_column = pi->cpt_canon_column;
	stty->column = pi->cpt_column;
	stty->raw = pi->cpt_raw;
	stty->real_raw = pi->cpt_real_raw;
	stty->erasing = pi->cpt_erasing;
	stty->lnext = pi->cpt_lnext;
	stty->icanon = pi->cpt_icanon;
	stty->closing = pi->cpt_closing;
	stty->minimum_to_wake = pi->cpt_minimum_to_wake;

	stty->termios->c_iflag = pi->cpt_c_iflag;
	stty->termios->c_oflag = pi->cpt_c_oflag;
	stty->termios->c_lflag = pi->cpt_c_lflag;
	stty->termios->c_cflag = pi->cpt_c_cflag;
	memcpy(&stty->termios->c_cc, &pi->cpt_c_cc, NCCS);
	memcpy(stty->read_flags, pi->cpt_read_flags, sizeof(stty->read_flags));

	if (pi->cpt_next > pi->cpt_hdrlen) {
		int err;
		struct cpt_obj_bits b;
		err = rst_get_object(CPT_OBJ_BITS, pos + pi->cpt_hdrlen, &b, ctx);
		if (err)
			return err;
		if (b.cpt_size == 0)
			return 0;
		err = ctx->pread(stty->read_buf, b.cpt_size, ctx, pos + pi->cpt_hdrlen + b.cpt_hdrlen);
		if (err)
			return err;

		spin_lock_irq(&stty->read_lock);
		stty->read_tail = 0;
		stty->read_cnt = b.cpt_size;
		stty->read_head = b.cpt_size;
		stty->canon_head = stty->read_tail + pi->cpt_canon_head;
		stty->canon_data = pi->cpt_canon_data;
		spin_unlock_irq(&stty->read_lock);
	}

	return 0;
}
예제 #5
0
static int rst_restore_netstats(loff_t pos, struct net_device *dev,
			struct cpt_context * ctx)
{
	struct cpt_netstats_image *n;
	struct net_device_stats *stats = NULL;
	int err;

	if (!dev->netdev_ops->ndo_get_stats)
		return 0;

	n = cpt_get_buf(ctx);
	err = rst_get_object(CPT_OBJ_NET_STATS, pos, n, ctx);
	if (err)
		goto out;
	BUG_ON(sizeof(struct cpt_netstats_image) != n->cpt_hdrlen);
	preempt_disable();

	if (dev->netdev_ops->ndo_cpt == NULL) {
		err = -ENODEV;
		eprintk_ctx("Network device %s is not supported\n", dev->name);
		goto out;
	}

	stats = dev->netdev_ops->ndo_get_stats(dev);

	stats->rx_packets = n->cpt_rx_packets;
	stats->tx_packets = n->cpt_tx_packets;
	stats->rx_bytes = n->cpt_rx_bytes;
	stats->tx_bytes = n->cpt_tx_bytes;
	stats->rx_errors = n->cpt_rx_errors;
	stats->tx_errors = n->cpt_tx_errors;
	stats->rx_dropped = n->cpt_rx_dropped;
	stats->tx_dropped = n->cpt_tx_dropped;
	stats->multicast = n->cpt_multicast;
	stats->collisions = n->cpt_collisions;
	stats->rx_length_errors = n->cpt_rx_length_errors;
	stats->rx_over_errors = n->cpt_rx_over_errors;
	stats->rx_crc_errors = n->cpt_rx_crc_errors;
	stats->rx_frame_errors = n->cpt_rx_frame_errors;
	stats->rx_fifo_errors = n->cpt_rx_fifo_errors;
	stats->rx_missed_errors = n->cpt_rx_missed_errors;
	stats->tx_aborted_errors = n->cpt_tx_aborted_errors;
	stats->tx_carrier_errors = n->cpt_tx_carrier_errors;
	stats->tx_fifo_errors = n->cpt_tx_fifo_errors;
	stats->tx_heartbeat_errors = n->cpt_tx_heartbeat_errors;
	stats->tx_window_errors = n->cpt_tx_window_errors;
	stats->rx_compressed = n->cpt_rx_compressed;
	stats->tx_compressed = n->cpt_tx_compressed;

out:
	preempt_enable();
	cpt_release_buf(ctx);
	return err;
}
예제 #6
0
int rst_tty_jobcontrol(struct cpt_context *ctx)
{
	int err;
	loff_t sec = ctx->sections[CPT_SECT_TTY];
	loff_t endsec;
	struct cpt_section_hdr h;

	err = ctx->pread(&h, sizeof(h), ctx, sec);
	if (err)
		return err;
	if (h.cpt_section != CPT_SECT_TTY || h.cpt_hdrlen < sizeof(h))
		return -EINVAL;
	endsec = sec + h.cpt_next;
	sec += h.cpt_hdrlen;
	while (sec < endsec) {
		cpt_object_t *obj;
		struct cpt_tty_image *pibuf = cpt_get_buf(ctx);

		if (rst_get_object(CPT_OBJ_TTY, sec, pibuf, ctx)) {
			cpt_release_buf(ctx);
			return -EINVAL;
		}

		obj = lookup_cpt_obj_bypos(CPT_OBJ_TTY, sec, ctx);
		if (obj) {
			struct tty_struct *stty = obj->o_obj;
			if ((int)pibuf->cpt_pgrp > 0) {
				stty->pgrp = alloc_vpid_safe(pibuf->cpt_pgrp);
				if (!stty->pgrp)
					dprintk_ctx("unknown tty pgrp %d\n", pibuf->cpt_pgrp);
			} else if (pibuf->cpt_pgrp) {
				stty->pgrp = alloc_pid(current->nsproxy->pid_ns,
							0);
				if (!stty->pgrp) {
					eprintk_ctx("cannot allocate stray tty->pgr\n");
					cpt_release_buf(ctx);
					return -EINVAL;
				}
			}
			if ((int)pibuf->cpt_session > 0) {
				stty->session = alloc_vpid_safe(pibuf->cpt_session);
				if (!stty->session)
					dprintk_ctx("unknown tty session %d\n", pibuf->cpt_session);
			}
		}
		sec += pibuf->cpt_next;
		cpt_release_buf(ctx);
	}
	return 0;
}
예제 #7
0
int rst_orphans(struct cpt_context *ctx)
{
    int err;
    loff_t sec = ctx->sections[CPT_SECT_ORPHANS];
    loff_t endsec;
    cpt_object_t *obj;
    struct cpt_section_hdr h;

    if (sec == CPT_NULL)
        return 0;

    err = ctx->pread(&h, sizeof(h), ctx, sec);
    if (err)
        return err;
    if (h.cpt_section != CPT_SECT_ORPHANS || h.cpt_hdrlen < sizeof(h))
        return -EINVAL;

    endsec = sec + h.cpt_next;
    sec += h.cpt_hdrlen;
    while (sec < endsec) {
        struct cpt_sock_image *sbuf = cpt_get_buf(ctx);
        err = rst_get_object(CPT_OBJ_SOCKET, sec, sbuf, ctx);
        if (err) {
            cpt_release_buf(ctx);
            return err;
        }
        obj = alloc_cpt_object(GFP_KERNEL, ctx);
        if (obj == NULL) {
            cpt_release_buf(ctx);
            return -ENOMEM;
        }
        obj->o_pos = sec;
        obj->o_ppos  = sbuf->cpt_file;
        err = open_socket(obj, sbuf, ctx);
        dprintk_ctx("Restoring orphan: %d\n", err);
        free_cpt_object(obj, ctx);
        cpt_release_buf(ctx);
        if (err)
            return err;
        sec += sbuf->cpt_next;
    }

    return 0;
}
예제 #8
0
int rst_eventpoll(cpt_context_t *ctx)
{
	int err;
	loff_t sec = ctx->sections[CPT_SECT_EPOLL];
	loff_t endsec;
	struct cpt_section_hdr h;

	if (sec == CPT_NULL)
		return 0;

	err = ctx->pread(&h, sizeof(h), ctx, sec);
	if (err)
		return err;
	if (h.cpt_section != CPT_SECT_EPOLL || h.cpt_hdrlen < sizeof(h))
		return -EINVAL;

	endsec = sec + h.cpt_next;
	sec += h.cpt_hdrlen;
	while (sec < endsec) {
		cpt_object_t *obj;
		struct cpt_epoll_image *ebuf = cpt_get_buf(ctx);
		err = rst_get_object(CPT_OBJ_EPOLL, sec, ebuf, ctx);
		if (err) {
			cpt_release_buf(ctx);
			return err;
		}
		obj = lookup_cpt_obj_bypos(CPT_OBJ_FILE, ebuf->cpt_file, ctx);
		if (obj == NULL) {
			eprintk_ctx("cannot find epoll file object\n");
			cpt_release_buf(ctx);
			return -EINVAL;
		}
		err = restore_one_epoll(obj, sec, ebuf, ctx);
		cpt_release_buf(ctx);
		if (err)
			return err;
		sec += ebuf->cpt_next;
	}

	return 0;
	
}
예제 #9
0
static int
rst_sock_attr_mcfilter(loff_t *pos_p, struct sock *sk, cpt_context_t *ctx)
{
    int err;
    loff_t pos = *pos_p;
    struct cpt_sockmc_image v;

    err = rst_get_object(CPT_OBJ_SOCK_MCADDR, pos, &v, ctx);
    if (err)
        return err;

    *pos_p += v.cpt_next;

    if (v.cpt_family == AF_INET)
        return rst_sk_mcfilter_in(sk, &v, pos, ctx);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
    else if (v.cpt_family == AF_INET6)
        return rst_sk_mcfilter_in6(sk, &v, pos, ctx);
#endif
    else
        return -EAFNOSUPPORT;
}
예제 #10
0
int rst_restore_route(struct cpt_context *ctx)
{
	int err;
	struct socket *sock;
	struct msghdr msg;
	struct iovec iov;
	struct sockaddr_nl nladdr;
	mm_segment_t oldfs;
	loff_t sec = ctx->sections[CPT_SECT_NET_ROUTE];
	loff_t endsec;
	struct cpt_section_hdr h;
	struct cpt_object_hdr v;
	char *pg;

	if (sec == CPT_NULL)
		return 0;

	err = ctx->pread(&h, sizeof(h), ctx, sec);
	if (err)
		return err;
	if (h.cpt_section != CPT_SECT_NET_ROUTE || h.cpt_hdrlen < sizeof(h))
		return -EINVAL;

	if (h.cpt_hdrlen >= h.cpt_next)
		return 0;

	sec += h.cpt_hdrlen;
	err = rst_get_object(CPT_OBJ_NET_ROUTE, sec, &v, ctx);
	if (err < 0)
		return err;

	err = sock_create(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, &sock);
	if (err)
		return err;

	pg = (char*)__get_free_page(GFP_KERNEL);
	if (pg == NULL) {
		err = -ENOMEM;
		goto out_sock;
	}

	memset(&nladdr, 0, sizeof(nladdr));
	nladdr.nl_family = AF_NETLINK;

	endsec = sec + v.cpt_next;
	sec += v.cpt_hdrlen;

	while (sec < endsec) {
		struct nlmsghdr *n;
		struct nlmsghdr nh;
		int kernel_flag;

		if (endsec - sec < sizeof(nh))
			break;

		err = ctx->pread(&nh, sizeof(nh), ctx, sec);
		if (err)
			goto out_sock_pg;
		if (nh.nlmsg_len < sizeof(nh) || nh.nlmsg_len > PAGE_SIZE ||
		    endsec - sec < nh.nlmsg_len) {
			err = -EINVAL;
			goto out_sock_pg;
		}
		err = ctx->pread(pg, nh.nlmsg_len, ctx, sec);
		if (err)
			goto out_sock_pg;

		n = (struct nlmsghdr*)pg;
		n->nlmsg_flags = NLM_F_REQUEST|NLM_F_APPEND|NLM_F_CREATE;

		err = rewrite_rtmsg(n, ctx);
		if (err < 0)
			goto out_sock_pg;
		kernel_flag = err;

		if (kernel_flag == 2)
			goto do_next;

		iov.iov_base=n;
		iov.iov_len=nh.nlmsg_len;
		msg.msg_name=&nladdr;
		msg.msg_namelen=sizeof(nladdr);
		msg.msg_iov=&iov;
		msg.msg_iovlen=1;
		msg.msg_control=NULL;
		msg.msg_controllen=0;
		msg.msg_flags=MSG_DONTWAIT;

		oldfs = get_fs(); set_fs(KERNEL_DS);
		err = sock_sendmsg(sock, &msg, nh.nlmsg_len);
		set_fs(oldfs);

		if (err < 0)
			goto out_sock_pg;
		err = 0;

		iov.iov_base=pg;
		iov.iov_len=PAGE_SIZE;

		oldfs = get_fs(); set_fs(KERNEL_DS);
		err = sock_recvmsg(sock, &msg, PAGE_SIZE, MSG_DONTWAIT);
		set_fs(oldfs);
		if (err != -EAGAIN) {
			if (n->nlmsg_type == NLMSG_ERROR) {
				struct nlmsgerr *e = NLMSG_DATA(n);
				if (e->error != -EEXIST || !kernel_flag)
					eprintk_ctx("NLMERR: %d\n", e->error);
			} else {
				eprintk_ctx("Res: %d %d\n", err, n->nlmsg_type);
			}
		}
do_next:
		err = 0;
		sec += NLMSG_ALIGN(nh.nlmsg_len);
	}

out_sock_pg:
	free_page((unsigned long)pg);
out_sock:
	sock_release(sock);
	return err;
}
예제 #11
0
static int restore_one_epoll(cpt_object_t *obj,
			     loff_t pos,
			     struct cpt_epoll_image *ebuf,
			     cpt_context_t *ctx)
{
	int err = 0;
	loff_t endpos;
	struct file *file = obj->o_obj;
	struct eventpoll *ep;

	if (file->f_op != &eventpoll_fops) {
		eprintk_ctx("bad epoll file\n");
		return -EINVAL;
	}

	ep = file->private_data;

	if (unlikely(ep == NULL)) {
		eprintk_ctx("bad epoll device\n");
		return -EINVAL;
	}

	endpos = pos + ebuf->cpt_next;
	pos += ebuf->cpt_hdrlen;
	while (pos < endpos) {
		struct cpt_epoll_file_image efi;
		struct epoll_event epds;
		
		cpt_object_t *tobj;

		err = rst_get_object(CPT_OBJ_EPOLL_FILE, pos, &efi, ctx);
		if (err)
			return err;
		tobj = lookup_cpt_obj_bypos(CPT_OBJ_FILE, efi.cpt_file, ctx);
		if (!tobj) {
			eprintk_ctx("epoll file not found\n");
			return -EINVAL;
		}
		epds.events = efi.cpt_events;
		epds.data = efi.cpt_data;
		mutex_lock(&ep->mtx);
		err = ep_insert(ep, &epds, tobj->o_obj, efi.cpt_fd);
		if (!err) {
			struct epitem *epi;
			epi = ep_find(ep, tobj->o_obj, efi.cpt_fd);
			if (epi) {
				if (efi.cpt_ready) {
					unsigned long flags;
					spin_lock_irqsave(&ep->lock, flags);
					if (list_empty(&epi->rdllink))
						list_add_tail(&epi->rdllink, &ep->rdllist);
					spin_unlock_irqrestore(&ep->lock, flags);
				}
			}
		}
		mutex_unlock(&ep->mtx);
		if (err)
			break;
		pos += efi.cpt_next;
	}
	return err;
}
예제 #12
0
static int rst_restore_iptables(struct cpt_context * ctx)
{
	int err;
	int pfd[2];
	struct file *f;
	struct cpt_object_hdr v;
	int n;
	struct cpt_section_hdr h;
	loff_t sec = ctx->sections[CPT_SECT_NET_IPTABLES];
	loff_t end;
	int pid;
	int status;
	mm_segment_t oldfs;
	sigset_t ignore, blocked;

	if (sec == CPT_NULL)
		return 0;

	err = ctx->pread(&h, sizeof(h), ctx, sec);
	if (err)
		return err;
	if (h.cpt_section != CPT_SECT_NET_IPTABLES || h.cpt_hdrlen < sizeof(h))
		return -EINVAL;

	if (h.cpt_hdrlen == h.cpt_next)
		return 0;
	if (h.cpt_hdrlen > h.cpt_next)
		return -EINVAL;
	sec += h.cpt_hdrlen;
	err = rst_get_object(CPT_OBJ_NAME, sec, &v, ctx);
	if (err < 0)
		return err;

	err = sc_pipe(pfd);
	if (err < 0)
		return err;
	ignore.sig[0] = CPT_SIG_IGNORE_MASK;
	sigprocmask(SIG_BLOCK, &ignore, &blocked);
	pid = err = local_kernel_thread(dumpfn, (void*)pfd, SIGCHLD, 0);
	if (err < 0) {
		eprintk_ctx("iptables local_kernel_thread: %d\n", err);
		goto out;
	}
	f = fget(pfd[1]);
	sc_close(pfd[1]);
	sc_close(pfd[0]);

	ctx->file->f_pos = sec + v.cpt_hdrlen;
	end = sec + v.cpt_next;
	do {
		char *p;
		char buf[16];

		n = end - ctx->file->f_pos;
		if (n > sizeof(buf))
			n = sizeof(buf);

		if (ctx->read(buf, n, ctx))
			break;
		if ((p = memchr(buf, 0, n)) != NULL)
			n = p - buf;
		oldfs = get_fs(); set_fs(KERNEL_DS);
		f->f_op->write(f, buf, n, &f->f_pos);
		set_fs(oldfs);
	} while (ctx->file->f_pos < end);

	fput(f);

	oldfs = get_fs(); set_fs(KERNEL_DS);
	if ((err = sc_waitx(pid, 0, &status)) < 0)
		eprintk_ctx("wait4: %d\n", err);
	else if ((status & 0x7f) == 0) {
		err = (status & 0xff00) >> 8;
		if (err != 0) {
			eprintk_ctx("iptables-restore exited with %d\n", err);
			eprintk_ctx("Most probably some iptables modules are not loaded\n");
			err = -EINVAL;
		}
	} else {
예제 #13
0
struct sk_buff * rst_skb(struct sock *sk, loff_t *pos_p, __u32 *owner,
                         __u32 *queue, struct cpt_context *ctx)
{
    int err;
    struct sk_buff *skb;
    struct cpt_skb_image v;
    loff_t pos = *pos_p;
    struct scm_fp_list *fpl = NULL;
    struct timeval tmptv;

    err = rst_get_object(CPT_OBJ_SKB, pos, &v, ctx);
    if (err)
        return ERR_PTR(err);
    *pos_p = pos + v.cpt_next;

    if (owner)
        *owner = v.cpt_owner;
    if (queue)
        *queue = v.cpt_queue;

    skb = alloc_skb(v.cpt_len + v.cpt_hspace + v.cpt_tspace, GFP_KERNEL);
    if (skb == NULL)
        return ERR_PTR(-ENOMEM);
    skb_reserve(skb, v.cpt_hspace);
    skb_put(skb, v.cpt_len);
#ifdef NET_SKBUFF_DATA_USES_OFFSET
    skb->transport_header = v.cpt_h;
    skb->network_header = v.cpt_nh;
    skb->mac_header = v.cpt_mac;
#else
    skb->transport_header = skb->head + v.cpt_h;
    skb->network_header = skb->head + v.cpt_nh;
    skb->mac_header = skb->head + v.cpt_mac;
#endif
    BUILD_BUG_ON(sizeof(skb->cb) < sizeof(v.cpt_cb));
    if (sk->sk_protocol == IPPROTO_TCP) {
        /*
         * According to Alexey all packets in queue have non-zero
         * flags, as at least TCPCB_FLAG_ACK is set on them.
         * Luckily for us, offset of field flags in tcp_skb_cb struct
         * with IPv6 is higher then total size of tcp_skb_cb struct
         * without IPv6.
         */
        if (ctx->image_version >= CPT_VERSION_18_2 ||
                ((struct tcp_skb_cb_ipv6 *)&v.cpt_cb)->flags) {
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
            check_tcp_cb_conv(NOT_CONV, CONV);
            memcpy(skb->cb, v.cpt_cb, sizeof(v.cpt_cb));
#else
            check_tcp_cb_conv(CONV, NOT_CONV);
            rst_tcp_cb_ipv6_to_ipv4(&v, skb);
#endif
        } else {
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
            check_tcp_cb_conv(CONV, NOT_CONV);
            rst_tcp_cb_ipv4_to_ipv6(&v, skb);
#else
            check_tcp_cb_conv(NOT_CONV, CONV);
            memcpy(skb->cb, v.cpt_cb, sizeof(v.cpt_cb));
#endif
        }
    } else
        memcpy(skb->cb, v.cpt_cb, sizeof(v.cpt_cb));
    skb->mac_len = v.cpt_mac_len;

    skb->csum = v.cpt_csum;
    skb->local_df = v.cpt_local_df;
    skb->pkt_type = v.cpt_pkt_type;
    skb->ip_summed = v.cpt_ip_summed;
    skb->priority = v.cpt_priority;
    skb->protocol = v.cpt_protocol;
    cpt_timeval_import(&tmptv, v.cpt_stamp);
    skb->tstamp = timeval_to_ktime(tmptv);

    skb_shinfo(skb)->gso_segs = v.cpt_gso_segs;
    skb_shinfo(skb)->gso_size = v.cpt_gso_size;
    if (ctx->image_version == 0) {
        skb_shinfo(skb)->gso_segs = 1;
        skb_shinfo(skb)->gso_size = 0;
    }

    if (v.cpt_next > v.cpt_hdrlen) {
        pos = pos + v.cpt_hdrlen;
        while (pos < *pos_p) {
            union {
                struct cpt_obj_bits b;
                struct cpt_fd_image f;
            } u;

            err = rst_get_object(-1, pos, &u, ctx);
            if (err) {
                kfree_skb(skb);
                return ERR_PTR(err);
            }
            if (u.b.cpt_object == CPT_OBJ_BITS) {
                if (u.b.cpt_size != v.cpt_hspace + skb->len) {
                    eprintk_ctx("invalid skb image %u != %u + %u\n", u.b.cpt_size, v.cpt_hspace, skb->len);
                    kfree_skb(skb);
                    return ERR_PTR(-EINVAL);
                }

                err = ctx->pread(skb->head, u.b.cpt_size, ctx, pos+u.b.cpt_hdrlen);
                if (err) {
                    kfree_skb(skb);
                    return ERR_PTR(err);
                }
            } else if (u.f.cpt_object == CPT_OBJ_FILEDESC) {
                if (!fpl) {
                    fpl = kmalloc(sizeof(struct scm_fp_list),
                                  GFP_KERNEL_UBC);
                    if (!fpl) {
                        kfree_skb(skb);
                        return ERR_PTR(-ENOMEM);
                    }
                    fpl->count = 0;
                    UNIXCB(skb).fp = fpl;
                }
                fpl->fp[fpl->count] = rst_file(u.f.cpt_file, -1, ctx);
                if (!IS_ERR(fpl->fp[fpl->count]))
                    fpl->count++;
            }
            pos += u.b.cpt_next;
        }
    }

    return skb;
}
예제 #14
0
static int open_socket(cpt_object_t *obj, struct cpt_sock_image *si,
                       struct cpt_context *ctx)
{
    int err;
    struct socket *sock;
    struct socket *sock2 = NULL;
    struct file *file;
    cpt_object_t *fobj;
    cpt_object_t *pobj = NULL;

    err = sock_create(si->cpt_family, si->cpt_type, si->cpt_protocol,
                      &sock);
    if (err)
        return err;

    if (si->cpt_socketpair) {
        err = sock_create(si->cpt_family, si->cpt_type,
                          si->cpt_protocol, &sock2);
        if (err)
            goto err_out;

        err = sock->ops->socketpair(sock, sock2);
        if (err < 0)
            goto err_out;

        /* Socketpair with a peer outside our environment.
         * So, we create real half-open pipe and do not worry
         * about dead end anymore. */
        if (si->cpt_peer == -1) {
            sock_release(sock2);
            sock2 = NULL;
        }
    }

    cpt_obj_setobj(obj, sock->sk, ctx);

    if (si->cpt_file != CPT_NULL) {
        file = sock_mapfile(sock);
        err = PTR_ERR(file);
        if (IS_ERR(file))
            goto err_out;

        err = -ENOMEM;

        obj->o_parent = file;

        if ((fobj = cpt_object_add(CPT_OBJ_FILE, file, ctx)) == NULL)
            goto err_out;
        cpt_obj_setpos(fobj, si->cpt_file, ctx);
        cpt_obj_setindex(fobj, si->cpt_index, ctx);
    }

    if (sock2) {
        struct file *file2;

        pobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, si->cpt_peer, ctx);
        if (!pobj) BUG();
        if (pobj->o_obj) BUG();
        cpt_obj_setobj(pobj, sock2->sk, ctx);

        if (pobj->o_ppos != CPT_NULL) {
            file2 = sock_mapfile(sock2);
            err = PTR_ERR(file2);
            if (IS_ERR(file2))
                goto err_out;

            err = -ENOMEM;
            if ((fobj = cpt_object_add(CPT_OBJ_FILE, file2, ctx)) == NULL)
                goto err_out;
            cpt_obj_setpos(fobj, pobj->o_ppos, ctx);
            cpt_obj_setindex(fobj, si->cpt_peer, ctx);

            pobj->o_parent = file2;
        }
    }

    setup_sock_common(sock->sk, si, obj->o_pos, ctx);
    if (sock->sk->sk_family == AF_INET || sock->sk->sk_family == AF_INET6) {
        int saved_reuse = sock->sk->sk_reuse;

        inet_sk(sock->sk)->freebind = 1;
        sock->sk->sk_reuse = 2;
        if (si->cpt_laddrlen) {
            err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, si->cpt_laddrlen);
            if (err) {
                dprintk_ctx("binding failed: %d, do not worry\n", err);
            }
        }
        sock->sk->sk_reuse = saved_reuse;
        rst_socket_in(si, obj->o_pos, sock->sk, ctx);
    } else if (sock->sk->sk_family == AF_NETLINK) {
        struct sockaddr_nl *nl = (struct sockaddr_nl *)&si->cpt_laddr;
        if (nl->nl_pid) {
            err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, si->cpt_laddrlen);
            if (err) {
                eprintk_ctx("AF_NETLINK binding failed: %d\n", err);
            }
        }
        if (si->cpt_raddrlen && nl->nl_pid) {
            err = sock->ops->connect(sock, (struct sockaddr *)&si->cpt_raddr, si->cpt_raddrlen, O_NONBLOCK);
            if (err) {
                eprintk_ctx("oops, AF_NETLINK connect failed: %d\n", err);
            }
        }
        generic_restore_queues(sock->sk, si, obj->o_pos, ctx);
    } else if (sock->sk->sk_family == PF_PACKET) {
        struct sockaddr_ll *ll = (struct sockaddr_ll *)&si->cpt_laddr;
        if (ll->sll_protocol || ll->sll_ifindex) {
            int alen = si->cpt_laddrlen;
            if (alen < sizeof(struct sockaddr_ll))
                alen = sizeof(struct sockaddr_ll);
            err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, alen);
            if (err) {
                eprintk_ctx("AF_PACKET binding failed: %d\n", err);
            }
        }
        generic_restore_queues(sock->sk, si, obj->o_pos, ctx);
    }
    fixup_unix_address(sock, si, ctx);

    if (sock2) {
        err = rst_get_object(CPT_OBJ_SOCKET, pobj->o_pos, si, ctx);
        if (err)
            return err;
        setup_sock_common(sock2->sk, si, pobj->o_pos, ctx);
        fixup_unix_address(sock2, si, ctx);
    }

    if ((sock->sk->sk_family == AF_INET || sock->sk->sk_family == AF_INET6)
            && (int)si->cpt_parent != -1) {
        cpt_object_t *lobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, si->cpt_parent, ctx);
        if (lobj && cpt_attach_accept(lobj->o_obj, sock->sk, ctx) == 0)
            sock->sk = NULL;
    }


    if (si->cpt_file == CPT_NULL && sock->sk &&
            sock->sk->sk_family == AF_INET) {
        struct sock *sk = sock->sk;

        if (sk) {
            sock->sk = NULL;

            local_bh_disable();
            bh_lock_sock(sk);
            if (sock_owned_by_user(sk))
                eprintk_ctx("oops, sock is locked by user\n");

            sock_hold(sk);
            sock_orphan(sk);
            ub_inc_orphan_count(sk);
            bh_unlock_sock(sk);
            local_bh_enable();
            sock_put(sk);
            dprintk_ctx("orphaning socket %p\n", sk);
        }
    }

    if (si->cpt_file == CPT_NULL && sock->sk == NULL)
        sock_release(sock);

    return 0;

err_out:
    if (sock2)
        sock_release(sock2);
    sock_release(sock);
    return err;
}
예제 #15
0
int rst_restore_netdev(struct cpt_context *ctx)
{
	struct net *net = get_exec_env()->ve_netns;
	int err;
	loff_t sec = ctx->sections[CPT_SECT_NET_DEVICE];
	loff_t endsec;
	struct cpt_section_hdr h;
	struct cpt_netdev_image di;
	struct net_device *dev;

	get_exec_env()->disable_net = 1;

	if (sec == CPT_NULL)
		return 0;

	err = ctx->pread(&h, sizeof(h), ctx, sec);
	if (err)
		return err;
	if (h.cpt_section != CPT_SECT_NET_DEVICE || h.cpt_hdrlen < sizeof(h))
		return -EINVAL;

	endsec = sec + h.cpt_next;
	sec += h.cpt_hdrlen;
	while (sec < endsec) {
		loff_t pos;
		struct net_device *dev_new;
		struct netdev_rst *ops;

		err = rst_get_object(CPT_OBJ_NET_DEVICE, sec, &di, ctx);
		if (err)
			return err;

		rtnl_lock();
		pos = sec + di.cpt_hdrlen;
		if (di.cpt_next > sizeof(di)) {
			struct cpt_object_hdr hdr;
			err = ctx->pread(&hdr, sizeof(struct cpt_object_hdr),
					ctx, sec + di.cpt_hdrlen);
			if (err)
				goto out;

			ops = NULL;
			while (1) {
				ops = netdev_find_rst(hdr.cpt_object, ops);
				if (ops == NULL)
					break;

				err = ops->ndo_rst(sec, &di, &rst_ops, ctx);
				if (!err) {
					pos += hdr.cpt_next;
					break;
				} else if (err < 0) {
					eprintk_ctx("netdev %d rst failed %d\n",
							hdr.cpt_object, err);
					goto out;
				}
			}
		}

		dev = __dev_get_by_name(net, di.cpt_name);
		if (dev) {
			if (dev->ifindex != di.cpt_index) {
				dev_new = __dev_get_by_index(net, di.cpt_index);
				if (!dev_new) {
					write_lock_bh(&dev_base_lock);
					hlist_del(&dev->index_hlist);
					if (dev->iflink == dev->ifindex)
						dev->iflink = di.cpt_index;
					dev->ifindex = di.cpt_index;
					hlist_add_head(&dev->index_hlist,
							dev_index_hash(net, dev->ifindex));
					write_unlock_bh(&dev_base_lock);
				} else {
					write_lock_bh(&dev_base_lock);
					hlist_del(&dev->index_hlist);
					hlist_del(&dev_new->index_hlist);
					if (dev_new->iflink == dev_new->ifindex)
						dev_new->iflink = dev->ifindex;
					dev_new->ifindex = dev->ifindex;
					if (dev->iflink == dev->ifindex)
						dev->iflink = di.cpt_index;
					dev->ifindex = di.cpt_index;
					hlist_add_head(&dev->index_hlist,
							dev_index_hash(net, dev->ifindex));
					hlist_add_head(&dev_new->index_hlist,
							dev_index_hash(net, dev_new->ifindex));
					write_unlock_bh(&dev_base_lock);
				}
			}
			if (di.cpt_flags^dev->flags) {
				err = dev_change_flags(dev, di.cpt_flags);
				if (err)
					eprintk_ctx("dev_change_flags err: %d\n", err);
			}
			while (pos < sec + di.cpt_next) {
				struct cpt_object_hdr hdr;
				err = ctx->pread(&hdr, sizeof(struct cpt_object_hdr),
						ctx, pos);
				if (err)
					goto out;
				if (hdr.cpt_object == CPT_OBJ_NET_HWADDR) {
					/* Restore hardware address */
					struct cpt_hwaddr_image hw;
					err = rst_get_object(CPT_OBJ_NET_HWADDR,
							pos, &hw, ctx);
					if (err)
						goto out;
					BUILD_BUG_ON(sizeof(hw.cpt_dev_addr) !=
							MAX_ADDR_LEN);
					memcpy(dev->dev_addr, hw.cpt_dev_addr,
							sizeof(hw.cpt_dev_addr));
				} else if (hdr.cpt_object == CPT_OBJ_NET_STATS) {
					err = rst_restore_netstats(pos, dev, ctx);
					if (err) {
						eprintk_ctx("rst stats %s: %d\n",
								di.cpt_name, err);
						goto out;
					}
				}
				pos += hdr.cpt_next;
			}
		} else {
			eprintk_ctx("unknown interface 2 %s\n", di.cpt_name);
		}
		rtnl_unlock();
		sec += di.cpt_next;
	}
	return 0;
out:
	rtnl_unlock();
	return err;
}
예제 #16
0
int rst_restore_ifaddr(struct cpt_context *ctx)
{
	struct net *net = get_exec_env()->ve_netns;
	int err;
	loff_t sec = ctx->sections[CPT_SECT_NET_IFADDR];
	loff_t endsec;
	struct cpt_section_hdr h;
	struct cpt_ifaddr_image di;
	struct net_device *dev;

	if (sec == CPT_NULL)
		return 0;

	err = ctx->pread(&h, sizeof(h), ctx, sec);
	if (err)
		return err;
	if (h.cpt_section != CPT_SECT_NET_IFADDR || h.cpt_hdrlen < sizeof(h))
		return -EINVAL;

	endsec = sec + h.cpt_next;
	sec += h.cpt_hdrlen;
	while (sec < endsec) {
		int cindex = -1;
		int err;
		err = rst_get_object(CPT_OBJ_NET_IFADDR, sec, &di, ctx);
		if (err)
			return err;
		cindex = di.cpt_index;
		rtnl_lock();
		dev = __dev_get_by_index(net, cindex);
		if (dev && di.cpt_family == AF_INET) {
			struct in_device *in_dev;
			struct in_ifaddr *ifa;
			if ((in_dev = __in_dev_get_rtnl(dev)) == NULL)
				in_dev = inetdev_init(dev);
			ifa = inet_alloc_ifa();
			if (ifa) {
				ifa->ifa_local = di.cpt_address[0];
				ifa->ifa_address = di.cpt_peer[0];
				ifa->ifa_broadcast = di.cpt_broadcast[0];
				ifa->ifa_prefixlen = di.cpt_masklen;
				ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
				ifa->ifa_flags = di.cpt_flags;
				ifa->ifa_scope = di.cpt_scope;
				memcpy(ifa->ifa_label, di.cpt_label, IFNAMSIZ);
				in_dev_hold(in_dev);
				ifa->ifa_dev   = in_dev;
				err = inet_insert_ifa(ifa);
				if (err && err != -EEXIST) {
					rtnl_unlock();
					eprintk_ctx("add ifaddr err %d for %d %s\n", err, di.cpt_index, di.cpt_label);
					return err;
				}
			}
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
		} else if (dev && di.cpt_family == AF_INET6) {
			__u32 prefered_lft;
			__u32 valid_lft;
			struct net *net = get_exec_env()->ve_ns->net_ns;
			prefered_lft = (di.cpt_flags & IFA_F_DEPRECATED) ?
				0 : di.cpt_prefered_lft;
			valid_lft = (di.cpt_flags & IFA_F_PERMANENT) ?
				0xFFFFFFFF : di.cpt_valid_lft;
			err = inet6_addr_add(net, dev->ifindex,
					     (struct in6_addr *)di.cpt_address,
					     di.cpt_masklen, 0,
					     prefered_lft,
					     valid_lft);
			if (err && err != -EEXIST) {
				rtnl_unlock();
				eprintk_ctx("add ifaddr err %d for %d %s\n", err, di.cpt_index, di.cpt_label);
				return err;
			}
#endif
		} else {
			rtnl_unlock();
			eprintk_ctx("unknown ifaddr 2 for %d\n", di.cpt_index);
			return -EINVAL;
		}
		rtnl_unlock();
		sec += di.cpt_next;
	}
	return 0;
}
예제 #17
0
int rst_sockets(struct cpt_context *ctx)
{
    int err;
    loff_t sec = ctx->sections[CPT_SECT_SOCKET];
    loff_t endsec;
    cpt_object_t *obj;
    struct cpt_section_hdr h;

    if (sec == CPT_NULL)
        return 0;

    err = ctx->pread(&h, sizeof(h), ctx, sec);
    if (err) {
        eprintk_ctx("rst_sockets: ctx->pread: %d\n", err);
        return err;
    }
    if (h.cpt_section != CPT_SECT_SOCKET || h.cpt_hdrlen < sizeof(h)) {
        eprintk_ctx("rst_sockets: hdr err\n");
        return -EINVAL;
    }

    /* The first pass: we create socket index and open listening sockets. */
    endsec = sec + h.cpt_next;
    sec += h.cpt_hdrlen;
    while (sec < endsec) {
        struct cpt_sock_image *sbuf = cpt_get_buf(ctx);
        err = rst_get_object(CPT_OBJ_SOCKET, sec, sbuf, ctx);
        if (err) {
            eprintk_ctx("rst_sockets: rst_get_object: %d\n", err);
            cpt_release_buf(ctx);
            return err;
        }
        if (sbuf->cpt_state == TCP_LISTEN) {
            err = open_listening_socket(sec, sbuf, ctx);
            cpt_release_buf(ctx);
            if (err) {
                eprintk_ctx("rst_sockets: open_listening_socket: %d\n", err);
                return err;
            }
        } else {
            cpt_release_buf(ctx);
            obj = alloc_cpt_object(GFP_KERNEL, ctx);
            if (obj == NULL)
                return -ENOMEM;
            cpt_obj_setindex(obj, sbuf->cpt_index, ctx);
            cpt_obj_setpos(obj, sec, ctx);
            obj->o_ppos  = sbuf->cpt_file;
            intern_cpt_object(CPT_OBJ_SOCKET, obj, ctx);
        }
        sec += sbuf->cpt_next;
    }

    /* Pass 2: really restore sockets */
    for_each_object(obj, CPT_OBJ_SOCKET) {
        struct cpt_sock_image *sbuf;
        if (obj->o_obj != NULL)
            continue;
        sbuf = cpt_get_buf(ctx);
        err = rst_get_object(CPT_OBJ_SOCKET, obj->o_pos, sbuf, ctx);
        if (err) {
            eprintk_ctx("rst_sockets: rst_get_object: %d\n", err);
            cpt_release_buf(ctx);
            return err;
        }
        if (sbuf->cpt_state == TCP_LISTEN) BUG();
        err = open_socket(obj, sbuf, ctx);
        cpt_release_buf(ctx);
        if (err) {
            eprintk_ctx("rst_sockets: open_socket: %d\n", err);
            return err;
        }
    }

    return 0;
}
예제 #18
0
int rst_restore_synwait_queue(struct sock *sk, struct cpt_sock_image *si,
			      loff_t pos, struct cpt_context *ctx)
{
	int err;
	loff_t end = pos + si->cpt_next;

	pos += si->cpt_hdrlen;

	lock_sock(sk);
	while (pos < end) {
		struct cpt_openreq_image oi;

		err = rst_sock_attr(&pos, sk, ctx);
		if (!err)
			continue;
		if (err < 0)
			goto out;

		err = rst_get_object(CPT_OBJ_OPENREQ, pos, &oi, ctx);
		if (err)
			goto out;

		if (oi.cpt_object == CPT_OBJ_OPENREQ) {
			struct request_sock *req;

			if (oi.cpt_family == AF_INET6 &&
			    sk->sk_family != AF_INET6)
				/* related to non initialized cpt_family bug */
				goto next;
			req = rst_reqsk_alloc(oi.cpt_family);
			if (IS_ERR(req)) {
				release_sock(sk);
				return PTR_ERR(req);
			}

			if (req == NULL) {
				release_sock(sk);
				return -ENOMEM;
			}

			tcp_rsk(req)->rcv_isn = oi.cpt_rcv_isn;
			tcp_rsk(req)->snt_isn = oi.cpt_snt_isn;
			inet_rsk(req)->rmt_port = oi.cpt_rmt_port;
			req->mss = oi.cpt_mss;
			req->retrans = oi.cpt_retrans;
			inet_rsk(req)->snd_wscale = oi.cpt_snd_wscale;
			inet_rsk(req)->rcv_wscale = oi.cpt_rcv_wscale;
			inet_rsk(req)->tstamp_ok = oi.cpt_tstamp_ok;
			inet_rsk(req)->sack_ok = oi.cpt_sack_ok;
			inet_rsk(req)->wscale_ok = oi.cpt_wscale_ok;
			inet_rsk(req)->ecn_ok = oi.cpt_ecn_ok;
			inet_rsk(req)->acked = oi.cpt_acked;
			inet_rsk(req)->opt = NULL;
			req->window_clamp = oi.cpt_window_clamp;
			req->rcv_wnd = oi.cpt_rcv_wnd;
			req->ts_recent = oi.cpt_ts_recent;
			req->expires = jiffies_import(oi.cpt_expires);
			req->sk = NULL;
			req->secid = 0;
			req->peer_secid = 0;

			if (oi.cpt_family == AF_INET6) {
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
				inet6_rsk(req)->pktopts = NULL;
				memcpy(&inet6_rsk(req)->loc_addr, oi.cpt_loc_addr, 16);
				memcpy(&inet6_rsk(req)->rmt_addr, oi.cpt_rmt_addr, 16);
				inet6_rsk(req)->iif = oi.cpt_iif;
				inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
#endif
			} else {
				memcpy(&inet_rsk(req)->loc_addr, oi.cpt_loc_addr, 4);
				memcpy(&inet_rsk(req)->rmt_addr, oi.cpt_rmt_addr, 4);
				inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
			}
		}
next:
		pos += oi.cpt_next;
	}
	err = 0;
out:
	release_sock(sk);
	return err;
}
예제 #19
0
struct file * rst_open_tty(cpt_object_t *mntobj, char *name,
			   struct cpt_file_image *fi, struct cpt_inode_image *ii,
			   unsigned flags, struct cpt_context *ctx)
{
	int err;
	cpt_object_t *obj;
	struct file *master, *slave;
	struct tty_struct *stty;
	struct cpt_tty_image *pi;
	static char *a = "pqrstuvwxyzabcde";
	static char *b = "0123456789abcdef";
	char pairname[16];
	unsigned master_flags, slave_flags;

	if (fi->cpt_priv == CPT_NULL)
		return ERR_PTR(-EINVAL);

	obj = lookup_cpt_obj_bypos(CPT_OBJ_TTY, fi->cpt_priv, ctx);
	if (obj && obj->o_parent) {
		dprintk_ctx("obtained pty as pair to existing\n");
		master = obj->o_parent;
		stty = file_tty(master);

		if (stty->driver->subtype == PTY_TYPE_MASTER &&
		    (stty->driver->flags&TTY_DRIVER_DEVPTS_MEM)) {
			wprintk_ctx("cloning ptmx\n");
			get_file(master);
			return master;
		}

		master = dentry_open(dget(master->f_dentry),
				     mntget(master->f_vfsmnt), flags,
				     current_cred());
		if (!IS_ERR(master)) {
			stty = file_tty(master);
			if (stty->driver->subtype != PTY_TYPE_MASTER)
				fixup_tty_attrs(ii, master, ctx);
		}
		return master;
	}

	pi = cpt_get_buf(ctx);
	err = rst_get_object(CPT_OBJ_TTY, fi->cpt_priv, pi, ctx);
	if (err) {
		cpt_release_buf(ctx);
		return ERR_PTR(err);
	}

	if (MAJOR(ii->cpt_rdev) == TTY_MAJOR ||
	    ii->cpt_rdev == MKDEV(TTYAUX_MAJOR, 1)) {
		if (mntobj && (mntobj->o_flags & CPT_VFSMOUNT_DELAYFS))
		       return ERR_PTR(-ENOTSUPP);
		master = rst_open_file(mntobj, name, fi,
				flags|O_NONBLOCK|O_NOCTTY, ctx);
		if (IS_ERR(master)) {
			eprintk_ctx("rst_open_tty: %s %Ld %ld\n",
					name, (long long)fi->cpt_priv,
					PTR_ERR(master));
			return master;
		}

		stty = file_tty(master);
		obj = cpt_object_add(CPT_OBJ_TTY, stty, ctx);
		obj->o_parent = master;
		cpt_obj_setpos(obj, fi->cpt_priv, ctx);

		obj = cpt_object_add(CPT_OBJ_FILE, master, ctx);
		cpt_obj_setpos(obj, CPT_NULL, ctx);
		get_file(master);

		/* Do not restore /dev/ttyX state */
		cpt_release_buf(ctx);
		return master;
	}

	master_flags = slave_flags = 0;
	if (pi->cpt_drv_subtype == PTY_TYPE_MASTER)
		master_flags = flags;
	else
		slave_flags = flags;

	/*
	 * Open pair master/slave.
	 */
	if (pi->cpt_drv_flags&TTY_DRIVER_DEVPTS_MEM) {
		master = ptmx_open(pi->cpt_index, master_flags);
	} else {
		sprintf(pairname, "/dev/pty%c%c", a[pi->cpt_index/16], b[pi->cpt_index%16]);
		master = filp_open(pairname, master_flags|O_NONBLOCK|O_NOCTTY|O_RDWR, 0);
	}
	if (IS_ERR(master)) {
		eprintk_ctx("filp_open master: %Ld %ld\n", (long long)fi->cpt_priv, PTR_ERR(master));
		cpt_release_buf(ctx);
		return master;
	}
	stty = file_tty(master);
	clear_bit(TTY_PTY_LOCK, &stty->flags);
	if (pi->cpt_drv_flags&TTY_DRIVER_DEVPTS_MEM)
		sprintf(pairname, "/dev/pts/%d", stty->index);
	else
		sprintf(pairname, "/dev/tty%c%c", a[stty->index/16], b[stty->index%16]);
	slave = filp_open(pairname, slave_flags|O_NONBLOCK|O_NOCTTY|O_RDWR, 0);
	if (IS_ERR(slave)) {
		eprintk_ctx("filp_open slave %s: %ld\n", pairname, PTR_ERR(slave));
		fput(master);
		cpt_release_buf(ctx);
		return slave;
	}

	if (pi->cpt_drv_subtype != PTY_TYPE_MASTER)
		fixup_tty_attrs(ii, slave, ctx);

	cpt_object_add(CPT_OBJ_TTY, file_tty(master), ctx);
	cpt_object_add(CPT_OBJ_TTY, file_tty(slave), ctx);
	cpt_object_add(CPT_OBJ_FILE, master, ctx);
	cpt_object_add(CPT_OBJ_FILE, slave, ctx);

	if (pi->cpt_drv_subtype == PTY_TYPE_MASTER) {
		loff_t pos;
		obj = lookup_cpt_object(CPT_OBJ_TTY, file_tty(master), ctx);
		obj->o_parent = master;
		cpt_obj_setpos(obj, fi->cpt_priv, ctx);
		pty_setup(stty, fi->cpt_priv, pi, ctx);

		obj = lookup_cpt_object(CPT_OBJ_TTY, file_tty(slave), ctx);
		obj->o_parent = slave;
		pos = find_pty_pair(stty->link, fi->cpt_priv, pi, ctx);
		cpt_obj_setpos(obj, pos, ctx);

		obj = lookup_cpt_object(CPT_OBJ_FILE, slave, ctx);
		cpt_obj_setpos(obj, CPT_NULL, ctx);
		get_file(master);
		cpt_release_buf(ctx);
		return master;
	} else {
		loff_t pos;
		obj = lookup_cpt_object(CPT_OBJ_TTY, file_tty(slave), ctx);
		obj->o_parent = slave;
		cpt_obj_setpos(obj, fi->cpt_priv, ctx);
		pty_setup(stty->link, fi->cpt_priv, pi, ctx);

		obj = lookup_cpt_object(CPT_OBJ_TTY, file_tty(master), ctx);
		obj->o_parent = master;
		pos = find_pty_pair(stty, fi->cpt_priv, pi, ctx);
		cpt_obj_setpos(obj, pos, ctx);

		obj = lookup_cpt_object(CPT_OBJ_FILE, master, ctx);
		cpt_obj_setpos(obj, CPT_NULL, ctx);
		get_file(slave);
		cpt_release_buf(ctx);
		return slave;
	}
}