int cpt_dump_link(struct cpt_context * ctx) { struct net *net = get_exec_env()->ve_netns; struct net_device *dev; cpt_open_section(ctx, CPT_SECT_NET_DEVICE); for_each_netdev(net, dev) { struct cpt_netdev_image v; struct cpt_hwaddr_image hw; loff_t saved_obj; if (dev->netdev_ops->ndo_cpt == NULL) { eprintk_ctx("unsupported netdev %s\n", dev->name); cpt_close_section(ctx); return -EBUSY; } cpt_open_object(NULL, ctx); v.cpt_next = CPT_NULL; v.cpt_object = CPT_OBJ_NET_DEVICE; v.cpt_hdrlen = sizeof(v); v.cpt_content = CPT_CONTENT_ARRAY; v.cpt_index = dev->ifindex; v.cpt_flags = dev->flags; memcpy(v.cpt_name, dev->name, IFNAMSIZ); ctx->write(&v, sizeof(v), ctx); cpt_push_object(&saved_obj, ctx); cpt_open_object(NULL, ctx); dev->netdev_ops->ndo_cpt(dev, &cpt_ops, ctx); /* Dump hardware address */ cpt_open_object(NULL, ctx); hw.cpt_next = CPT_NULL; hw.cpt_object = CPT_OBJ_NET_HWADDR; hw.cpt_hdrlen = sizeof(hw); hw.cpt_content = CPT_CONTENT_VOID; if (dev->dev_addrs.count != 1) { eprintk_ctx("multiple hwaddrs on %s\n", dev->name); return -EINVAL; } BUILD_BUG_ON(sizeof(hw.cpt_dev_addr) != MAX_ADDR_LEN); memcpy(hw.cpt_dev_addr, dev->dev_addr, sizeof(hw.cpt_dev_addr)); ctx->write(&hw, sizeof(hw), ctx); cpt_close_object(ctx); cpt_dump_netstats(dev, ctx); cpt_pop_object(&saved_obj, ctx); cpt_close_object(ctx); } cpt_close_section(ctx); return 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; }
static int restore_unix_rqueue(struct sock *sk, struct cpt_sock_image *si, loff_t pos, struct cpt_context *ctx) { loff_t endpos; pos = pos + si->cpt_hdrlen; endpos = pos + si->cpt_next; while (pos < endpos) { struct sk_buff *skb; struct sock *owner_sk; __u32 owner; skb = rst_skb(sk, &pos, &owner, NULL, ctx); if (IS_ERR(skb)) { if (PTR_ERR(skb) == -EINVAL) { int err; err = rst_sock_attr(&pos, sk, ctx); if (err) return err; } return PTR_ERR(skb); } owner_sk = unix_peer(sk); if (owner != -1) { cpt_object_t *pobj; pobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, owner, ctx); if (pobj == NULL) { eprintk_ctx("orphan af_unix skb?\n"); kfree_skb(skb); continue; } owner_sk = pobj->o_obj; } if (owner_sk == NULL) { dprintk_ctx("orphan af_unix skb 2?\n"); kfree_skb(skb); continue; } skb_set_owner_w(skb, owner_sk); if (UNIXCB(skb).fp) skb->destructor = unix_destruct_fds; skb_queue_tail(&sk->sk_receive_queue, skb); if (sk->sk_state == TCP_LISTEN) { struct socket *sock = skb->sk->sk_socket; if (sock == NULL) BUG(); if (sock->file) BUG(); skb->sk->sk_socket = NULL; skb->sk->sk_sleep = NULL; sock->sk = NULL; sock_release(sock); } } return 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; }
int rst_sk_mcfilter_in6(struct sock *sk, struct cpt_sockmc_image *v, loff_t pos, cpt_context_t *ctx) { if (v->cpt_mode || v->cpt_next != v->cpt_hdrlen) { eprintk_ctx("IGMPv3 is still not supported\n"); return -EINVAL; } return ipv6_sock_mc_join(sk, v->cpt_ifindex, (struct in6_addr*)v->cpt_mcaddr); }
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; }
int cpt_close_dumpfile(struct cpt_context *ctx) { if (ctx->file) { fput(ctx->file); ctx->file = NULL; } if (ctx->tmpbuf) { free_page((unsigned long)ctx->tmpbuf); ctx->tmpbuf = NULL; } if (ctx->write_error) eprintk_ctx("error while writing dump file: %d\n", ctx->write_error); return ctx->write_error; }
int rst_sk_mcfilter_in(struct sock *sk, struct cpt_sockmc_image *v, loff_t pos, cpt_context_t *ctx) { struct ip_mreqn imr; if (v->cpt_mode || v->cpt_next != v->cpt_hdrlen) { eprintk_ctx("IGMPv3 is still not supported\n"); return -EINVAL; } memset(&imr, 0, sizeof(imr)); imr.imr_ifindex = v->cpt_ifindex; imr.imr_multiaddr.s_addr = v->cpt_mcaddr[0]; return ip_mc_join_group(sk, &imr); }
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; }
static int select_deleted_name(char * name, cpt_context_t *ctx) { int i; for (i=0; i<100; i++) { struct nameidata nd; unsigned int rnd = net_random(); sprintf(name, "/tmp/SOCK.%08x", rnd); if (path_lookup(name, 0, &nd) != 0) return 0; path_put(&nd.path); } eprintk_ctx("failed to allocate deleted socket inode\n"); return -ELOOP; }
static int do_rst_aio(struct cpt_aio_ctx_image *aimg, loff_t pos, cpt_context_t *ctx) { int err; struct kioctx *aio_ctx; extern spinlock_t aio_nr_lock; aio_ctx = kmem_cache_alloc(kioctx_cachep, GFP_KERNEL); if (!aio_ctx) return -ENOMEM; memset(aio_ctx, 0, sizeof(*aio_ctx)); aio_ctx->max_reqs = aimg->cpt_max_reqs; if ((err = restore_aio_ring(aio_ctx, aimg)) < 0) { kmem_cache_free(kioctx_cachep, aio_ctx); eprintk_ctx("AIO %Ld restore_aio_ring: %d\n", pos, err); return err; } aio_ctx->mm = current->mm; atomic_inc(&aio_ctx->mm->mm_count); atomic_set(&aio_ctx->users, 1); spin_lock_init(&aio_ctx->ctx_lock); spin_lock_init(&aio_ctx->ring_info.ring_lock); init_waitqueue_head(&aio_ctx->wait); INIT_LIST_HEAD(&aio_ctx->active_reqs); INIT_LIST_HEAD(&aio_ctx->run_list); INIT_DELAYED_WORK(&aio_ctx->wq, aio_kick_handler); spin_lock(&aio_nr_lock); aio_nr += aio_ctx->max_reqs; spin_unlock(&aio_nr_lock); spin_lock(&aio_ctx->mm->ioctx_lock); hlist_add_head(&aio_ctx->list, &aio_ctx->mm->ioctx_list); spin_unlock(&aio_ctx->mm->ioctx_lock); return 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; }
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; }
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; }
static int cpt_dump_iptables(struct cpt_context * ctx) { int err = 0; #ifdef CONFIG_VE_IPTABLES int pid; int pfd[2]; struct file *f; struct cpt_object_hdr v; char buf[16]; loff_t pos; int n; int status; mm_segment_t oldfs; sigset_t ignore, blocked; struct args_t args; struct ve_struct *oldenv; if (!(get_exec_env()->_iptables_modules & VE_IP_IPTABLES_MOD)) return 0; err = sc_pipe(pfd); if (err < 0) { eprintk_ctx("sc_pipe: %d\n", err); return err; } args.pfd = pfd; args.veid = VEID(get_exec_env()); ignore.sig[0] = CPT_SIG_IGNORE_MASK; sigprocmask(SIG_BLOCK, &ignore, &blocked); oldenv = set_exec_env(get_ve0()); err = pid = local_kernel_thread(dumpfn, (void*)&args, SIGCHLD | CLONE_VFORK, 0); set_exec_env(oldenv); if (err < 0) { eprintk_ctx("local_kernel_thread: %d\n", err); goto out; } f = fget(pfd[0]); sc_close(pfd[1]); sc_close(pfd[0]); cpt_open_section(ctx, CPT_SECT_NET_IPTABLES); cpt_open_object(NULL, ctx); v.cpt_next = CPT_NULL; v.cpt_object = CPT_OBJ_NAME; v.cpt_hdrlen = sizeof(v); v.cpt_content = CPT_CONTENT_NAME; ctx->write(&v, sizeof(v), ctx); pos = ctx->file->f_pos; do { oldfs = get_fs(); set_fs(KERNEL_DS); n = f->f_op->read(f, buf, sizeof(buf), &f->f_pos); set_fs(oldfs); if (n > 0) ctx->write(buf, n, ctx); } while (n > 0); if (n < 0) eprintk_ctx("read: %d\n", n); 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-save exited with %d\n", err); err = -EINVAL; } } else {
static int bind_unix_socket(struct socket *sock, struct cpt_sock_image *si, cpt_context_t *ctx) { int err; char *name; struct sockaddr* addr; int addrlen; struct sockaddr_un sun; struct nameidata nd; if ((addrlen = si->cpt_laddrlen) <= 2) return 0; nd.path.dentry = NULL; name = ((char*)si->cpt_laddr) + 2; addr = (struct sockaddr *)si->cpt_laddr; if (name[0]) { if (path_lookup(name, 0, &nd)) nd.path.dentry = NULL; if (si->cpt_deleted) { if (nd.path.dentry == NULL && sock->ops->bind(sock, addr, addrlen) == 0) { sc_unlink(name); return 0; } addr = (struct sockaddr*)&sun; addr->sa_family = AF_UNIX; name = ((char*)addr) + 2; err = select_deleted_name(name, ctx); if (err) goto out; addrlen = 2 + strlen(name); } else if (nd.path.dentry) { if (!S_ISSOCK(nd.path.dentry->d_inode->i_mode)) { eprintk_ctx("bind_unix_socket: not a socket dentry\n"); err = -EINVAL; goto out; } sc_unlink(name); } } err = sock->ops->bind(sock, addr, addrlen); if (!err && name[0]) { if (nd.path.dentry) { sc_chown(name, nd.path.dentry->d_inode->i_uid, nd.path.dentry->d_inode->i_gid); sc_chmod(name, nd.path.dentry->d_inode->i_mode); } if (si->cpt_deleted) sc_unlink(name); } out: if (nd.path.dentry) path_put(&nd.path); return err; }
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; }
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; }
int rst_socket_in(struct cpt_sock_image *si, loff_t pos, struct sock *sk, struct cpt_context *ctx) { struct inet_sock *inet = inet_sk(sk); struct net *net = get_exec_env()->ve_ns->net_ns; int err, ret_err = 0; lock_sock(sk); sk->sk_state = si->cpt_state; inet->daddr = si->cpt_daddr; inet->dport = si->cpt_dport; inet->saddr = si->cpt_saddr; inet->rcv_saddr = si->cpt_rcv_saddr; inet->sport = si->cpt_sport; inet->uc_ttl = si->cpt_uc_ttl; inet->tos = si->cpt_tos; inet->cmsg_flags = si->cpt_cmsg_flags; inet->mc_index = si->cpt_mc_index; inet->mc_addr = si->cpt_mc_addr; inet->hdrincl = si->cpt_hdrincl; inet->mc_ttl = si->cpt_mc_ttl; inet->mc_loop = si->cpt_mc_loop; inet->pmtudisc = si->cpt_pmtudisc; inet->recverr = si->cpt_recverr; inet->freebind = si->cpt_freebind; inet->id = si->cpt_idcounter; inet->cork.flags = si->cpt_cork_flags; inet->cork.fragsize = si->cpt_cork_fragsize; inet->cork.length = si->cpt_cork_length; inet->cork.addr = si->cpt_cork_addr; inet->cork.fl.fl4_src = si->cpt_cork_saddr; inet->cork.fl.fl4_dst = si->cpt_cork_daddr; inet->cork.fl.oif = si->cpt_cork_oif; if (inet->cork.fragsize) { if (ip_route_output_key(net, (struct rtable **)&inet->cork.dst, &inet->cork.fl)) { eprintk_ctx("failed to restore cork route\n"); inet->cork.fragsize = 0; } } if (sk->sk_type == SOCK_DGRAM && sk->sk_protocol == IPPROTO_UDP) { struct udp_sock *up = udp_sk(sk); up->pending = si->cpt_udp_pending; up->corkflag = si->cpt_udp_corkflag; up->encap_type = si->cpt_udp_encap; up->len = si->cpt_udp_len; } if (sk->sk_family == AF_INET6) { struct ipv6_pinfo *np = inet6_sk(sk); memcpy(&np->saddr, si->cpt_saddr6, 16); memcpy(&np->rcv_saddr, si->cpt_rcv_saddr6, 16); memcpy(&np->daddr, si->cpt_daddr6, 16); np->flow_label = si->cpt_flow_label6; np->frag_size = si->cpt_frag_size6; np->hop_limit = si->cpt_hop_limit6; np->mcast_hops = si->cpt_mcast_hops6; np->mcast_oif = si->cpt_mcast_oif6; np->rxopt.all = si->cpt_rxopt6; np->mc_loop = si->cpt_mc_loop6; np->recverr = si->cpt_recverr6; np->sndflow = si->cpt_sndflow6; np->pmtudisc = si->cpt_pmtudisc6; np->ipv6only = si->cpt_ipv6only6; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) if (si->cpt_mapped) { extern struct inet_connection_sock_af_ops ipv6_mapped; if (sk->sk_type == SOCK_STREAM && sk->sk_protocol == IPPROTO_TCP) { inet_csk(sk)->icsk_af_ops = &ipv6_mapped; sk->sk_backlog_rcv = tcp_v4_do_rcv; } } #endif } err = restore_queues(sk, si, pos, ctx); if (sk->sk_type == SOCK_STREAM && sk->sk_protocol == IPPROTO_TCP) { ret_err = err; rst_socket_tcp(si, pos, sk, ctx); } release_sock(sk); return ret_err; }
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 {
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; }
static int open_listening_socket(loff_t pos, struct cpt_sock_image *si, struct cpt_context *ctx) { int err; struct socket *sock; struct file *file; cpt_object_t *obj, *fobj; err = sock_create(si->cpt_family, si->cpt_type, si->cpt_protocol, &sock); if (err) { eprintk_ctx("open_listening_socket: sock_create: %d\n", err); return err; } sock->sk->sk_reuse = 2; sock->sk->sk_bound_dev_if = si->cpt_bound_dev_if; if (sock->sk->sk_family == AF_UNIX) { err = bind_unix_socket(sock, si, ctx); } else if (si->cpt_laddrlen) { if (sock->sk->sk_family == AF_INET || sock->sk->sk_family == AF_INET6) inet_sk(sock->sk)->freebind = 1; err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, si->cpt_laddrlen); if (err) { eprintk_ctx("open_listening_socket: bind: %d\n", err); goto err_out; } } err = sock->ops->listen(sock, si->cpt_max_ack_backlog); if (err) { eprintk_ctx("open_listening_socket: listen: %d, %Ld, %d\n", err, pos, si->cpt_deleted); goto err_out; } /* Now we may access socket body directly and fixup all the things. */ file = sock_mapfile(sock); err = PTR_ERR(file); if (IS_ERR(file)) { eprintk_ctx("open_listening_socket: map: %d\n", err); goto err_out; } err = -ENOMEM; if ((fobj = cpt_object_add(CPT_OBJ_FILE, file, ctx)) == NULL) goto err_out; if ((obj = cpt_object_add(CPT_OBJ_SOCKET, sock->sk, ctx)) == NULL) goto err_out; cpt_obj_setpos(obj, pos, ctx); cpt_obj_setindex(obj, si->cpt_index, ctx); obj->o_parent = file; cpt_obj_setpos(fobj, si->cpt_file, ctx); cpt_obj_setindex(fobj, si->cpt_index, ctx); setup_sock_common(sock->sk, si, pos, ctx); if (si->cpt_family == AF_INET || si->cpt_family == AF_INET6) { rst_listen_socket_in(sock->sk, si, pos, ctx); rst_restore_synwait_queue(sock->sk, si, pos, ctx); } return 0; err_out: sock_release(sock); return err; }
static int rst_socket_tcp(struct cpt_sock_image *si, loff_t pos, struct sock *sk, struct cpt_context *ctx) { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; tp->pred_flags = si->cpt_pred_flags; tp->rcv_nxt = si->cpt_rcv_nxt; tp->snd_nxt = si->cpt_snd_nxt; tp->snd_una = si->cpt_snd_una; tp->snd_sml = si->cpt_snd_sml; tp->rcv_tstamp = tcp_jiffies_import(si->cpt_rcv_tstamp); tp->lsndtime = tcp_jiffies_import(si->cpt_lsndtime); tp->tcp_header_len = si->cpt_tcp_header_len; inet_csk(sk)->icsk_ack.pending = si->cpt_ack_pending; inet_csk(sk)->icsk_ack.quick = si->cpt_quick; inet_csk(sk)->icsk_ack.pingpong = si->cpt_pingpong; inet_csk(sk)->icsk_ack.blocked = si->cpt_blocked; inet_csk(sk)->icsk_ack.ato = si->cpt_ato; inet_csk(sk)->icsk_ack.timeout = jiffies_import(si->cpt_ack_timeout); inet_csk(sk)->icsk_ack.lrcvtime = tcp_jiffies_import(si->cpt_lrcvtime); inet_csk(sk)->icsk_ack.last_seg_size = si->cpt_last_seg_size; inet_csk(sk)->icsk_ack.rcv_mss = si->cpt_rcv_mss; tp->snd_wl1 = si->cpt_snd_wl1; tp->snd_wnd = si->cpt_snd_wnd; tp->max_window = si->cpt_max_window; inet_csk(sk)->icsk_pmtu_cookie = si->cpt_pmtu_cookie; tp->mss_cache = si->cpt_mss_cache; tp->rx_opt.mss_clamp = si->cpt_mss_clamp; inet_csk(sk)->icsk_ext_hdr_len = si->cpt_ext_header_len; inet_csk(sk)->icsk_ca_state = si->cpt_ca_state; inet_csk(sk)->icsk_retransmits = si->cpt_retransmits; tp->reordering = si->cpt_reordering; tp->frto_counter = si->cpt_frto_counter; tp->frto_highmark = si->cpt_frto_highmark; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) // // tp->adv_cong = si->cpt_adv_cong; #endif inet_csk(sk)->icsk_accept_queue.rskq_defer_accept = si->cpt_defer_accept; inet_csk(sk)->icsk_backoff = si->cpt_backoff; tp->srtt = si->cpt_srtt; tp->mdev = si->cpt_mdev; tp->mdev_max = si->cpt_mdev_max; tp->rttvar = si->cpt_rttvar; tp->rtt_seq = si->cpt_rtt_seq; inet_csk(sk)->icsk_rto = si->cpt_rto; tp->packets_out = si->cpt_packets_out; tp->retrans_out = si->cpt_retrans_out; tp->lost_out = si->cpt_lost_out; tp->sacked_out = si->cpt_sacked_out; tp->fackets_out = si->cpt_fackets_out; tp->snd_ssthresh = si->cpt_snd_ssthresh; tp->snd_cwnd = si->cpt_snd_cwnd; tp->snd_cwnd_cnt = si->cpt_snd_cwnd_cnt; tp->snd_cwnd_clamp = si->cpt_snd_cwnd_clamp; tp->snd_cwnd_used = si->cpt_snd_cwnd_used; tp->snd_cwnd_stamp = tcp_jiffies_import(si->cpt_snd_cwnd_stamp); inet_csk(sk)->icsk_timeout = tcp_jiffies_import(si->cpt_timeout); tp->rcv_wnd = si->cpt_rcv_wnd; tp->rcv_wup = si->cpt_rcv_wup; tp->write_seq = si->cpt_write_seq; tp->pushed_seq = si->cpt_pushed_seq; tp->copied_seq = si->cpt_copied_seq; tp->rx_opt.tstamp_ok = si->cpt_tstamp_ok; tp->rx_opt.wscale_ok = si->cpt_wscale_ok; tp->rx_opt.sack_ok = si->cpt_sack_ok; tp->rx_opt.saw_tstamp = si->cpt_saw_tstamp; tp->rx_opt.snd_wscale = si->cpt_snd_wscale; tp->rx_opt.rcv_wscale = si->cpt_rcv_wscale; tp->nonagle = si->cpt_nonagle; tp->keepalive_probes = si->cpt_keepalive_probes; tp->rx_opt.rcv_tsval = si->cpt_rcv_tsval; tp->rx_opt.rcv_tsecr = si->cpt_rcv_tsecr; tp->rx_opt.ts_recent = si->cpt_ts_recent; tp->rx_opt.ts_recent_stamp = si->cpt_ts_recent_stamp; tp->rx_opt.user_mss = si->cpt_user_mss; tp->rx_opt.dsack = si->cpt_dsack; tp->duplicate_sack[0].start_seq = si->cpt_sack_array[0]; tp->duplicate_sack[0].end_seq = si->cpt_sack_array[1]; tp->selective_acks[0].start_seq = si->cpt_sack_array[2]; tp->selective_acks[0].end_seq = si->cpt_sack_array[3]; tp->selective_acks[1].start_seq = si->cpt_sack_array[4]; tp->selective_acks[1].end_seq = si->cpt_sack_array[5]; tp->selective_acks[2].start_seq = si->cpt_sack_array[6]; tp->selective_acks[2].end_seq = si->cpt_sack_array[7]; tp->selective_acks[3].start_seq = si->cpt_sack_array[8]; tp->selective_acks[3].end_seq = si->cpt_sack_array[9]; tp->window_clamp = si->cpt_window_clamp; tp->rcv_ssthresh = si->cpt_rcv_ssthresh; inet_csk(sk)->icsk_probes_out = si->cpt_probes_out; tp->rx_opt.num_sacks = si->cpt_num_sacks; tp->advmss = si->cpt_advmss; inet_csk(sk)->icsk_syn_retries = si->cpt_syn_retries; tp->ecn_flags = si->cpt_ecn_flags; tp->prior_ssthresh = si->cpt_prior_ssthresh; tp->high_seq = si->cpt_high_seq; tp->retrans_stamp = si->cpt_retrans_stamp; tp->undo_marker = si->cpt_undo_marker; tp->undo_retrans = si->cpt_undo_retrans; tp->urg_seq = si->cpt_urg_seq; tp->urg_data = si->cpt_urg_data; inet_csk(sk)->icsk_pending = si->cpt_pending; tp->snd_up = si->cpt_snd_up; tp->keepalive_time = si->cpt_keepalive_time; tp->keepalive_intvl = si->cpt_keepalive_intvl; tp->linger2 = si->cpt_linger2; sk->sk_send_head = NULL; for (skb = skb_peek(&sk->sk_write_queue); skb && skb != (struct sk_buff*)&sk->sk_write_queue; skb = skb->next) { if (!after(tp->snd_nxt, TCP_SKB_CB(skb)->seq)) { sk->sk_send_head = skb; break; } } if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN) { struct inet_sock *inet = inet_sk(sk); if (inet->num == 0) { cpt_object_t *lobj = NULL; if ((int)si->cpt_parent != -1) lobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, si->cpt_parent, ctx); if (lobj && lobj->o_obj) { inet->num = ntohs(inet->sport); local_bh_disable(); __inet_inherit_port(lobj->o_obj, sk); local_bh_enable(); dprintk_ctx("port inherited from parent\n"); } else { struct sock *lsk = find_parent(inet->sport, ctx); if (lsk) { inet->num = ntohs(inet->sport); local_bh_disable(); __inet_inherit_port(lsk, sk); local_bh_enable(); dprintk_ctx("port inherited\n"); } else { eprintk_ctx("we are kinda lost...\n"); } } } sk->sk_prot->hash(sk); if (inet_csk(sk)->icsk_ack.pending&ICSK_ACK_TIMER) sk_reset_timer(sk, &inet_csk(sk)->icsk_delack_timer, inet_csk(sk)->icsk_ack.timeout); if (inet_csk(sk)->icsk_pending) sk_reset_timer(sk, &inet_csk(sk)->icsk_retransmit_timer, inet_csk(sk)->icsk_timeout); if (sock_flag(sk, SOCK_KEEPOPEN)) { unsigned long expires = jiffies_import(si->cpt_ka_timeout); if (time_after(jiffies, expires)) expires = jiffies + HZ; sk_reset_timer(sk, &sk->sk_timer, expires); } } if (sk->sk_family == AF_INET6) sk->sk_gso_type = SKB_GSO_TCPV6; else sk->sk_gso_type = SKB_GSO_TCPV4; return 0; }
static int cpt_dump_route(struct cpt_context * ctx) { int err; struct socket *sock; struct msghdr msg; struct iovec iov; struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; struct sockaddr_nl nladdr; struct cpt_object_hdr v; mm_segment_t oldfs; char *pg; err = sock_create(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, &sock); if (err) return err; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_type = RTM_GETROUTE; req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; req.nlh.nlmsg_pid = 0; req.g.rtgen_family = AF_INET; iov.iov_base=&req; iov.iov_len=sizeof(req); 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, sizeof(req)); set_fs(oldfs); if (err < 0) goto out_sock; pg = (char*)__get_free_page(GFP_KERNEL); if (pg == NULL) { err = -ENOMEM; goto out_sock; } cpt_open_section(ctx, CPT_SECT_NET_ROUTE); cpt_open_object(NULL, ctx); v.cpt_next = CPT_NULL; v.cpt_object = CPT_OBJ_NET_ROUTE; v.cpt_hdrlen = sizeof(v); v.cpt_content = CPT_CONTENT_NLMARRAY; ctx->write(&v, sizeof(v), ctx); #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) restart: #endif for (;;) { struct nlmsghdr *h; 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 < 0) goto out_sock_pg; if (msg.msg_flags & MSG_TRUNC) { err = -ENOBUFS; goto out_sock_pg; } h = (struct nlmsghdr*)pg; while (NLMSG_OK(h, err)) { if (h->nlmsg_type == NLMSG_DONE) { err = 0; goto done; } if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *errm = (struct nlmsgerr*)NLMSG_DATA(h); err = errm->error; eprintk_ctx("NLMSG error: %d\n", errm->error); goto done; } if (h->nlmsg_type != RTM_NEWROUTE) { eprintk_ctx("NLMSG: %d\n", h->nlmsg_type); err = -EINVAL; goto done; } ctx->write(h, NLMSG_ALIGN(h->nlmsg_len), ctx); h = NLMSG_NEXT(h, err); } if (err) { eprintk_ctx("!!!Remnant of size %d %d %d\n", err, h->nlmsg_len, h->nlmsg_type); err = -EINVAL; break; } } done: #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) if (!err && req.g.rtgen_family == AF_INET) { req.g.rtgen_family = AF_INET6; iov.iov_base=&req; iov.iov_len=sizeof(req); 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, sizeof(req)); set_fs(oldfs); if (err > 0) goto restart; } #endif ctx->align(ctx); cpt_close_object(ctx); cpt_close_section(ctx); out_sock_pg: free_page((unsigned long)pg); out_sock: sock_release(sock); return err; }
static int restore_queues(struct sock *sk, struct cpt_sock_image *si, loff_t pos, struct cpt_context *ctx) { loff_t endpos; endpos = pos + si->cpt_next; pos = pos + si->cpt_hdrlen; while (pos < endpos) { struct sk_buff *skb; __u32 type; int err; err = rst_sock_attr(&pos, sk, ctx); if (!err) continue; if (err < 0) return err; skb = rst_skb(sk, &pos, NULL, &type, ctx); if (IS_ERR(skb)) return PTR_ERR(skb); if (sk->sk_type == SOCK_STREAM) { if (type == CPT_SKB_RQ) { skb_set_owner_r(skb, sk); ub_tcprcvbuf_charge_forced(sk, skb); skb_queue_tail(&sk->sk_receive_queue, skb); } else if (type == CPT_SKB_OFOQ) { struct tcp_sock *tp = tcp_sk(sk); skb_set_owner_r(skb, sk); ub_tcprcvbuf_charge_forced(sk, skb); skb_queue_tail(&tp->out_of_order_queue, skb); } else if (type == CPT_SKB_WQ) { sk->sk_wmem_queued += skb->truesize; sk->sk_forward_alloc -= skb->truesize; ub_tcpsndbuf_charge_forced(sk, skb); skb_queue_tail(&sk->sk_write_queue, skb); } else { wprintk_ctx("strange stream queue type %u\n", type); kfree_skb(skb); } } else { if (type == CPT_SKB_RQ) { skb_set_owner_r(skb, sk); skb_queue_tail(&sk->sk_receive_queue, skb); } else if (type == CPT_SKB_WQ) { struct inet_sock *inet = inet_sk(sk); if (inet->cork.fragsize) { skb_set_owner_w(skb, sk); skb_queue_tail(&sk->sk_write_queue, skb); } else { eprintk_ctx("cork skb is dropped\n"); kfree_skb(skb); } } else { wprintk_ctx("strange dgram queue type %u\n", type); kfree_skb(skb); } } } return 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; } }
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; }
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; }