示例#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
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;
}
示例#3
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;
}
示例#4
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;
	
}
示例#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
static void cpt_dump_netstats(struct net_device *dev, struct cpt_context * ctx)
{
	struct cpt_netstats_image *n;
	struct net_device_stats *stats;

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

	n = cpt_get_buf(ctx);
	stats = dev->netdev_ops->ndo_get_stats(dev);
	cpt_open_object(NULL, ctx);

	n->cpt_next = CPT_NULL;
	n->cpt_object = CPT_OBJ_NET_STATS;
	n->cpt_hdrlen = sizeof(*n);
	n->cpt_content = CPT_CONTENT_VOID;

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

	ctx->write(n, sizeof(*n), ctx);
	cpt_close_object(ctx);
	cpt_release_buf(ctx);
	return;
}
示例#7
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;
}
示例#8
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;
	}
}
示例#9
0
int cpt_dump_tty(cpt_object_t *obj, struct cpt_context *ctx)
{
	struct tty_struct *tty = obj->o_obj;
	struct cpt_tty_image *v;

	if (tty->link) {
		if (lookup_cpt_object(CPT_OBJ_TTY, tty->link, ctx) == NULL) {
			eprintk_ctx("orphan pty %s %d\n", tty->name, tty->driver->subtype == PTY_TYPE_SLAVE);
			return -EINVAL;
		}
		if (tty->link->link != tty) {
			eprintk_ctx("bad pty pair\n");
			return -EINVAL;
		}
		if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
		    tty->driver->subtype == PTY_TYPE_SLAVE &&
		    tty->link->count)
			obj->o_count++;
	}
	if (obj->o_count != tty->count) {
		eprintk_ctx("tty %s is referenced outside %d %d\n", tty->name, obj->o_count, tty->count);
		return -EBUSY;
	}

	cpt_open_object(obj, ctx);

	v = cpt_get_buf(ctx);
	v->cpt_next = -1;
	v->cpt_object = CPT_OBJ_TTY;
	v->cpt_hdrlen = sizeof(*v);
	v->cpt_content = CPT_CONTENT_ARRAY;

	v->cpt_index = tty->index;
	v->cpt_link = -1;
	if (tty->link)
		v->cpt_link = tty->link->index;
	v->cpt_drv_type = tty->driver->type;
	v->cpt_drv_subtype = tty->driver->subtype;
	v->cpt_drv_flags = tty->driver->flags;
	v->cpt_packet = tty->packet;
	v->cpt_stopped = tty->stopped;
	v->cpt_hw_stopped = tty->hw_stopped;
	v->cpt_flow_stopped = tty->flow_stopped;
	v->cpt_flags = tty->flags;
	v->cpt_ctrl_status = tty->ctrl_status;
	v->cpt_canon_data = tty->canon_data;
	v->cpt_canon_head = tty->canon_head - tty->read_tail;
	v->cpt_canon_column = tty->canon_column;
	v->cpt_column = tty->column;
	v->cpt_erasing = tty->erasing;
	v->cpt_lnext = tty->lnext;
	v->cpt_icanon = tty->icanon;
	v->cpt_raw = tty->raw;
	v->cpt_real_raw = tty->real_raw;
	v->cpt_closing = tty->closing;
	v->cpt_minimum_to_wake = tty->minimum_to_wake;
	v->cpt_pgrp = 0;
	if (tty->pgrp) {
		v->cpt_pgrp = pid_vnr(tty->pgrp);
		if ((int)v->cpt_pgrp < 0) {
			dprintk_ctx("cannot map tty->pgrp %d -> %d\n", pid_vnr(tty->pgrp), (int)v->cpt_pgrp);
			v->cpt_pgrp = -1;
		}
	}
	v->cpt_session = 0;
	if (tty->session) {
		v->cpt_session = pid_vnr(tty->session);
		if ((int)v->cpt_session < 0) {
			eprintk_ctx("cannot map tty->session %d -> %d\n", pid_nr(tty->session), (int)v->cpt_session);
			cpt_release_buf(ctx);
			return -EINVAL;
		}
	}
	memcpy(v->cpt_name, tty->name, 64);
	v->cpt_ws_row = tty->winsize.ws_row;
	v->cpt_ws_col = tty->winsize.ws_col;
	v->cpt_ws_prow = tty->winsize.ws_ypixel;
	v->cpt_ws_pcol = tty->winsize.ws_xpixel;
	if (tty->termios == NULL) {
		eprintk_ctx("NULL termios");
		cpt_release_buf(ctx);
		return -EINVAL;
	}
	v->cpt_c_line = tty->termios->c_line;
	v->cpt_c_iflag = tty->termios->c_iflag;
	v->cpt_c_oflag = tty->termios->c_oflag;
	v->cpt_c_cflag = tty->termios->c_cflag;
	v->cpt_c_lflag = tty->termios->c_lflag;
	memcpy(v->cpt_c_cc, tty->termios->c_cc, NCCS);
	if (NCCS < 32)
		memset(v->cpt_c_cc + NCCS, 255, 32 - NCCS);
	memcpy(v->cpt_read_flags, tty->read_flags, sizeof(v->cpt_read_flags));

	ctx->write(v, sizeof(*v), ctx);
	cpt_release_buf(ctx);

	if (tty->read_buf && tty->read_cnt) {
		struct cpt_obj_bits *v = cpt_get_buf(ctx);
		loff_t saved_pos;

		cpt_push_object(&saved_pos, ctx);
		cpt_open_object(NULL, ctx);
		v->cpt_next = CPT_NULL;
		v->cpt_object = CPT_OBJ_BITS;
		v->cpt_hdrlen = sizeof(*v);
		v->cpt_content = CPT_CONTENT_DATA;
		v->cpt_size = tty->read_cnt;
		ctx->write(v, sizeof(*v), ctx);
		cpt_release_buf(ctx);

		if (tty->read_cnt) {
			int n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);
			ctx->write(tty->read_buf + tty->read_tail, n, ctx);
			if (tty->read_cnt > n)
				ctx->write(tty->read_buf, tty->read_cnt-n, ctx);
			ctx->align(ctx);
		}

		cpt_close_object(ctx);
		cpt_pop_object(&saved_pos, ctx);
	}

	cpt_close_object(ctx);

	return 0;
}