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; }
/* 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 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; }
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; }
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_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 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; }
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 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; }
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 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 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_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_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_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 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; }
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; } }