Index: projects/vps/sys/vps/vps_libdump.h =================================================================== --- projects/vps/sys/vps/vps_libdump.h (revision 256999) +++ projects/vps/sys/vps/vps_libdump.h (revision 257000) @@ -1,1049 +1,1056 @@ /*- * Copyright (c) 2009-2013 Klaus P. Ohrhallinger * All rights reserved. * * Development of this software was partly funded by: * TransIP.nl * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* $Id: vps_libdump.h 162 2013-06-06 18:17:55Z klaus $ */ #ifndef _VPS_LIBDUMP_H #define _VPS_LIBDUMP_H #include #ifndef _KERNEL struct vps_snapst_ctx { void *data; void *cpos; long dsize; long maxsize; struct vps_dumpobj *rootobj; struct vps_dumpobj *lastobj; struct vps_dumpobj *curobj; int level; int elements; char relative; }; struct vps_dumpheader; /* object functions */ struct vps_dumpobj *vps_dumpobj_create(struct vps_snapst_ctx *ctx, int type, int how); void *vps_dumpobj_space(struct vps_snapst_ctx *ctx, long size, int how); int vps_dumpobj_append(struct vps_snapst_ctx *ctx, const void *data, long size, int how); void vps_dumpobj_close(struct vps_snapst_ctx *ctx); void vps_dumpobj_discard(struct vps_snapst_ctx *ctx, struct vps_dumpobj *o); int vps_dumpobj_checkobj(struct vps_snapst_ctx *ctx, struct vps_dumpobj *o); void vps_dumpobj_setcur(struct vps_snapst_ctx *ctx, struct vps_dumpobj *o); struct vps_dumpobj *vps_dumpobj_next(struct vps_snapst_ctx *ctx); struct vps_dumpobj *vps_dumpobj_prev(struct vps_snapst_ctx *ctx); struct vps_dumpobj *vps_dumpobj_peek(struct vps_snapst_ctx *ctx); struct vps_dumpobj *vps_dumpobj_getcur(struct vps_snapst_ctx *ctx); int vps_dumpobj_typeofnext(struct vps_snapst_ctx *ctx); int vps_dumpobj_nextischild(struct vps_snapst_ctx *ctx, struct vps_dumpobj *op); int vps_dumpobj_recurse(struct vps_snapst_ctx *ctx, struct vps_dumpobj *o, void (*func)(struct vps_snapst_ctx *ctx, struct vps_dumpobj *)); /* tree functions */ int vps_dumpobj_makerelative(struct vps_snapst_ctx *ctx); int vps_dumpobj_makeabsolute(struct vps_snapst_ctx *ctx); int vps_dumpobj_printtree(struct vps_snapst_ctx *ctx); int vps_dumpobj_checktree(struct vps_snapst_ctx *ctx); /* various subroutines */ int vps_dumpobj_checkptr(struct vps_snapst_ctx *ctx, void *p, size_t off); const char *vps_libdump_objtype2str(int objt); int vps_libdump_checkheader(struct vps_dumpheader *h); void vps_libdump_printheader(struct vps_dumpheader *h); #endif /* !_KERNEL */ #define VPS_DUMPOBJT_ROOT 2 #define VPS_DUMPOBJT_SYSINFO 3 #define VPS_DUMPOBJT_VPS 4 #define VPS_DUMPOBJT_ARG 6 #define VPS_DUMPOBJT_END 9 #define VPS_DUMPOBJT_PROC 10 #define VPS_DUMPOBJT_THREAD 12 #define VPS_DUMPOBJT_PGRP 14 #define VPS_DUMPOBJT_SESSION 15 #define VPS_DUMPOBJT_SAVEFPU 18 #define VPS_DUMPOBJT_SYSENTVEC 19 #define VPS_DUMPOBJT_VMSPACE 20 #define VPS_DUMPOBJT_VMMAPENTRY 22 #define VPS_DUMPOBJT_VMOBJECT 23 #define VPS_DUMPOBJT_VMPAGE 24 #define VPS_DUMPOBJT_VMOBJ_VNPATH 25 #define VPS_DUMPOBJT_FDSET 30 #define VPS_DUMPOBJT_FILE 31 #define VPS_DUMPOBJT_FILE_PATH 32 #define VPS_DUMPOBJT_PTS 33 #define VPS_DUMPOBJT_PIPE 34 #define VPS_DUMPOBJT_PARGS 35 #define VPS_DUMPOBJT_SOCKET 36 #define VPS_DUMPOBJT_SOCKBUF 37 #define VPS_DUMPOBJT_MBUFCHAIN 38 #define VPS_DUMPOBJT_SOCKET_UNIX 39 #define VPS_DUMPOBJT_MOUNT 40 #define VPS_DUMPOBJT_VNET_IFACE 50 #define VPS_DUMPOBJT_VNET_ADDR 52 #define VPS_DUMPOBJT_VNET_ROUTETABLE 55 #define VPS_DUMPOBJT_VNET_ROUTE 56 #define VPS_DUMPOBJT_VNET 59 #define VPS_DUMPOBJT_SYSVSEM_VPS 70 #define VPS_DUMPOBJT_SYSVSEM_PROC 71 #define VPS_DUMPOBJT_SYSVSHM_VPS 72 #define VPS_DUMPOBJT_SYSVSHM_PROC 73 #define VPS_DUMPOBJT_SYSVMSG_VPS 74 #define VPS_DUMPOBJT_SYSVMSG_PROC 75 #define VPS_DUMPOBJT_KQUEUE 80 #define VPS_DUMPOBJT_KNOTE 81 #define VPS_DUMPOBJT_KEVENT 82 #define VPS_DUMPOBJT_UMTX 90 +#define VPS_DUMPOBJT_FILE_INODENUM 95 #define VPS_DUMPOBJT_PRISON 100 #define VPS_DUMPOBJT_UCRED 120 #define VPS_DUMPH_MAGIC 0xc0debabe -#define VPS_DUMPH_VERSION 0x20130719 +#define VPS_DUMPH_VERSION 0x20130729 #define VPS_DUMPH_MSB 12 #define VPS_DUMPH_LSB 21 #define VPS_DUMPH_32BIT 32 #define VPS_DUMPH_64BIT 64 #if defined(VPS_ARCH_AMD64) /* AMD64 declarations */ typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32; typedef unsigned long uint64; typedef signed char sint8; typedef signed short sint16; typedef signed int sint32; typedef signed long sint64; typedef unsigned char byte; typedef void * ptr; typedef uint64 offset; #define PTR(x) ptr x #define ALIGN_MASK 0x7 #define PTRTO64(x) (uint64)(x) #define PTRFROM64(x) (void *)(x) #ifndef PAGE_SHIFT #define PAGE_SHIFT 12 #endif #ifndef _KERNEL /* typedef unsigned long size_t; */ #endif /* end amd64 declarations */ #elif defined(VPS_ARCH_I386) /* i386 declarations */ typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32; typedef unsigned long long uint64; typedef signed char sint8; typedef signed short sint16; typedef signed int sint32; typedef signed long long sint64; typedef unsigned char byte; typedef void * ptr; typedef uint32 offset; #define PTR(x) ptr x; uint32 _pad_##x #define ALIGN_MASK 0x3 #define PTRTO64(x) (uint64)(uint32)(x) #define PTRFROM64(x) (void *)(uint32)(x) #ifndef PAGE_SHIFT #define PAGE_SHIFT 12 #endif #ifndef _KERNEL typedef unsigned int size_t; #endif /* end i386 declarations */ #elif defined(VPS_ARCH_MIPSEB64) /* mipseb64 declarations */ typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32; typedef unsigned long uint64; typedef signed char sint8; typedef signed short sint16; typedef signed int sint32; typedef signed long sint64; typedef unsigned char byte; typedef void * ptr; typedef uint64 offset; #define PTR(x) ptr x #define ALIGN_MASK 0x7 #define PTRTO64(x) (uint64)(x) #define PTRFROM64(x) (void *)(x) #ifndef PAGE_SHIFT #define PAGE_SHIFT 12 #endif #ifndef _KERNEL /* typedef unsigned long size_t; */ #endif /* end mipseb64 declarations */ #else #error "unsupported architecture" #endif struct vps_dumpobj { uint32 magic; /* for debugging purposes; 0x0 or 0xc0debabe */ uint16 type; uint16 level; /* level this object is in */ uint32 size; /* size of this object including it's header */ sint16 prio; /* priority; 0 == any */ uint16 pad0; PTR(parent); /* offset to parent object (from start of snapshot) */ PTR(next); /* offset to next object (from start of snapshot) */ PTR(list_children); /* internal: SLIST_ENTRY(vps_dumpobj) list */ PTR(list_siblings); /* internal: SLIST_ENTRY(vps_dumpobj) list */ byte data[0]; /* amount of data specified by 'size' */ /* next object is always aligned to 'ptrsize' */ }; struct vps_dumpheader { uint8 byteorder; /* 0d12 == MSB; 0d21 == LSB */ uint8 ptrsize; /* 0d32 == 32 bits; 0d64 == 64 bits; ... */ uint8 pageshift; /* e.g. 0d12 for 4096 byte pages */ byte pad0[5]; uint32 version; /* date in hexadecimal; e.g. 0x20120518 */ uint32 magic; sint64 time; uint64 size; uint64 checksum; uint32 nsyspages; uint32 nuserpages; }; #if 0 /* Example of a vps_snapst_ctx. */ struct vps_snapst_ctx { void *data; void *cpos; long dsize; long maxsize; struct vps_dumpobj *rootobj; struct vps_dumpobj *lastobj; struct vps_dumpobj *curobj; int level; int elements; char relative; }; #endif /* * functions */ /* * dump object types */ struct vps_dump_sysinfo { char kernel[0x100]; char hostname[0x100]; PTR(shared_page_obj); }; struct vps_dump_vps { char hostname[0x100]; char vps_name[0x100]; char rootpath[0x400]; struct { sint64 tv_sec; sint64 tv_usec; } boottime; sint32 lastpid; sint32 initpgrp_id; sint32 initproc_id; uint32 restore_count; }; struct vps_dump_mount { char mntfrom[0x80]; char mnton[0x80]; char fstype[0x10]; uint8 vpsmount; uint8 optcnt; uint8 _pad0[6]; uint64 flags; PTR(mnt_cred); }; struct vps_dump_mount_opt { char name[0x40]; char value[0x100]; uint16 len; uint16 _pad0[3]; }; struct vps_dump_vnet { PTR(orig_ptr); }; struct vps_dump_vnet_ifnet { char if_dname[0x10]; char if_xname[0x10]; uint32 if_dunit; uint32 if_flags; }; struct vps_dump_vnet_ifaddr { uint8 have_addr; uint8 have_dstaddr; uint8 have_netmask; uint8 _pad0[5]; }; // #define SOCK_MAXADDRLEN 255 /* longest possible addresses */ struct vps_dump_vnet_sockaddr { uint16 sa_len; uint16 sa_family; uint32 _pad0; char sa_data[0x100]; }; struct vps_dump_vnet_inet6_lifetime { sint64 ia6t_expire; sint64 ia6t_preferred; uint32 ia6t_vltime; uint32 ia6t_pltime; }; struct vps_dump_ucred { uint32 cr_uid; uint32 cr_ruid; uint32 cr_svuid; sint32 cr_ngroups; uint32 cr_rgid; uint32 cr_svgid; uint32 cr_flags; uint32 cr_ref; PTR(cr_origptr); PTR(cr_prison); PTR(cr_vps); uint32 cr_groups[0]; /* always padded to 64 bit alignment */ }; struct vps_dump_prison { uint32 pr_id; uint32 pr_securelevel; uint32 pr_enforce_statfs; uint32 pr_childmax; uint32 pr_ip4s; uint32 pr_ip6s; uint64 pr_flags; uint64 pr_allow; char pr_name[0x100]; char pr_path[0x400]; PTR(pr_root); PTR(pr_origptr); PTR(pr_parent); char pr_ipdata[0]; }; struct vps_dump_pgrp { uint32 pg_id; uint32 pg_jobc; uint32 pg_session_id; uint32 _pad0; }; struct vps_dump_session { uint32 s_sid; uint32 s_leader_id; uint32 s_count; uint8 s_have_ttyvp; uint8 _pad0[3]; char s_login[0x30]; }; struct vps_dump_proc { sint32 p_pid; uint32 p_swtick; char p_comm[0x20]; sint64 p_cpulimit; sint32 p_flag; sint32 p_state; uint32 p_stops; uint32 p_stype; sint8 p_nice; sint8 p_step; uint16 p_xstat; sint32 p_oppid; uint32 p_xthread_id; sint32 p_sigparent; PTR(p_ucred); uint8 p_have_tracevp; uint8 p_have_textvp; uint16 _pad0; sint32 p_traceflag; PTR(p_tracecred); sint32 p_pptr_id; sint32 p_peers_id; sint32 p_leader_id; sint32 p_pgrp_id; PTR(p_fd); PTR(p_vmspace); struct { uint32 ps_maxsig; uint32 ps_sigwords; uint64 ps_sigact[0x80]; uint32 ps_catchmask[0x80][0x4]; uint32 ps_sigonstack[0x4]; uint32 ps_sigintr[0x4]; uint32 ps_sigreset[0x4]; uint32 ps_signodefer[0x4]; uint32 ps_siginfo[0x4]; uint32 ps_sigignore[0x4]; uint32 ps_sigcatch[0x4]; uint32 ps_freebsd4[0x4]; uint32 ps_osigset[0x4]; uint32 ps_usertramp[0x4]; uint32 ps_flag; uint32 _pad0; } p_sigacts; struct { uint32 pl_nlimits; uint32 _pad1; struct { sint64 rlim_cur; sint64 rlim_max; } pl_rlimit[0x10]; } p_limit; }; struct vps_dump_pargs { uint32 ar_length; uint32 _pad0; char ar_args[0]; /* always padded to 64 bit alignment */ }; struct vps_dump_savefpu { uint32 sf_length; uint32 _pad0; char sf_data[0]; /* always padded to 64 bit alignment */ }; struct vps_dump_sysentvec { uint32 sv_type; uint32 _pad0; }; struct vps_dump_vmmap { uint64 minoffset; uint64 maxoffset; }; struct vps_dump_vmspace { PTR(vm_orig_ptr); struct vps_dump_vmmap vm_map; uint64 vm_tsize; uint64 vm_dsize; uint64 vm_ssize; }; struct vps_dump_vmmapentry { PTR(map_object); PTR(cred); uint32 eflags; uint8 protection; uint8 max_protection; sint8 inheritance; uint8 _pad0; uint64 offset; uint64 start; uint64 end; uint64 avail_ssize; }; struct vps_dump_vmobject { PTR(orig_ptr); PTR(cred); PTR(backing_object); uint16 flags; uint8 type; uint8 have_vnode; uint8 is_sharedpageobj; uint8 _pad0[3]; uint64 size; uint64 charge; uint64 backing_object_offset; }; struct vps_dump_vmpages { uint64 count; }; struct vps_dump_thread { struct { uint64 ss_sp; uint64 ss_size; sint32 ss_flags; sint32 _pad0; } td_sigstk; uint32 td_sigmask[0x4]; uint32 td_oldsigmask[0x4]; sint32 td_xsig; sint32 td_dbgflags; uint8 td_rqindex; uint8 td_base_pri; uint8 td_priority; uint8 td_pri_class; uint8 td_user_pri; uint8 td_base_user_pri; uint8 _pad1[2]; uint64 td_retval[2]; sint32 td_errno; uint32 _pad2; uint64 td_spare[4]; sint32 td_tid; uint32 td_kstack_pages; char td_kstack[0]; /* always padded to 64 bit alignment */ }; struct vps_dump_filedesc { PTR(fd_orig_ptr); uint8 fd_have_cdir; uint8 fd_have_rdir; uint8 fd_have_jdir; uint8 _pad0[5]; uint32 fd_nfiles; uint32 _pad1; struct { PTR(fp); uint8 flags; uint8 _pad0[7]; uint64 rights; } fd_entries[0]; }; struct vps_dump_file { uint32 flags; uint32 _pad0; PTR(orig_ptr); PTR(f_cred); uint64 f_offset; uint32 f_flag; sint16 f_type; uint16 _pad1; }; struct vps_dump_pipe { uint8 pi_have_dumped_pipe; uint8 _pad0[7]; PTR(pi_localend); PTR(pi_pair); PTR(pi_rpipe); PTR(pi_wpipe); }; struct vps_dump_filepath { uint32 fp_size; uint32 _pad0; char fp_path[0]; /* always padded to 64 bit alignment */ +}; + +struct vps_dump_fileinodenum { + uint64 fsid; + sint32 fileid; + uint32 _pad0; }; struct vps_dump_pts { sint32 pt_index; sint32 pt_pgrp_id; uint32 pt_flags; uint32 _pad0; PTR(pt_cred); struct { uint32 c_iflag; uint32 c_oflag; uint32 c_cflag; uint32 c_lflag; uint8 c_cc[0x20]; uint32 c_ispeed; uint32 c_ospeed; } pt_termios; }; struct vps_dump_socket { PTR(so_orig_ptr); PTR(so_cred); sint16 so_family; sint16 so_type; sint16 so_protocol; sint16 _pad0; sint16 so_options; uint16 so_qlimit; sint16 so_state; sint16 _pad1; sint32 so_qstate; uint16 so_qlen; uint16 so_incqlen; }; struct vps_dump_unixpcb { uint8 unp_have_conn; uint8 unp_have_addr; uint8 unp_have_vnode; uint8 _pad0[5]; sint16 unp_flags; sint16 _pad1; uint32 _pad2; sint32 unp_cc; sint32 unp_mbcnt; PTR(unp_socket); PTR(unp_conn_socket); struct { uint32 cr_uid; uint16 cr_ngroups; uint16 _pad3; uint32 cr_groups[16]; } unp_peercred; }; struct vps_dump_inetpcb { uint8 inp_vflag; uint8 inp_ip_p; uint8 inp_have_ppcb; uint8 _pad0[5]; sint32 inp_flags; sint32 inp_flags2; struct { uint8 inc_flags; uint8 inc_len; uint16 inc_fibnum; uint32 _pad0; uint8 ie_ufaddr[0x10]; uint8 ie_uladdr[0x10]; uint16 ie_fport; uint16 ie_lport; uint32 _pad1; } inp_inc; }; struct vps_dump_udppcb { uint8 u_have_tun_func; uint8 _pad0[3]; uint32 u_flags; }; struct vps_dump_tcppcb { sint32 t_state; sint32 t_flags; uint32 snd_una; uint32 snd_max; uint32 snd_nxt; uint32 snd_up; uint32 snd_wl1; uint32 snd_wl2; uint32 iss; uint32 irs; uint32 rcv_nxt; uint32 rcv_adv; uint64 rcv_wnd; uint32 rcv_up; uint32 _pad0; uint64 snd_wnd; uint64 snd_cwnd; uint64 snd_ssthresh; }; struct vps_dump_sockbuf { PTR(sb_mb); PTR(sb_mbtail); PTR(sb_lastrecord); PTR(sb_sndptr); sint16 sb_state; sint16 sb_flags; uint32 sb_sndptroff; uint32 sb_cc; uint32 sb_hiwat; uint32 sb_mbcnt; uint32 sb_mcnt; uint32 sb_ccnt; uint32 sb_mbmax; uint32 sb_ctl; sint32 sb_lowat; uint32 sb_timeo; uint32 _pad0; }; struct vps_dump_mbufchain { uint32 mc_mbcount; uint32 _pad0; }; struct vps_dump_mbuf { PTR(mb_orig_ptr); sint16 mb_type; sint16 _pad0[3]; sint32 mb_len; sint32 mb_flags; uint8 mb_have_dat; uint8 mb_have_ext; uint8 mb_have_data; uint8 _pad1[5]; /* uint32 mb_dat_size; uint32 mb_ext_size; */ uint32 mb_payload_size; uint32 _pad2; uint32 mb_data_off; uint32 mb_checksum; char mb_payload[0]; /* always padded to 64 bit alignment */ }; struct vps_dump_vmpageref { PTR(pr_vmobject); uint64 pr_pindex; }; struct vps_dump_route { sint32 rt_flags; uint32 rt_fibnum; uint8 rt_have_mask; uint8 rt_have_gateway; uint8 rt_have_ifp; uint8 rt_have_ifa; uint8 _pad0[4]; struct { uint64 rmx_mtu; uint64 rmx_expire; uint64 rmx_pksent; uint64 rmx_weight; } rt_rmx; }; struct vps_dump_knote { sint32 kn_status; uint32 _pad0; sint16 ke_filter; uint16 ke_flags; uint32 ke_fflags; uint64 ke_ident; uint64 ke_data; PTR(ke_udata); }; struct vps_dump_accounting_val { uint64 cur; uint64 cnt_cur; uint64 soft; uint64 hard; uint32 hits_soft; uint32 hits_hard; }; struct vps_dump_accounting { struct vps_dump_accounting_val virt; struct vps_dump_accounting_val phys; struct vps_dump_accounting_val kmem; struct vps_dump_accounting_val kernel; struct vps_dump_accounting_val buffer; struct vps_dump_accounting_val pctcpu; struct vps_dump_accounting_val blockio; struct vps_dump_accounting_val threads; struct vps_dump_accounting_val procs; }; struct vps_dump_arg_ip4 { uint8 a4_addr[0x4]; uint8 a4_mask[0x4]; }; struct vps_dump_arg_ip6 { uint8 a6_addr[0x10]; uint8 a6_plen; uint8 _pad0[7]; }; struct vps_dump_arg { uint32 ip4net_cnt; uint32 ip6net_cnt; uint32 privset_size; uint8 have_accounting; uint8 _pad0[3]; }; struct vps_dump_sysv_ipcperm { uint32 cuid; uint32 cgid; uint32 uid; uint32 gid; uint16 mode; uint16 seq; uint32 _pad0; sint64 key; }; /* sysv msg */ struct vps_dump_sysvmsg_msginfo { sint32 msgmax; sint32 msgmni; sint32 msgmnb; sint32 msgtql; sint32 msgssz; sint32 msgseg; sint32 nfree_msgmaps; sint32 free_msgmaps; uint64 free_msghdrs_idx; }; struct vps_dump_sysvmsg_msg { sint64 msg_next; sint64 msg_type; uint16 msg_ts; sint16 msg_spot; uint32 _pad0; PTR(label); }; struct vps_dump_sysvmsg_msqid { struct vps_dump_sysv_ipcperm msg_perm; sint64 msg_first; sint64 msg_last; uint64 msg_cbytes; uint64 msg_qnum; uint64 msg_qbytes; sint32 msg_lspid; sint32 msg_lrpid; uint64 msg_stime; uint64 msg_rtime; uint64 msg_ctime; PTR(label); PTR(cred); }; /* sysv sem */ struct vps_dump_sysvsem_seminfo { sint32 semmni; sint32 semmns; sint32 semmnu; sint32 semmsl; sint32 semopm; sint32 semume; sint32 semusz; sint32 semvmx; sint32 semaem; sint32 _pad0; sint32 semtot; sint32 semundo_active; }; struct vps_dump_sysvsem_semid { struct vps_dump_sysv_ipcperm sem_perm; sint64 sem_base; uint16 sem_nsems; uint16 _pad0[3]; uint64 sem_otime; uint64 sem_ctime; PTR(label); PTR(cred); }; struct vps_dump_sysvsem_sem { uint16 semval; uint16 _pad0[3]; sint32 sempid; uint16 semncnt; uint16 semzcnt; }; struct vps_dump_sysvsem_sem_undo { sint32 un_proc; sint16 un_cnt; uint16 _pad0; struct { sint16 un_adjval; sint16 un_num; sint32 un_id; uint16 un_seq; uint16 _pad1[3]; } un_ent[0]; }; /* sysv shm */ struct vps_dump_sysvshm_shmid { struct vps_dump_sysv_ipcperm shm_perm; uint64 shm_segsz; sint32 shm_lpid; sint32 shm_cpid; sint32 shm_nattch; sint32 _pad0; uint64 shm_atime; uint64 shm_dtime; uint64 shm_ctime; PTR(object); PTR(label); PTR(cred); }; struct vps_dump_sysvshm_shminfo { uint64 shmmax; uint64 shmmin; uint64 shmmni; uint64 shmseg; uint64 shmall; sint32 shm_last_free; sint32 shm_nused; sint32 shmalloced; sint32 _pad0; uint64 shm_committed; struct vps_dump_sysvshm_shmid shmsegs[0]; }; struct vps_dump_sysvshm_shmmap_state { uint64 va; sint32 shmid; uint32 _pad0; }; #endif /*_VPS_LIBDUMP_H*/ /* EOF */ Index: projects/vps/sys/vps/vps_restore.c =================================================================== --- projects/vps/sys/vps/vps_restore.c (revision 256999) +++ projects/vps/sys/vps/vps_restore.c (revision 257000) @@ -1,5260 +1,5348 @@ /*- * Copyright (c) 2009-2013 Klaus P. Ohrhallinger * All rights reserved. * * Development of this software was partly funded by: * TransIP.nl * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ static const char vpsid[] = "$Id: vps_restore.c 164 2013-06-10 12:46:17Z klaus $"; #include #include "opt_ddb.h" #include "opt_ktrace.h" #include "opt_global.h" #include "opt_compat.h" #include "opt_kstack_max_pages.h" #ifdef VPS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vps_account.h" #include "vps_user.h" #include "vps.h" #include "vps2.h" #include #define _VPS_SNAPST_H_ALL #include "vps_snapst.h" #include "vps_libdump.h" /* see vm/vm_glue.c */ #ifndef KSTACK_MAX_PAGES #define KSTACK_MAX_PAGES 32 #endif #define ERRMSG vps_snapst_pusherrormsg #ifdef DIAGNOSTIC #define DBGR if (debug_restore) printf static int debug_restore = 1; SYSCTL_INT(_debug, OID_AUTO, vps_restore_debug, CTLFLAG_RW, &debug_restore, 0, ""); #else #define DBGR(x, ...) #endif /* DIAGNOSTIC */ static int debug_restore_ktrace = 0; SYSCTL_INT(_debug, OID_AUTO, vps_restore_ktrace, CTLFLAG_RW, &debug_restore_ktrace, 0, ""); int sys_posix_openpt_unit(struct thread *, struct posix_openpt_args *, int); int ktrops(struct thread *,struct proc *,int,int,struct vnode *); MALLOC_DEFINE(M_VPS_RESTORE, "vps_restore", "Virtual Private Systems Restore memory"); void vps_restore_return(struct thread *, struct trapframe *); static struct prison *vps_restore_prison_lookup( struct vps_snapst_ctx *ctx, struct vps *vps, struct prison *old_pr); static int vps_restore_mod_refcnt; /* * * * * * Restore functions. * * * * */ static struct ucred *vps_restore_ucred_lookup(struct vps_snapst_ctx *ctx, struct vps *vps, void *orig_ptr); VPSFUNC static int vps_restore_ucred(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_restore_obj *ro; struct vps_dumpobj *o1; struct vps_dump_ucred *vdcr; struct ucred *ncr; gid_t *tmp_groups; int i; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_UCRED) { ERRMSG(ctx, "%s: o1=%p type=%d != VPS_DUMPOBJT_UCRED\n", __func__, o1, o1->type); return (EINVAL); } vdcr = (struct vps_dump_ucred *)o1->data; KASSERT(vps != NULL, ("%s: vps == NULL\n", __func__)); if ((ncr = vps_restore_ucred_lookup(ctx, vps, vdcr->cr_origptr)) != NULL) { /* debugging panic("%s: double restore, orig_ptr=%p !\n", __func__, vdcr->cr_origptr); */ DBGR("%s: double restore, orig_ptr=%p !\n", __func__, vdcr->cr_origptr); /* Already restored. */ crfree(ncr); return (0); } ncr = crget(); ncr->cr_vps = vps; vps_ref(ncr->cr_vps, ncr); /* Is re-set in fixup routine, after prisons are restored. */ ncr->cr_prison = VPS_VPS(vps, prison0); prison_hold(ncr->cr_prison); ncr->cr_uid = vdcr->cr_uid; ncr->cr_ruid = vdcr->cr_ruid; ncr->cr_svuid = vdcr->cr_svuid; ncr->cr_rgid = vdcr->cr_rgid; ncr->cr_svgid = vdcr->cr_svgid; ncr->cr_loginclass = loginclass_find("default"); ncr->cr_uidinfo = uifind(vdcr->cr_uid); ncr->cr_ruidinfo = uifind(vdcr->cr_ruid); if ((caddr_t)vdcr->cr_groups + (sizeof(vdcr->cr_groups[0]) * vdcr->cr_ngroups) > (caddr_t)o1 + o1->size) { ERRMSG(ctx, "%s: vdcr->cr_groups smaller than specified by " "vdcr->cr_ngroups=%d\n", __func__, vdcr->cr_ngroups); return (EINVAL); } tmp_groups = malloc(sizeof(tmp_groups[0]) * vdcr->cr_ngroups, M_TEMP, M_WAITOK); for (i = 0; i < vdcr->cr_ngroups; i++) tmp_groups[i] = vdcr->cr_groups[i]; crsetgroups(ncr, vdcr->cr_ngroups, tmp_groups); free(tmp_groups, M_TEMP); ncr->cr_flags = vdcr->cr_flags; ncr->cr_pspare2[0] = NULL; ncr->cr_pspare2[1] = NULL; ncr->cr_label = NULL; memset(&ncr->cr_audit, 0, sizeof(ncr->cr_audit)); ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); ro->type = VPS_DUMPOBJT_UCRED; ro->orig_ptr = vdcr->cr_origptr; ro->new_ptr = ncr; ro->spare[0] = vdcr->cr_prison; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); DBGR("%s: ncr=%p\n", __func__, ncr); return (0); } VPSFUNC static int vps_restore_ucred_all(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_dumpobj *o1, *o2; int error = 0; int cnt = 0; o2 = ctx->curobj; o1 = ctx->rootobj; ctx->curobj = o1; do { if (vdo_typeofnext(ctx) != VPS_DUMPOBJT_UCRED) { o1 = vdo_next(ctx); continue; } if ((error = vps_restore_ucred(ctx, vps))) break; ++cnt; } while (o1 != NULL); DBGR("%s: restored %d ucreds\n", __func__, cnt); /* Reset ctx to where it was. */ ctx->curobj = o2; ctx->lastobj = o2; return (error); } VPSFUNC static int vps_restore_ucred_checkall(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_dumpobj *o1, *o2; struct vps_dump_ucred *vdcr; struct ucred *ncr; int cnt = 0; o2 = ctx->curobj; o1 = ctx->rootobj; ctx->curobj = o1; do { if (vdo_typeofnext(ctx) != VPS_DUMPOBJT_UCRED) { o1 = vdo_next(ctx); continue; } o1 = vdo_next(ctx); vdcr = (struct vps_dump_ucred *)o1->data; ncr = vps_restore_ucred_lookup(ctx, vps, vdcr->cr_origptr); crfree(ncr); KASSERT(ncr != NULL, ("%s: ncr==NULL\n", __func__)); /* There is still one extra reference that is kept during the restore run. */ if (ncr->cr_ref != vdcr->cr_ref + 1) DBGR("%s: ncr=%p orig=%p ncr->cr_ref=%d " "vdcr->cr_ref=%d\n", __func__, ncr, vdcr->cr_origptr, ncr->cr_ref - 1, vdcr->cr_ref); ++cnt; } while (o1 != NULL); DBGR("%s: checked %d ucreds\n", __func__, cnt); /* Reset ctx to where it was. */ ctx->curobj = o2; ctx->lastobj = o2; return (0); } VPSFUNC static struct ucred * vps_restore_ucred_lookup(struct vps_snapst_ctx *ctx, struct vps *vps, void *orig_ptr) { struct vps_restore_obj *ro; struct ucred *ncr; ncr = NULL; SLIST_FOREACH(ro, &ctx->obj_list, list) if (ro->type == VPS_DUMPOBJT_UCRED && ro->orig_ptr == orig_ptr) { ncr = ro->new_ptr; break; } if (ncr != NULL) { DBGR("%s: found ucred %p for %p, ncr->ref=%d\n", __func__, ncr, orig_ptr, ncr->cr_ref+1); crhold(ncr); return (ncr); } else { DBGR("%s: no ucred found for %p\n", __func__, orig_ptr); return (NULL); } } VPSFUNC static int vps_restore_ucred_fixup(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_restore_obj *ro; struct prison *pr2; struct ucred *cr; SLIST_FOREACH(ro, &ctx->obj_list, list) { if (ro->type != VPS_DUMPOBJT_UCRED) continue; cr = ro->new_ptr; pr2 = vps_restore_prison_lookup(ctx, vps, ro->spare[0]); KASSERT(pr2 != NULL, ("%s: prison not found for " "orig_ptr %p\n", __func__, ro->spare[0])); DBGR("%s: cr=%p: %p -> %p\n", __func__, cr, cr->cr_prison, pr2); prison_hold(pr2); prison_free(cr->cr_prison); cr->cr_prison = pr2; } return (0); } VPSFUNC static int vps_restore_vnet_route_one(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet *vnet, struct vps_dumpobj *o1, struct radix_node_head *rnh, int fibnum, int af) { struct vps_dump_route *vdr; struct vps_dump_vnet_sockaddr *vds; struct rtentry *rt_entry; struct sockaddr *dst, *gateway, *netmask; size_t saddr_offset; int flags; int error = 0; vdr = (struct vps_dump_route *)o1->data; flags = vdr->rt_flags; dst = netmask = gateway = NULL; saddr_offset = offsetof(struct sockaddr, sa_data); DBGR("%s: rt_have_mask=%d rt_have_gateway=%d rt_have_ifa=%d\n", __func__, vdr->rt_have_mask, vdr->rt_have_gateway, vdr->rt_have_ifa); if (1) { dst = malloc(sizeof(struct sockaddr_storage), M_TEMP, M_WAITOK|M_ZERO); vds = (struct vps_dump_vnet_sockaddr *)(vdr + 1); dst->sa_len = vds->sa_len; dst->sa_family = vds->sa_family; if (vds->sa_len - saddr_offset > 0 && vds->sa_len - saddr_offset <= sizeof(struct sockaddr_storage)) memcpy(dst->sa_data, vds->sa_data, vds->sa_len - saddr_offset); } if (vdr->rt_have_mask == 1) { netmask = malloc(sizeof(struct sockaddr_storage), M_TEMP, M_WAITOK|M_ZERO); vds = (struct vps_dump_vnet_sockaddr *)(vds + 1); netmask->sa_len = vds->sa_len; netmask->sa_family = vds->sa_family; if (vds->sa_len - saddr_offset > 0 && vds->sa_len - saddr_offset <= sizeof(struct sockaddr_storage)) memcpy(netmask->sa_data, vds->sa_data, vds->sa_len - saddr_offset); } if (vdr->rt_have_gateway == 1 || vdr->rt_have_ifa == 1) { gateway = malloc(sizeof(struct sockaddr_storage), M_TEMP, M_WAITOK|M_ZERO); /* Either rt->rt_gateway or rt->rt_ifa->ifa_addr */ vds = (struct vps_dump_vnet_sockaddr *)(vds + 1); gateway->sa_len = vds->sa_len; gateway->sa_family = vds->sa_family; if (vds->sa_len - saddr_offset > 0 && vds->sa_len - saddr_offset <= sizeof(struct sockaddr_storage)) memcpy(gateway->sa_data, vds->sa_data, vds->sa_len - saddr_offset); } CURVNET_SET_QUIET(vnet); error = rtrequest_fib(RTM_ADD, dst, gateway, netmask, flags, &rt_entry, vdr->rt_fibnum); if (error) ERRMSG(ctx, "%s: rtrequest_fib: error=%d\n", __func__, error); CURVNET_RESTORE(); if (dst != NULL) free(dst, M_TEMP); if (netmask != NULL) free(netmask, M_TEMP); if (gateway != NULL) free(gateway, M_TEMP); return (error); } VPSFUNC static int vps_restore_vnet_route(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet *vnet) { struct vps_dumpobj *o1, *o2; struct radix_node_head *rnh; int fibnum; int af; int error = 0; CURVNET_SET_QUIET(vnet); /* Freeing and reinitalizing routing tables to have them clean. */ /* XXX We lose memory this way ... */ vnet_route_uninit(NULL); vnet_route_init(NULL); while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_VNET_ROUTETABLE) { o1 = vdo_next(ctx); fibnum = *(int *)(o1->data + (sizeof(int) * 0)); af = *(int *)(o1->data + (sizeof(int) * 1)); DBGR("%s: fibnum=%d af=%d\n", __func__, fibnum, af); rnh = NULL; while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_VNET_ROUTE) { o2 = vdo_next(ctx); if ((error = vps_restore_vnet_route_one(ctx, vps, vnet, o2, rnh, fibnum, af))) goto out; } } out: CURVNET_RESTORE(); return (error); } VPSFUNC static int vps_restore_iface_ifaddr(struct vps_snapst_ctx *ctx, struct vps *vps, struct ifnet *ifp) { struct vps_dumpobj *o2; struct vps_dump_vnet_ifaddr *vdifaddr; struct vps_dump_vnet_sockaddr *vdsaddr; struct vps_dump_vnet_inet6_lifetime *vdia6lt; struct thread *td; struct vnet *savevnet; struct in_aliasreq *in_alreq; struct in6_aliasreq *in6_alreq; size_t saddr_offset; int error = 0; #ifdef DIAGNOSTIC char ip6buf[INET6_ADDRSTRLEN]; #endif td = curthread; curvnet = TD_TO_VNET(td); savevnet = curvnet; o2 = vdo_next(ctx); vdifaddr = (struct vps_dump_vnet_ifaddr *)o2->data; if (vdifaddr->have_addr == 0) { DBGR("%s: vdifaddr->have_addr == 0\n", __func__); return (0); } vdsaddr = (struct vps_dump_vnet_sockaddr *) (vdifaddr + 1); switch (vdsaddr->sa_family) { case AF_LINK: DBGR("%s: AF_LINK: ignoring\n", __func__); break; case AF_INET: in_alreq = (struct in_aliasreq *)malloc(sizeof(*in_alreq), M_TEMP, M_WAITOK | M_ZERO); memcpy(in_alreq->ifra_name, ifp->if_xname, sizeof(in_alreq->ifra_name)); saddr_offset = offsetof(struct sockaddr_in, sin_port); DBGR("%s: ifa: have_addr=%d have_dstaddr=%d " "have_netmask=%d\n", __func__, vdifaddr->have_addr, vdifaddr->have_dstaddr, vdifaddr->have_netmask); if (vdifaddr->have_addr && vdsaddr->sa_len > saddr_offset && vdsaddr->sa_len <= sizeof(struct sockaddr_in)) { in_alreq->ifra_addr.sin_family = vdsaddr->sa_family; in_alreq->ifra_addr.sin_len = vdsaddr->sa_len; memcpy(&in_alreq->ifra_addr.sin_port, vdsaddr->sa_data, vdsaddr->sa_len - saddr_offset); vdsaddr += 1; } if (vdifaddr->have_dstaddr && vdsaddr->sa_len > saddr_offset && vdsaddr->sa_len <= sizeof(struct sockaddr_in)) { in_alreq->ifra_dstaddr.sin_family = vdsaddr->sa_family; in_alreq->ifra_dstaddr.sin_len = vdsaddr->sa_len; memcpy(&in_alreq->ifra_dstaddr.sin_port, vdsaddr->sa_data, vdsaddr->sa_len - saddr_offset); vdsaddr += 1; } if (vdifaddr->have_netmask && vdsaddr->sa_len > saddr_offset && vdsaddr->sa_len <= sizeof(struct sockaddr_in)) { in_alreq->ifra_mask.sin_family = vdsaddr->sa_family; in_alreq->ifra_mask.sin_len = vdsaddr->sa_len; memcpy(&in_alreq->ifra_mask.sin_port, vdsaddr->sa_data, vdsaddr->sa_len - saddr_offset); vdsaddr += 1; } DBGR("%s: AF_INET: if_name=[%s] addr=[%08x] " "dst=[%08x] mask=[%08x]\n", __func__, in_alreq->ifra_name, in_alreq->ifra_addr.sin_addr.s_addr, in_alreq->ifra_dstaddr.sin_addr.s_addr, in_alreq->ifra_mask.sin_addr.s_addr); curvnet = ifp->if_vnet; if ((error = in_control(NULL, SIOCAIFADDR, (caddr_t)in_alreq, ifp, td))) { ERRMSG(ctx, "%s: in_control() error = %d\n", __func__, error); free(in_alreq, M_TEMP); curvnet = savevnet; goto out; } if (ifp->if_pspare[2] != NULL) ((void (*) (u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)) ifp->if_pspare[2]) (SIOCAIFADDR, (caddr_t)in_alreq, ifp, td); free(in_alreq, M_TEMP); curvnet = savevnet; break; case AF_INET6: saddr_offset = offsetof(struct sockaddr_in6, sin6_port); in6_alreq = (struct in6_aliasreq *) malloc(sizeof(*in6_alreq), M_TEMP, M_WAITOK | M_ZERO); memcpy(in6_alreq->ifra_name, ifp->if_xname, sizeof(in6_alreq->ifra_name)); DBGR("%s: AF_INET6 in6_aliasreq @ %p\n\tifra_name=[%s]\n", __func__, in6_alreq, in6_alreq->ifra_name); if (vdifaddr->have_addr && vdsaddr->sa_len == sizeof(struct sockaddr_in6)) { in6_alreq->ifra_addr.sin6_family = vdsaddr->sa_family; in6_alreq->ifra_addr.sin6_len = vdsaddr->sa_len; memcpy(&in6_alreq->ifra_addr.sin6_port, vdsaddr->sa_data, vdsaddr->sa_len - saddr_offset); vdsaddr += 1; DBGR("\tifra_addr: %s\n", ip6_sprintf(ip6buf, &in6_alreq->ifra_addr.sin6_addr)); } if (vdifaddr->have_dstaddr && vdsaddr->sa_len == sizeof(struct sockaddr_in6)) { in6_alreq->ifra_dstaddr.sin6_family = vdsaddr->sa_family; in6_alreq->ifra_dstaddr.sin6_len = vdsaddr->sa_len; memcpy(&in6_alreq->ifra_dstaddr.sin6_port, vdsaddr->sa_data, vdsaddr->sa_len - saddr_offset); vdsaddr += 1; DBGR("\tifra_dstaddr: %s\n", ip6_sprintf(ip6buf, &in6_alreq->ifra_dstaddr.sin6_addr)); } if (vdifaddr->have_netmask && vdsaddr->sa_len == sizeof(struct sockaddr_in6)) { in6_alreq->ifra_prefixmask.sin6_family = vdsaddr->sa_family; in6_alreq->ifra_prefixmask.sin6_len = vdsaddr->sa_len; memcpy(&in6_alreq->ifra_prefixmask.sin6_port, vdsaddr->sa_data, vdsaddr->sa_len - saddr_offset); vdsaddr += 1; DBGR("\tifra_prefixmask: %s\n", ip6_sprintf(ip6buf, &in6_alreq->ifra_prefixmask.sin6_addr)); } if (1) { vdia6lt = (struct vps_dump_vnet_inet6_lifetime *) vdsaddr; in6_alreq->ifra_lifetime.ia6t_expire = vdia6lt->ia6t_expire; in6_alreq->ifra_lifetime.ia6t_preferred = vdia6lt->ia6t_preferred; in6_alreq->ifra_lifetime.ia6t_vltime = vdia6lt->ia6t_vltime; in6_alreq->ifra_lifetime.ia6t_pltime = vdia6lt->ia6t_pltime; DBGR("\tia6_lifetime: ...\n"); } /* Skipping link-local */ if (in6_alreq->ifra_addr.sin6_addr.s6_addr8[0] == 0xfe && in6_alreq->ifra_addr.sin6_addr.s6_addr8[1] == 0x80) { free(in6_alreq, M_TEMP); goto out; } curvnet = ifp->if_vnet; if ((error = in6_control(NULL, SIOCAIFADDR_IN6, (caddr_t)in6_alreq, ifp, td))) { ERRMSG(ctx, "%s: in6_control() error = %d\n", __func__, error); free(in6_alreq, M_TEMP); curvnet = savevnet; goto out; } if (ifp->if_pspare[2] != NULL) ((void (*) (u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)) ifp->if_pspare[2])(SIOCAIFADDR_IN6, (caddr_t)in6_alreq, ifp, td); free(in6_alreq, M_TEMP); curvnet = savevnet; break; default: ERRMSG(ctx, "%s: unhandled address family %d\n", __func__, vdsaddr->sa_family); error = EINVAL; goto out; } out: return (error); } VPSFUNC static int vps_restore_vnet_iface(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet *vnet) { struct vps_dumpobj *o1; struct vps_dump_vnet_ifnet *vdifnet; struct thread *td; struct vnet *savevnet; struct ifnet *nifnetp; char ifname[IFNAMSIZ]; int if_dunit; int last_was_epair = 0; int error = 0; td = curthread; curvnet = TD_TO_VNET(td); savevnet = curvnet; DBGR("curvnet=%p savevnet=%p\n", curvnet, savevnet); nifnetp = NULL; while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_VNET_IFACE) { o1 = vdo_next(ctx); vdifnet = (struct vps_dump_vnet_ifnet *)o1->data; DBGR("%s: vdifnet: if_xname=[%s] if_dname=[%s] " "if_dunit=%d\n", __func__, vdifnet->if_xname, vdifnet->if_dname, vdifnet->if_dunit); /* * Skip "lo0" attach, because it is created on * vps instance allocation. */ if (strcmp(vdifnet->if_xname, "lo0") == 0) { strcpy(ifname, "lo0"); CURVNET_SET_QUIET(vnet); nifnetp = ifunit(ifname); CURVNET_RESTORE(); } else { /* Restore interface. */ /* * XXX For non-cloned interfaces, * like hardware interfaces, * we need special treatment. */ if (last_was_epair == 1) { nifnetp = ((struct epair_softc *) (nifnetp->if_softc))->oifp; } else { /* Cloned interfaces. */ if_dunit = vdifnet->if_dunit; do { snprintf(ifname, IFNAMSIZ, "%s%d", vdifnet->if_dname, if_dunit++); DBGR("%s: ifname=[%s]\n", __func__, ifname); error = if_clone_create(ifname, sizeof(ifname), NULL); } while (error == EEXIST); if (error) { ERRMSG(ctx, "%s: if_clone_create " "returned error = %d\n", __func__, error); goto out; } nifnetp = ifunit(ifname); } if (nifnetp == NULL) { ERRMSG(ctx, "%s: ifunit ([%s]) == NULL !\n", __func__, ifname); error = EINVAL; goto out; } if (last_was_epair == 0) { /* Move interface into new vps instance. */ if ((error = if_vmove_vps(td, nifnetp->if_xname, 0, vps, vdifnet->if_xname))) { ERRMSG(ctx, "%s: if_vps_vmove() " "error = %d\n", __func__, error); goto out; } if (strcmp(vdifnet->if_dname, "epair") == 0) last_was_epair = 1; } else last_was_epair = 0; } if (vdifnet->if_flags & IFF_UP) { curvnet = nifnetp->if_vnet; if_up(nifnetp); DBGR("%s: setting IFF_UP on [%s]\n", __func__, nifnetp->if_xname); curvnet = savevnet; } while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_VNET_ADDR) { error = vps_restore_iface_ifaddr(ctx, vps, nifnetp); if (error != 0) goto out; /* Next ifaddr. */ } } out: return (error); } VPSFUNC static int vps_restore_vnet(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet **vnetp) { struct vps_dump_vnet *vdvnet; struct vps_dumpobj *o1; struct vnet *nvnet; int error = 0; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_VNET) { DBGR("%s: o1=%p o1->type=%d\n", __func__, o1, o1->type); error = EINVAL; goto out; } vdvnet = (struct vps_dump_vnet *)o1->data; DBGR("%s: orig_ptr=%p\n", __func__, vdvnet->orig_ptr); if (*vnetp != NULL) { nvnet = *vnetp; DBGR("%s: vnet=%p (existed)\n", __func__, nvnet); } else { nvnet = vnet_alloc(); *vnetp = nvnet; DBGR("%s: vnet=%p (allocated)\n", __func__, nvnet); } if ((error = vps_restore_vnet_iface(ctx, vps, nvnet))) goto out; if ((error = vps_restore_vnet_route(ctx, vps, nvnet))) goto out; out: if (error) *vnetp = NULL; return (error); } VPSFUNC static int vps_restore_sysentvec(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct vps_dump_sysentvec *vds; struct vps_dumpobj *o1; struct sysentvec *sv; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_SYSENTVEC) { ERRMSG(ctx, "%s: wrong object type: %d\n", __func__, o1->type); return (EINVAL); } vds = (struct vps_dump_sysentvec *)o1->data; if (vps_md_restore_sysentvec(vds->sv_type, &sv) != 0) { ERRMSG(ctx, "%s: unknown sysentvec type: %d\n", __func__, vds->sv_type); return (EINVAL); } p->p_sysent = sv; return (0); } /* XXX */ int kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int waitok); int kqueue_acquire(struct file *fp, struct kqueue **kqp); void kqueue_release(struct kqueue *kq, int locked); VPSFUNC static int vps_restore_kqueue(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct vps_dumpobj *o1, *o2; struct vps_dump_knote *vdkn; struct kevent *nkev; struct kqueue *kq; struct thread *td; struct file *fp; int error; int dfl; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_KQUEUE) return (EINVAL); DBGR("%s: \n", __func__); td = FIRST_THREAD_IN_PROC(p); if ((error = sys_kqueue(td, NULL))) { ERRMSG(ctx, "%s: sys_kqueue(): %d\n", __func__, error); return (error); } fget(td, td->td_retval[0], 0, &fp); DBGR("%s: kqueue installed at fd %ld\n", __func__, td->td_retval[0]); kq = NULL; if ((error = kqueue_acquire(fp, &kq)) != 0) { ERRMSG(ctx, "%s: kqueue_acquire(): error=%d\n", __func__, error); goto out; } while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_KNOTE) { o2 = vdo_next(ctx); vdkn = (struct vps_dump_knote *)o2->data; dfl = vdkn->kn_status; if (!vdkn->ke_filter) continue; nkev = malloc(sizeof(*nkev), M_TEMP, M_WAITOK); nkev->ident = vdkn->ke_ident; nkev->filter = vdkn->ke_filter; nkev->flags = vdkn->ke_flags; nkev->fflags = vdkn->ke_fflags; nkev->data = vdkn->ke_data; nkev->udata = vdkn->ke_udata; nkev->flags = EV_ADD; if (dfl & KN_ACTIVE) DBGR("KN_ACTIVE\n"); if (dfl & KN_QUEUED) DBGR("KN_QUEUED\n"); if (dfl & KN_DISABLED) nkev->flags |= EV_DISABLE; /* XXX ?! kevp->flags &= ~EV_SYSFLAGS; */ DBGR("kevent: ident = 0x%016zx\n", (size_t)nkev->ident); DBGR("kevent: filter = 0x%04hx\n", nkev->filter); DBGR("kevent: flags = 0x%04hx\n", nkev->flags); DBGR("kevent: fflags = 0x%08x\n", nkev->fflags); DBGR("kevent: data = 0x%016zx\n", (size_t)nkev->data); DBGR("kevent: udata = 0x%016lx\n", (long unsigned int)nkev->udata); error = kqueue_register(kq, nkev, td, 1); if (error) { ERRMSG(ctx, "%s: kqueue_register(): error=%d\n", __func__, error); free(nkev, M_TEMP); goto out; } free(nkev, M_TEMP); /* XXX if (dfl & KN_ACTIVE) // set activate flag if (dfl & KN_QUEUED) //knote_enqueue() */ } out: if (kq != NULL) kqueue_release(kq, 0); fdrop(fp, td); return (error); } /* * This section is a little bit tricky because we have to deal * with pairs of pipes that have one end closed. */ VPSFUNC static int vps_restore_pipe(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct vps_dumpobj *o1; struct vps_dump_pipe *vdp; struct vps_restore_obj *ro; struct file *fp; struct pipepair *npp; int filedes[2]; int error = 0; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_PIPE) return (EINVAL); vdp = (struct vps_dump_pipe *)o1->data; DBGR("%s: vdp: pi_have_dumped_pipe=%d pi_localend=%p pi_pair=%p " "pi_rpipe=%p pi_wpipe=%p\n", __func__, vdp->pi_have_dumped_pipe, vdp->pi_localend, vdp->pi_pair, vdp->pi_rpipe, vdp->pi_wpipe); if (vdp->pi_have_dumped_pipe != 0) { if ((error = kern_pipe(curthread, filedes))) { ERRMSG(ctx, "%s: kern_pipe() error: %d\n", __func__, error); goto out; } /* * filedes[0] is the read endpoint, * filedes[1] the write endpoint. * * We only keep the endpoint connected to the file * descriptor to be restored and close the other endpoint. * * If the currently restored process has a reference to * the second endpoint, it will be connected again later * in another run. */ fget(curthread, filedes[0], 0, &fp); npp = (struct pipepair *) ((struct pipe *)fp->f_data)->pipe_pair; fdrop(fp, curthread); /* Insert into restored objects list. */ ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); ro->type = VPS_DUMPOBJT_PIPE; ro->orig_ptr = vdp->pi_pair; ro->new_ptr = npp; /* These references have to be released later. */ fget(curthread, filedes[0], 0, &fp); ro->spare[0] = fp; fget(curthread, filedes[1], 0, &fp); ro->spare[1] = fp; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); if (vdp->pi_localend == vdp->pi_rpipe) { /* We want the read endpoint. */ fget(curthread, filedes[0], 0, &fp); fp->f_data = &npp->pp_rpipe; fdrop(fp, curthread); /* Close the write endpoint. */ fget(curthread, filedes[1], 0, &fp); fdclose(curthread->td_proc->p_fd, fp, filedes[1], curthread); fdrop(fp, curthread); curthread->td_retval[0] = filedes[0]; } else if (vdp->pi_localend == vdp->pi_wpipe) { /* We want the write endpoint. */ fget(curthread, filedes[1], 0, &fp); fp->f_data = &npp->pp_wpipe; fdrop(fp, curthread); /* Close the read endpoint. */ fget(curthread, filedes[0], 0, &fp); fdclose(curthread->td_proc->p_fd, fp, filedes[0], curthread); fdrop(fp, curthread); curthread->td_retval[0] = filedes[1]; } else { ERRMSG(ctx, "%s: vdp->pi_localend != vdp->pi_rpipe " "&& vdp->pi_localend != vdp->pi_wpipe\n", __func__); /* XXX Clean up. */ error = EINVAL; goto out; } } else { fp = NULL; npp = NULL; SLIST_FOREACH(ro, &ctx->obj_list, list) if (ro->type == VPS_DUMPOBJT_PIPE && ro->orig_ptr == vdp->pi_pair) break; if (ro == NULL) { ERRMSG(ctx, "%s: pipe pair (old_ptr %p) " "which should be there was not found !\n", __func__, vdp->pi_pair); error = EINVAL; goto out; } npp = ro->new_ptr; if (vdp->pi_localend == vdp->pi_rpipe) { /* We want the read endpoint. */ fp = (struct file *)ro->spare[0]; } else if (vdp->pi_localend == vdp->pi_wpipe) { /* We want the write endpoint. */ fp = (struct file *)ro->spare[1]; } else { ERRMSG(ctx, "%s: vdp->pi_localend != vdp->pi_rpipe " "&& vdp->pi_localend != vdp->pi_wpipe\n", __func__); /* XXX Clean up. */ error = EINVAL; goto out; } fhold(fp); FILEDESC_XLOCK (curthread->td_proc->p_fd); if ((error = fdalloc(curthread, 0, &filedes[0]))) { FILEDESC_XUNLOCK(curthread->td_proc->p_fd); ERRMSG(ctx, "%s: fdalloc() error: %d\n", __func__, error); goto out; } curthread->td_proc->p_fd->fd_ofiles[filedes[0]].fde_file = fp; FILEDESC_XUNLOCK(curthread->td_proc->p_fd); curthread->td_retval[0] = filedes[0]; } out: return (error); } VPSFUNC static void vps_restore_cleanup_pipe(struct vps_snapst_ctx *ctx, struct vps *vps, struct _vps_restore_obj_list *obj_list) { struct vps_restore_obj *obj, *obj2; SLIST_FOREACH_SAFE(obj, obj_list, list, obj2) { if (obj->type != VPS_DUMPOBJT_PIPE) continue; if (obj->spare[0]) fdrop((struct file *)obj->spare[0], curthread); if (obj->spare[1]) fdrop((struct file *)obj->spare[1], curthread); /* Let the generic cleanup unlink and free the list item. */ } } VPSFUNC static void vps_restore_cleanup_ucred(struct vps_snapst_ctx *ctx, struct vps *vps, struct _vps_restore_obj_list *obj_list) { struct vps_restore_obj *obj, *obj2; SLIST_FOREACH_SAFE(obj, obj_list, list, obj2) { if (obj->type != VPS_DUMPOBJT_UCRED) continue; crfree(obj->new_ptr); /* Let the generic cleanup unlink and free the list item. */ } } /* * XXX * * All the socket snapshot and restore code is still incomplete. * Most socket options are not supported, */ VPSFUNC __attribute__((unused)) static void vps_sbcheck(struct sockbuf *sb) { struct mbuf *m; struct mbuf *n = 0; u_long len = 0, mbcnt = 0; SOCKBUF_LOCK_ASSERT(sb); for (m = sb->sb_mb; m; m = n) { n = m->m_nextpkt; for (; m; m = m->m_next) { len += m->m_len; mbcnt += MSIZE; if (m->m_flags & M_EXT) /*XXX*/ /* pretty sure this is bogus */ mbcnt += m->m_ext.ext_size; } } if (len != sb->sb_cc || mbcnt != sb->sb_mbcnt) { /* DBGR("cc %ld != %u || mbcnt %ld != %u\n", len, sb->sb_cc, mbcnt, sb->sb_mbcnt); panic("sbcheck"); */ /* debugging */ printf("cc %ld != %u || mbcnt %ld != %u\n", len, sb->sb_cc, mbcnt, sb->sb_mbcnt); kdb_enter(KDB_WHY_BREAK, "VPS break to debugger"); } } /* * Pointers m1, m2, m3, ... contain mbuf pointers of the system * where the snapshot was taken. * We compare them to the mbufs we restore and replace them with the new * pointer (XXX: , or NULL if not available). * * Since there *shouldn't* be any activity on the socket, * it *should* be safe to sleep for allocating mbufs. */ VPSFUNC static int vps_restore_mbufchain(struct vps_snapst_ctx *ctx, struct vps *vps, struct mbuf **mptrs) { struct vps_dump_mbufchain *vdmc; struct vps_dump_mbuf *vdmb; struct vps_dumpobj *o1; struct mbuf *nm, *lnm, *nm0; struct mbuf **mptrs2; int i, j; /* caller verified type */ o1 = vdo_next(ctx); vdmc = (struct vps_dump_mbufchain *)o1->data; mptrs2 = malloc(sizeof(struct mbuf *) * vdmc->mc_mbcount, M_TEMP, M_WAITOK); lnm = nm0 = NULL; vdmb = (struct vps_dump_mbuf *)(vdmc + 1); for (i = 0; i < vdmc->mc_mbcount; i++) { nm = m_get(M_WAITOK, vdmb->mb_type); nm->m_len = vdmb->mb_len; if (i == 0) nm0 = nm; mptrs2[i] = vdmb->mb_orig_ptr; if (vdmb->mb_have_dat==1) { memcpy(nm->m_dat, vdmb->mb_payload, vdmb->mb_payload_size); nm->m_flags = vdmb->mb_flags; if (vdmb->mb_have_data==1) nm->m_data = nm->m_dat + vdmb->mb_data_off; /* vps_print_ascii(nm->m_dat, nm->m_len); */ } else if (vdmb->mb_have_ext==1) { m_cljget(nm, M_WAITOK, vdmb->mb_payload_size); memcpy(nm->m_ext.ext_buf, vdmb->mb_payload, vdmb->mb_payload_size); nm->m_flags = vdmb->mb_flags; if (vdmb->mb_have_data==1) nm->m_data = nm->m_ext.ext_buf + vdmb->mb_data_off; DBGR("%s: M_EXT m_ext.ext_size=%u " "mb_payload_size=%u\n", __func__, nm->m_ext.ext_size, vdmb->mb_payload_size); /* vps_print_ascii(nm->m_ext.ext_buf, nm->m_ext.ext_size); */ /* checksum */ if (1) { int sum = 0, i; for (i = 0; i < nm->m_ext.ext_size; i++) sum += (u_char)nm->m_ext.ext_buf[i]; DBGR("%s: computed checksum=%08x, " "original checksum=%08x\n", __func__, sum, vdmb->mb_checksum); if (sum != vdmb->mb_checksum) { ERRMSG(ctx, "%s: checksum " "failure !\n", __func__); return (-1); } } } else { ERRMSG(ctx, "%s: DON'T KNOW HOW TO HANDLE MBUF\n", __func__); DBGR("%s: vdmb->mb_have_dat=%d " "vdmb->mb_have_ext=%d\n", __func__, vdmb->mb_have_dat, vdmb->mb_have_ext); return (-1); } if (nm->m_flags & M_PKTHDR) { DBGR("%s: nm=%p M_PKTHDR\n", __func__, nm); nm->m_pkthdr.rcvif = NULL; nm->m_pkthdr.header = NULL; /* XXX if (dm->m_pkthdr.header != NULL) nm->m_pkthdr.header = (caddr_t)nm + ((caddr_t)dm->m_pkthdr.header - (caddr_t)vdm->morigptr[i]); */ /* DBGR("%s: nm=%p dm_orig=%p header=%p rcvif=%p\n", __func__, nm, vdm->morigptr[i], dm->m_pkthdr.header, dm->m_pkthdr.rcvif); */ /* XXX */ SLIST_INIT(&nm->m_pkthdr.tags); } if (lnm) lnm->m_next = nm; lnm = nm; DBGR("%s: nm=%p type=%d flags=%08x len=%d next=%p " "nextpkt=%p\n", __func__, nm, nm->m_type, nm->m_flags, nm->m_len, nm->m_next, nm->m_nextpkt); for (j = 0; mptrs[j]; j++) if (mptrs[j] == mptrs2[i]) { mptrs[j] = nm; DBGR("%s: replaced %p --> %p, i=%d j=%d\n", __func__, mptrs2[i], nm, i, j); /* No 'break' because we may have the same pointer twice. */ } /* Next. */ DBGR("%s: vdmb=%p vdmb->mb_payload=%p " "vdmb->mb_payload_size=%u\n", __func__, vdmb, vdmb->mb_payload, vdmb->mb_payload_size); /* Always padded to meet 64 bit alignment ! */ vdmb = (struct vps_dump_mbuf *)(vdmb->mb_payload + roundup(vdmb->mb_payload_size, 8)); } DBGR("%s: restored %d mbufs\n", __func__, i); free(mptrs2, M_TEMP); if (nm0 && m_sanity(nm0, 1) != 1) { DBGR("%s: m_sanity: BAD mbuf(chain)\n", __func__); } return (i); } VPSFUNC static int vps_restore_sockbuf(struct vps_snapst_ctx *ctx, struct vps *vps, struct sockbuf *nsb) { struct vps_dumpobj *o1; struct vps_dump_sockbuf *vdsb; struct mbuf *mptrs[5]; int rc; int error = 0; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_SOCKBUF) return (-1); vdsb = (struct vps_dump_sockbuf *)o1->data; mptrs[0] = vdsb->sb_mb; mptrs[1] = vdsb->sb_mbtail; mptrs[2] = vdsb->sb_lastrecord; mptrs[3] = vdsb->sb_sndptr; mptrs[4] = NULL; DBGR("%s: mptrs[]: 0=%p 1=%p 2=%p 3=%p\n", __func__, mptrs[0], mptrs[1], mptrs[2], mptrs[3]); if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_MBUFCHAIN) rc = vps_restore_mbufchain(ctx, vps, mptrs); else rc = 0; if (rc != vdsb->sb_mcnt) { ERRMSG(ctx, "%s: vps_restore_mbufchain()=%d != " "dsb->sb_mcnt=%d\n", __func__, rc, vdsb->sb_mcnt); return (EINVAL); } DBGR("%s: mptrs[]: 0=%p 1=%p 2=%p 3=%p\n", __func__, mptrs[0], mptrs[1], mptrs[2], mptrs[3]); nsb->sb_mb = mptrs[0]; nsb->sb_mbtail = mptrs[1]; nsb->sb_lastrecord = mptrs[2]; nsb->sb_sndptr = mptrs[3]; nsb->sb_state = vdsb->sb_state; nsb->sb_sndptroff = vdsb->sb_sndptroff; nsb->sb_cc = vdsb->sb_cc; nsb->sb_hiwat = vdsb->sb_hiwat; nsb->sb_mbcnt = vdsb->sb_mbcnt; nsb->sb_mcnt = vdsb->sb_mcnt; nsb->sb_ccnt = vdsb->sb_ccnt; nsb->sb_mbmax = vdsb->sb_mbmax; nsb->sb_ctl = vdsb->sb_ctl; nsb->sb_lowat = vdsb->sb_lowat; nsb->sb_hiwat = vdsb->sb_hiwat; nsb->sb_timeo = vdsb->sb_timeo; nsb->sb_flags = vdsb->sb_flags; /* XXX nsb->sb_upcall = vdsb->sb_upcall; nsb->sb_upcallarg = vdsb->sb_upcallarg; */ nsb->sb_upcall = NULL; nsb->sb_upcallarg = NULL; DBGR("%s: restored sockbuf=%p sb_cc=%u sb_mcnt=%u " "sb_sndptroff=%u\n", __func__, nsb, nsb->sb_cc, nsb->sb_mcnt, nsb->sb_sndptroff); vps_sbcheck(nsb); return (error); } VPSFUNC static int vps_restore_fixup_unixsockets(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_restore_obj *obj1, *obj2; struct socket *srvso_old, *srvso_new, *cltso; struct unpcb *cltunp, *srvunp; int error = 0; SLIST_FOREACH(obj1, &ctx->obj_list, list) { if (obj1->type != VPS_DUMPOBJT_SOCKET_UNIX) continue; if (obj1->spare[0] != (void*)'c') /* not client socket */ continue; cltso = (struct socket *)obj1->new_ptr; srvso_old = (struct socket *)obj1->spare[1]; srvso_new = NULL; SLIST_FOREACH(obj2, &ctx->obj_list, list) { if (obj2->type != VPS_DUMPOBJT_SOCKET_UNIX) continue; if (obj2->orig_ptr == (void*)srvso_old) { srvso_new = obj2->new_ptr; break; } } if (srvso_new == NULL) { ERRMSG(ctx, "%s: srvso_new == NULL for " "srvso_old=%p\n", __func__, srvso_old); error = EINVAL; break; } cltunp = sotounpcb((struct socket *)obj1->new_ptr); srvunp = sotounpcb((struct socket *)srvso_new); cltunp->unp_conn = srvunp; cltso->so_state |= SS_ISCONNECTED; if (cltso->so_type == SOCK_DGRAM) { LIST_INSERT_HEAD(&srvunp->unp_refs, cltunp, unp_reflink); } else { srvunp->unp_conn = cltunp; srvso_new->so_state |= SS_ISCONNECTED; } DBGR("%s: cltso=%p srvso_new=%p\n", __func__, cltso, srvso_new); /* Let the generic cleanup unlink and free the list item. */ } return (error); } VPSFUNC static int vps_restore_socket(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct vps_dumpobj *o1; struct vps_dump_socket *vds; struct vps_dump_inetpcb *vdinpcb; struct vps_dump_unixpcb *vdunpcb; struct vps_dump_udppcb *vdudpcb; struct vps_dump_tcppcb *vdtcpcb; struct vps_dump_vnet_sockaddr *vdsaddr; struct vps_restore_obj *ro; struct socket *nso, *nso2; struct inpcb *ninpcb; struct tcpcb *ntcpcb; struct unpcb *nunpcb; struct udpcb *nudpcb; struct sockaddr_un *saddr_un; struct socket_args sockargs; /* 3 * int */ struct ucred *save_ucred, *ncr; struct filedesc *cfd; struct stat *statp; struct file *fp; caddr_t cpos; int fdidx, fdidx_save; int error; int i; nso = NULL; fdidx = -1; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_SOCKET) return (-1); /* XXX prison with own vnet ! */ CURVNET_SET_QUIET(vps->vnet); /* We have to temporarily change our ucred too. */ save_ucred = curthread->td_ucred; ncr = NULL; cpos = o1->data; vds = (struct vps_dump_socket *)cpos; cpos += sizeof(*vds); if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_UCRED) { vdo_next(ctx); /* XXX don't put child objects in the middle of data ! */ cpos = ctx->cpos; } ncr = vps_restore_ucred_lookup(ctx, vps, vds->so_cred); curthread->td_ucred = ncr; DBGR("%s: family=%d protocol=%d type=%d\n", __func__, vds->so_family, vds->so_protocol, vds->so_type); sockargs.domain = vds->so_family; sockargs.type = vds->so_type; sockargs.protocol = vds->so_protocol; if ((error = sys_socket(curthread, &sockargs))) { ERRMSG(ctx, "%s: sys_socket() error: %d\n", __func__, error); goto out; } fdidx = curthread->td_retval[0]; if ((error = getsock(curthread->td_proc->p_fd, fdidx, &fp, NULL))) { ERRMSG(ctx, "%s: getsock() error: %d\n", __func__, error); goto out; } nso = fp->f_data; sblock(&nso->so_rcv, SBL_WAIT | SBL_NOINTR); sblock(&nso->so_snd, SBL_WAIT | SBL_NOINTR); SOCKBUF_LOCK(&nso->so_rcv); SOCKBUF_LOCK(&nso->so_snd); nso->so_vnet = curvnet; nso->so_state = vds->so_state; if (vds->so_options & SO_ACCEPTCONN) nso->so_options |= SO_ACCEPTCONN; nso->so_qlimit = vds->so_qlimit; nso->so_qstate = vds->so_qstate; /* XXX restore all socket options at all levels ! */ DBGR("%s: nso=%p dso->so_state = %08x\n", __func__, nso, vds->so_state); DBGR("%s: nso->so_cred=%p nso->so_cred->cr_vps=%p " "nso->so_vnet=%p\n", __func__, nso->so_cred, nso->so_cred->cr_vps, nso->so_vnet); switch (vds->so_family) { case PF_UNIX: SOCKBUF_UNLOCK(&nso->so_snd); SOCKBUF_UNLOCK(&nso->so_rcv); vdunpcb = (struct vps_dump_unixpcb *)cpos; cpos += sizeof(*vdunpcb); nunpcb = (struct unpcb *)nso->so_pcb; if (vdunpcb->unp_have_addr) { vdsaddr = (struct vps_dump_vnet_sockaddr *)cpos; cpos += sizeof(*vdsaddr); saddr_un = malloc(sizeof(struct sockaddr_un), M_TEMP, M_WAITOK | M_ZERO); saddr_un->sun_len = vdsaddr->sa_len; saddr_un->sun_family = vdsaddr->sa_family; memcpy(saddr_un->sun_path, vdsaddr->sa_data, vdsaddr->sa_len); /* Make sure sun_path is null terminated. */ saddr_un->sun_path[saddr_un->sun_len - offsetof(struct sockaddr_un, sun_path[0])] = '\0'; } else saddr_un = NULL; if (vdunpcb->unp_have_conn==0 && vdunpcb->unp_have_vnode==1 && vdunpcb->unp_have_addr==1) { /* Socket was bound. */ DBGR("%s: socket was bound: [%s]\n", __func__, saddr_un->sun_path); /* Remove name from filesystem first. */ statp = malloc(sizeof(*statp), M_TEMP, M_WAITOK); error = kern_stat(curthread, saddr_un->sun_path, UIO_SYSSPACE, statp); if (error) { ERRMSG(ctx, "%s: kern_stat() error: " "%d [%s]\n", __func__, error, saddr_un->sun_path); free(statp, M_TEMP); free(saddr_un, M_TEMP); SOCKBUF_LOCK(&nso->so_rcv); SOCKBUF_LOCK(&nso->so_snd); goto out_unlock; } kern_unlink(curthread, saddr_un->sun_path, UIO_SYSSPACE); error = sobind(nso, (struct sockaddr *)saddr_un, curthread); if (error) { ERRMSG(ctx, "%s: sobind() error: %d [%s]\n", __func__, error, saddr_un->sun_path); free(statp, M_TEMP); free(saddr_un, M_TEMP); SOCKBUF_LOCK(&nso->so_rcv); SOCKBUF_LOCK(&nso->so_snd); goto out_unlock; } /* Restore permissions. */ error = kern_chown(curthread, saddr_un->sun_path, UIO_SYSSPACE, statp->st_uid, statp->st_gid); if (error) { ERRMSG(ctx, "%s: kern_chown() error: " "%d [%s]\n", __func__, error, saddr_un->sun_path); free(statp, M_TEMP); free(saddr_un, M_TEMP); SOCKBUF_LOCK(&nso->so_rcv); SOCKBUF_LOCK(&nso->so_snd); goto out_unlock; } error = kern_chmod(curthread, saddr_un->sun_path, UIO_SYSSPACE, statp->st_mode); if (error) { ERRMSG(ctx, "%s: kern_chmod() error: " "%d [%s]\n", __func__, error, saddr_un->sun_path); free(statp, M_TEMP); free(saddr_un, M_TEMP); SOCKBUF_LOCK(&nso->so_rcv); SOCKBUF_LOCK(&nso->so_snd); goto out_unlock; } free(statp, M_TEMP); if (vdunpcb->unp_flags & UNP_HAVEPCCACHED) { /* Socket was listening. */ DBGR("%s: socket was listening\n", __func__); error = solisten(nso, vds->so_qlimit, curthread); if (error) { ERRMSG(ctx, "%s: solisten() error: " "%d [%s]\n", __func__, error, saddr_un->sun_path); free(saddr_un, M_TEMP); SOCKBUF_LOCK(&nso->so_rcv); SOCKBUF_LOCK(&nso->so_snd); goto out_unlock; } } /* insert into global list of unix sockets */ ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); ro->type = VPS_DUMPOBJT_SOCKET_UNIX; ro->orig_ptr = vdunpcb->unp_socket; ro->new_ptr = nso; ro->spare[0] = (void*)'s'; ro->spare[1] = NULL; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); /* * Both client and server sockets are put into a global list * that is walked through at the end of vps restore. * All references can be restored then. */ } else if (vdunpcb->unp_have_conn==1 && vdunpcb->unp_have_addr==1 && (vdunpcb->unp_flags & UNP_HAVEPC)) { DBGR("%s: connected; server\n", __func__); /* insert into global list of unix sockets */ ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); ro->type = VPS_DUMPOBJT_SOCKET_UNIX; ro->orig_ptr = vdunpcb->unp_socket; ro->new_ptr = nso; ro->spare[0] = (void*)'s'; ro->spare[1] = NULL; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); DBGR("%s: server socket: orig_ptr=%p new_ptr=%p " "unp_socket=%p\n", __func__, ro->orig_ptr, ro->new_ptr, vdunpcb->unp_socket); } else if (vdunpcb->unp_have_conn) { DBGR("%s: connected; client\n", __func__); /* insert into global list of unix sockets */ ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); ro->type = VPS_DUMPOBJT_SOCKET_UNIX; ro->orig_ptr = vdunpcb->unp_socket; ro->new_ptr = nso; ro->spare[0] = (void*)'c'; ro->spare[1] = (void*)vdunpcb->unp_conn_socket; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); DBGR("%s: client socket: orig_ptr=%p new_ptr=%p " "unp_conn_socket=%p\n", __func__, ro->orig_ptr, ro->new_ptr, vdunpcb->unp_conn_socket); } else { DBGR("%s: unknown socket state\n", __func__); } if (vdunpcb->unp_flags & UNP_HAVEPCCACHED) { nunpcb->unp_flags |= UNP_HAVEPCCACHED; nunpcb->unp_peercred.cr_uid = vdunpcb->unp_peercred.cr_uid; nunpcb->unp_peercred.cr_ngroups = vdunpcb->unp_peercred.cr_ngroups; for (i = 0; i < nunpcb->unp_peercred.cr_ngroups; i++) nunpcb->unp_peercred.cr_groups[i] = vdunpcb->unp_peercred.cr_groups[i]; } /* XXX not used at all ? */ if (vdunpcb->unp_have_vnode==1 && vdo_typeofnext(ctx) == VPS_DUMPOBJT_FILE_PATH) { DBGR("%s: unused vnode object !\n", __func__); vdo_next(ctx); } DBGR("%s: nso=%p so_state=%d unpcb->unp_conn=%p\n", __func__, nso, nso->so_state, nunpcb->unp_conn); free(saddr_un, M_TEMP); SOCKBUF_LOCK(&nso->so_rcv); SOCKBUF_LOCK(&nso->so_snd); break; case PF_INET: case PF_INET6: vdinpcb = (struct vps_dump_inetpcb *)cpos; cpos += sizeof(*vdinpcb); ninpcb = (struct inpcb *)nso->so_pcb; INP_INFO_WLOCK(ninpcb->inp_pcbinfo); INP_HASH_WLOCK(ninpcb->inp_pcbinfo); INP_WLOCK(ninpcb); ninpcb->inp_vnet = curvnet; /* Connection info (endpoints). */ ninpcb->inp_inc.inc_flags = vdinpcb->inp_inc.inc_flags; ninpcb->inp_inc.inc_len = vdinpcb->inp_inc.inc_len; ninpcb->inp_inc.inc_fibnum = vdinpcb->inp_inc.inc_fibnum; ninpcb->inp_inc.inc_ie.ie_fport= vdinpcb->inp_inc.ie_fport; ninpcb->inp_inc.inc_ie.ie_lport= vdinpcb->inp_inc.ie_lport; if (vdinpcb->inp_vflag & INP_IPV6) { memcpy(&ninpcb->inp_inc.inc6_faddr, vdinpcb->inp_inc.ie_ufaddr, 0x10); memcpy(&ninpcb->inp_inc.inc6_laddr, vdinpcb->inp_inc.ie_uladdr, 0x10); } else { memcpy(&ninpcb->inp_inc.inc_faddr, vdinpcb->inp_inc.ie_ufaddr, 0x4); memcpy(&ninpcb->inp_inc.inc_laddr, vdinpcb->inp_inc.ie_uladdr, 0x4); } /* DBGR("%s: inc_flags=%08x inc_len=%d inc_fibnum=%d\n", __func__, vdinpcb->inp_inc.inc_flags, vdinpcb->inp_inc.inc_len, vdinpcb->inp_inc.inc_fibnum); DBGR("%s: inc_flags=%08x inc_len=%d inc_fibnum=%d\n", __func__, ninpcb->inp_inc.inc_flags, ninpcb->inp_inc.inc_len, ninpcb->inp_inc.inc_fibnum); DBGR("%s: inp_inc.ie_fport=%u inp_inc.ie_lport=%u\n", __func__, vdinpcb->inp_inc.ie_fport, vdinpcb->inp_inc.ie_lport); DBGR("%s: inp_inc.ie_fport=%u inp_inc.ie_lport=%u\n", __func__, ninpcb->inp_inc.inc_ie.ie_fport, ninpcb->inp_inc.inc_ie.ie_lport); DBGR("%s: inp_inc.inc6_faddr: %16D\n", __func__, vdinpcb->inp_inc.ie_ufaddr, ":"); DBGR("%s: inp_inc.inc6_laddr: %16D\n", __func__, vdinpcb->inp_inc.ie_uladdr, ":"); DBGR("%s: inp_inc.inc6_faddr: %16D\n", __func__, &ninpcb->inp_inc.inc6_faddr, ":"); DBGR("%s: inp_inc.inc6_laddr: %16D\n", __func__, &ninpcb->inp_inc.inc6_laddr, ":"); */ ninpcb->inp_vflag = vdinpcb->inp_vflag; ninpcb->inp_flags = vdinpcb->inp_flags; ninpcb->inp_flags2 = vdinpcb->inp_flags2; /* in_pcbinshash() has NOT been called */ ninpcb->inp_flags &= ~INP_INHASHLIST; in_pcbinshash(ninpcb); DBGR("%s: ninpcb=%p inp_vflag=%02hhx inp_flags=%08x " "inp_flags2=%08x\n", __func__, ninpcb, ninpcb->inp_vflag, ninpcb->inp_flags, ninpcb->inp_flags2); /* XXX IP options. */ if (vdinpcb->inp_have_ppcb != 0) { KASSERT(ninpcb->inp_ppcb != NULL, ("%s: ninpcb->inp_ppcb == NULL", __func__)); if (ninpcb->inp_ppcb == NULL) { ERRMSG(ctx, "%s: ninpcb->inp_ppcb == " "NULL\n", __func__); error = EINVAL; goto out_unlock; } /* inpcb->inp_ip_p seems to be 0 and only used for raw ip and divert sockets! */ switch (vds->so_protocol) { case IPPROTO_TCP: vdtcpcb = (struct vps_dump_tcppcb *)cpos; ntcpcb = (struct tcpcb *)ninpcb->inp_ppcb; INP_INFO_WLOCK(&V_tcbinfo); ntcpcb->t_state = vdtcpcb->t_state; ntcpcb->snd_una = vdtcpcb->snd_una; ntcpcb->snd_max = vdtcpcb->snd_max; ntcpcb->snd_nxt = vdtcpcb->snd_nxt; ntcpcb->snd_up = vdtcpcb->snd_up; ntcpcb->snd_wl1 = vdtcpcb->snd_wl1; ntcpcb->snd_wl2 = vdtcpcb->snd_wl2; ntcpcb->iss = vdtcpcb->iss; ntcpcb->irs = vdtcpcb->irs; ntcpcb->rcv_nxt = vdtcpcb->rcv_nxt; ntcpcb->rcv_adv = vdtcpcb->rcv_adv; ntcpcb->rcv_wnd = vdtcpcb->rcv_wnd; ntcpcb->rcv_up = vdtcpcb->rcv_up; ntcpcb->snd_wnd = vdtcpcb->snd_wnd; ntcpcb->snd_cwnd = vdtcpcb->snd_cwnd; ntcpcb->snd_ssthresh = vdtcpcb->snd_ssthresh; INP_INFO_WUNLOCK(&V_tcbinfo); break; case IPPROTO_UDP: vdudpcb = (struct vps_dump_udppcb *)cpos; nudpcb = (struct udpcb *)ninpcb->inp_ppcb; if (vdudpcb->u_have_tun_func != 0) { ERRMSG(ctx, "%s: ucb->u_tun_func " "!= NULL, unsupported\n", __func__); error = EINVAL; goto out_unlock; } nudpcb->u_flags = vdudpcb->u_flags; nudpcb->u_tun_func = NULL; break; case IPPROTO_RAW: case IPPROTO_ICMP: /* Nothing to do. */ break; default: ERRMSG(ctx, "%s: unhandled IPPROTO " "%d / %d\n", __func__, vds->so_protocol, vdinpcb->inp_ip_p); error = EINVAL; goto out_unlock; break; } } INP_WUNLOCK(ninpcb); INP_HASH_WUNLOCK(ninpcb->inp_pcbinfo); INP_INFO_WUNLOCK(ninpcb->inp_pcbinfo); break; default: ERRMSG(ctx, "%s: unhandled protocol family %d\n", __func__, vds->so_family); error = EINVAL; goto out_unlock; break; } if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_SOCKBUF) if ((error = vps_restore_sockbuf(ctx, vps, &nso->so_rcv))) goto out_unlock; if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_SOCKBUF) if ((error = vps_restore_sockbuf(ctx, vps, &nso->so_snd))) goto out_unlock; /* * On success, the only thing to return is the new fd index * in curthread->td_retval[0]. */ out_unlock: SOCKBUF_UNLOCK(&nso->so_snd); SOCKBUF_UNLOCK(&nso->so_rcv); sbunlock(&nso->so_snd); sbunlock(&nso->so_rcv); fdrop(fp, curthread); out: if (error) { /* XXX destroy socket. */ ERRMSG(ctx, "%s: error = %d\n", __func__, error); } curthread->td_retval[0] = fdidx; curthread->td_ucred = save_ucred; if (ncr) crfree(ncr); CURVNET_RESTORE(); /* Sockets that were on the accept queue of this socket. */ if (vds->so_qlen > 0 || vds->so_incqlen > 0) { DBGR("%s: so_qlen=%d so_incqlen=%d\n", __func__, vds->so_qlen, vds->so_incqlen); fdidx_save = fdidx; cfd = curthread->td_proc->p_fd; for (i = 0; i < (vds->so_qlen + vds->so_incqlen); i++) { if ((error = vps_restore_socket(ctx, vps, p))) return (error); /* * We have to remove it from the fdset and link it * up to the listening socket. */ fdidx = (int)curthread->td_retval[0]; DBGR("%s: open returned fd %d\n", __func__, fdidx); FILEDESC_XLOCK(cfd); fp = cfd->fd_ofiles[fdidx].fde_file; nso2 = (struct socket *)fp->f_data; cfd->fd_ofiles[fdidx].fde_file = NULL; cfd->fd_ofiles[fdidx].fde_flags = 0; fdunused(cfd, fdidx); FILEDESC_XUNLOCK(cfd); /* * Have to do ugly things here ... * Better allocate sockets using soalloc() rather * than sys_socket(). * */ fp->f_ops = &badfileops; fdrop(fp, curthread); nso2->so_count = 0; if (i < vds->so_qlen) { TAILQ_INSERT_TAIL(&nso->so_comp, nso2, so_list); nso->so_qlen++; } else { TAILQ_INSERT_TAIL(&nso->so_incomp, nso2, so_list); nso->so_incqlen++; } } curthread->td_retval[0] = fdidx_save; } return (error); } VPSFUNC static int vps_restore_pathtovnode(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnode **vnp) { struct vps_dumpobj *o1; struct vps_dump_filepath *vdfp; struct nameidata nd; int error; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_FILE_PATH) return (EINVAL); vdfp = (struct vps_dump_filepath *)o1->data; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, vdfp->fp_path, curthread); error = namei(&nd); if (error) return (error); /* XXX VREF() ?! */ *vnp = nd.ni_vp; VOP_UNLOCK(nd.ni_vp, 0); NDFREE(&nd, NDF_ONLY_PNBUF); if (strcmp(vdfp->fp_path, "/VPSRELINKED_") == 0) /* XXX oldinum -> 0 ? */ kern_unlinkat(curthread, AT_FDCWD, vdfp->fp_path, UIO_SYSSPACE, 0); return (0); } +/* EXPERIMENTAL - nfs doesn't support vfs_vget() :-( */ VPSFUNC static int +vps_restore_inodenumtovnode(struct vps_snapst_ctx *ctx, struct vps *vps, + struct vnode **vnp) +{ + struct vps_dump_fileinodenum *vdfi; + struct vps_dumpobj *o1; + struct mount *mp; + struct vnode *vp; + char *vpsroot; + char *mnton; + int len; + int error; + + o1 = vdo_next(ctx); + + if (o1->type != VPS_DUMPOBJT_FILE_INODENUM) + return (EINVAL); + + vdfi = (struct vps_dump_fileinodenum *)o1->data; + + DBGR("%s: fsid=%lu fileid=%d\n", __func__, vdfi->fsid, vdfi->fileid); + + DBGR("%s: vps's rootpath=[%s] vnode=%p\n", + __func__, vps->_rootpath, vps->_rootvnode); + + vpsroot = strdup(vps->_rootpath, M_TEMP); + if (vpsroot[strlen(vpsroot) - 1] == '/') + vpsroot[strlen(vpsroot) - 1] = '\0'; + len = strlen(vpsroot); + + mtx_lock(&mountlist_mtx); + TAILQ_FOREACH(mp, &mountlist, mnt_list) { + mnton = mp->mnt_stat.f_mntonname; + if (!(strncmp(vpsroot, mnton, len) == 0 && + (mnton[len] == '\0' || mnton[len] == '/'))) + continue; + + if (mp->mnt_stat.f_fsid.val[0] == vdfi->fsid) + break; + } + mtx_unlock(&mountlist_mtx); + + free(vpsroot, M_TEMP); + + if (mp == NULL) { + ERRMSG(ctx, "%s: no mount found for fsid [%16x]\n", + __func__, vdfi->fsid); + return (ENOENT); + } else + DBGR("%s: got mount=%p for fsid\n", __func__, mp); + + error = VFS_VGET(mp, vdfi->fileid, LK_SHARED | LK_RETRY, &vp); + if (error != 0) { + ERRMSG(ctx, "%s: VFS_VGET() error=%d\n", + __func__, error); + return (error); + } + + *vnp = vp; + + vref(vp); + VOP_UNLOCK(vp, 0); + + return (0); +} + +VPSFUNC +static int +vps_restore_vnode(struct vps_snapst_ctx *ctx, struct vps *vps, + struct vnode **vnp) +{ + int error; + + if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_FILE_INODENUM) + error = vps_restore_inodenumtovnode(ctx, vps, vnp); + else if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_FILE_PATH) + error = vps_restore_pathtovnode(ctx, vps, vnp); + else { + ERRMSG(ctx, "%s: vdo_typeofnext(ctx)=%d\n", + __func__, vdo_typeofnext(ctx)); + return (EINVAL); + } + + return (error); +} + +VPSFUNC +static int vps_restore_file_vnode(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p, struct thread *curtd, struct vps_dump_file *vdf) { struct vps_dumpobj *o1; struct vps_dump_filepath *vdfp; int oflags; int error = 0; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_FILE_PATH) { ERRMSG(ctx, "%s: DTYPE_VNODE without path information " "-> skipping !\n", __func__); error = EINVAL; goto out; } vdfp = (struct vps_dump_filepath *)o1->data; oflags = OFLAGS(vdf->f_flag); /* * We have to open the file in our current thread's context, * because e.g. devfs relies on td == curthread. * Afterwards we simply move the reference into the new proc's fd. */ if ((error = kern_openat(curtd, AT_FDCWD /* XXX */, vdfp->fp_path, UIO_SYSSPACE, oflags, 0))) { ERRMSG(ctx, "%s: open error: [%s] %d\n", __func__, vdfp->fp_path, error); #ifdef DIAGNOSTIC /* XXX debugging */ if (error == ENOENT && !strcmp(vdfp->fp_path, "/dev/null")) kdb_enter(KDB_WHY_BREAK, "VPS break to debugger"); error = 0; #else error = EINVAL; goto out; #endif } if (strncmp(vdfp->fp_path, "/VPSRELINKED_", 13) == 0) { DBGR("%s: unlinking [%s]\n", __func__, vdfp->fp_path); /* XXX oldinum -> 0 ? */ kern_unlinkat(curtd, AT_FDCWD, vdfp->fp_path, UIO_SYSSPACE, 0); } /* Returning new fd index in curthread->td_retval[0]. */ out: return (error); } VPSFUNC static int vps_restore_file_pts(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p, struct thread *curtd, struct vps_dump_file *vdf) { struct vps_dumpobj *o1, *o2; struct vps_restore_obj *ro; struct vps_dump_filepath *vdfp; struct filedesc *fdp; struct file *fp; struct tty *ttyp; struct vps_dump_pts *vdp; struct pts_softc *psc; struct termios *termiosp; int oflags; int fdidx; int found; int i; int error = 0; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_PTS) { ERRMSG(ctx, "%s: DTYPE_PTS without VPS_DUMPOBJT_PTS\n", __func__); error = EINVAL; goto out; } vdp = (struct vps_dump_pts *)o1->data; if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_UCRED) vdo_next(ctx); o2 = vdo_next(ctx); if (o2->type != VPS_DUMPOBJT_FILE_PATH) { ERRMSG(ctx, "%s: DTYPE_PTS without " "VPS_DUMPOBJT_FILE_PATH\n", __func__); error = EINVAL; goto out; } vdfp = (struct vps_dump_filepath *)o2->data; oflags = OFLAGS(vdf->f_flag); fdp = curtd->td_proc->p_fd; FILEDESC_XLOCK(fdp); found = 0; for (fdidx = 0; fdidx < fdp->fd_nfiles; fdidx++) { fp = fget_locked(fdp, fdidx); if (fp && fp->f_type == DTYPE_PTS) { if (strncmp(vdfp->fp_path, tty_devname((struct tty *)fp->f_data), vdfp->fp_size) == 0) { found = 1; curtd->td_retval[0] = fdidx; break; } } } FILEDESC_XUNLOCK(fdp); if (found == 0) { ERRMSG(ctx, "%s: pts [%s] not found !\n", __func__, vdfp->fp_path); error = EINVAL; goto out; } ttyp = (struct tty *)fp->f_data; termiosp = malloc(sizeof(*termiosp), M_TEMP, M_WAITOK); termiosp->c_iflag = vdp->pt_termios.c_iflag; termiosp->c_oflag = vdp->pt_termios.c_oflag; termiosp->c_cflag = vdp->pt_termios.c_cflag; termiosp->c_lflag = vdp->pt_termios.c_lflag; termiosp->c_ispeed = vdp->pt_termios.c_ispeed; termiosp->c_ospeed = vdp->pt_termios.c_ospeed; for (i = 0; i < NCCS; i++) termiosp->c_cc[i] = vdp->pt_termios.c_cc[i]; error = kern_ioctl(curtd, /* fd */ curtd->td_retval[0], TIOCSETAW, (caddr_t)termiosp); free(termiosp, M_TEMP); if (error) ERRMSG(ctx, "%s: ttydev_ioctl() error=%d\n", __func__, error); ttyp->t_pgrp = (void*)(size_t)vdp->pt_pgrp_id; /* ID */ psc = tty_softc(ttyp); psc->pts_flags = vdp->pt_flags; if (vdp->pt_cred != NULL) { if (psc->pts_cred != NULL) crfree(psc->pts_cred); psc->pts_cred = vps_restore_ucred_lookup(ctx, vps, vdp->pt_cred); } /* Insert into restored objects list. */ ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); ro->type = VPS_DUMPOBJT_PTS; ro->new_ptr = ttyp; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); out: return (error); } VPSFUNC static int vps_restore_file_kqueue(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p, struct thread *curtd, struct vps_dump_file *vdf) { struct filedesc *nfd, *cfd; struct file *nfp; char tmpflags; int idx; int error = 0; if ((error = vps_restore_kqueue(ctx, vps, p))) { ERRMSG(ctx, "%s: vps_restore_kqueue() error: %d\n", __func__, error); goto out; } /* The file was restored in newproc so move it to curproc. */ idx = (int)FIRST_THREAD_IN_PROC(p)->td_retval[0]; nfd = p->p_fd; cfd = curtd->td_proc->p_fd; FILEDESC_XLOCK(nfd); nfp = nfd->fd_ofiles[idx].fde_file; fhold(nfp); tmpflags = nfd->fd_ofiles[idx].fde_flags; nfd->fd_ofiles[idx].fde_file = NULL; nfd->fd_ofiles[idx].fde_flags = 0; fdunused(nfd, idx); FILEDESC_XUNLOCK(nfd); FILEDESC_XLOCK(cfd); /* fdalloc() calls fdused() for the new descriptor. */ if ((error = fdalloc(curtd, 0, &idx))) { ERRMSG(ctx, "%s: fdalloc(): %d\n", __func__, error); fdrop(nfp, curtd); FILEDESC_XUNLOCK(cfd); goto out; } cfd->fd_ofiles[idx].fde_file = nfp; cfd->fd_ofiles[idx].fde_flags = tmpflags; fdrop(nfp, curtd); FILEDESC_XUNLOCK(cfd); curtd->td_retval[0] = idx; out: return (error); } VPSFUNC static int vps_restore_file(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct vps_dumpobj *o1; struct vps_dump_file *vdf; struct vps_restore_obj *ro; struct filedesc *cfd; struct file *nfp; struct vps *save_vps; struct ucred *save_ucred, *ncr; struct vnode *save_rdir, *save_cdir; struct thread *curtd; char tmpflags; int idx; int error = 0; curtd = curthread; save_vps = curtd->td_vps; save_ucred = curtd->td_ucred; save_rdir = curtd->td_proc->p_fd->fd_rdir; save_cdir = curtd->td_proc->p_fd->fd_cdir; curtd->td_vps = vps; curtd->td_proc->p_fd->fd_rdir = p->p_fd->fd_rdir; curtd->td_proc->p_fd->fd_cdir = p->p_fd->fd_cdir; ncr = NULL; /* caller verified type */ o1 = vdo_next(ctx); /* DBGR("%s: o1=%p: VPS_DUMPOBJT_FILE size=%d\n", __func__, o1, o1->size); */ vdf = (struct vps_dump_file *)o1->data; ncr = NULL; DBGR("%s: index= origidx= origptr=%p type=%d flag=%08x offset=%d\n", __func__, vdf->orig_ptr, vdf->f_type, vdf->f_flag, (int)vdf->f_offset); /* Lookup in list of restored file objects. */ SLIST_FOREACH(ro, &ctx->obj_list, list) if (ro->type == VPS_DUMPOBJT_FILE && ro->orig_ptr == vdf->orig_ptr) break; if (ro != NULL) { DBGR("%s: found in restored obj list: orig_ptr=%p " "new_ptr=%p\n", __func__, ro->orig_ptr, ro->new_ptr); /* skip over child objects if exist */ while (vdo_nextischild(ctx, o1)) vdo_next(ctx); error = 0; goto out; } if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_UCRED) { vdo_next(ctx); } ncr = vps_restore_ucred_lookup(ctx, vps, vdf->f_cred); save_ucred = curtd->td_ucred; curtd->td_ucred = ncr; switch (vdf->f_type) { case DTYPE_VNODE: /* Returns new fd index in curtd->td_retval[0]. */ if ((error = vps_restore_file_vnode(ctx, vps, p, curtd, vdf))) { ERRMSG(ctx, "%s: vps_restore_file_vnode() " "error: %d\n", __func__, error); goto out; } break; case DTYPE_PTS: /* Returns new fd index in curtd->td_retval[0]. */ if ((error = vps_restore_file_pts(ctx, vps, p, curtd, vdf))) { ERRMSG(ctx, "%s: vps_restore_file_pts() " "error: %d\n", __func__, error); goto out; } break; case DTYPE_SOCKET: /* Returns new fd index in curtd->td_retval[0]. */ if ((error = vps_restore_socket(ctx, vps, p))) { ERRMSG(ctx, "%s: vps_restore_socket() error: %d\n", __func__, error); goto out; } break; case DTYPE_PIPE: /* Returns new fd index in curtd->td_retval[0]. */ if ((error = vps_restore_pipe(ctx, vps, p))) { ERRMSG(ctx, "%s: vps_restore_pipe() error: %d\n", __func__, error); goto out; } break; case DTYPE_KQUEUE: /* Returns new fd index in curtd->td_retval[0]. */ if ((error = vps_restore_file_kqueue(ctx, vps, p, curtd, vdf))) { ERRMSG(ctx, "%s: vps_restore_file_kqueue() " "error: %d\n", __func__, error); goto out; } break; default: ERRMSG(ctx, "%s: unhandled file type %d\n", __func__, vdf->f_type); error = ENOTSUP; goto out; break; } idx = (int)curtd->td_retval[0]; DBGR("%s: open returned fd %d\n", __func__, idx); cfd = curtd->td_proc->p_fd; FILEDESC_XLOCK(cfd); nfp = cfd->fd_ofiles[idx].fde_file; fhold(nfp); tmpflags = cfd->fd_ofiles[idx].fde_flags; cfd->fd_ofiles[idx].fde_file = NULL; cfd->fd_ofiles[idx].fde_flags = 0; fdunused(cfd, idx); FILEDESC_XUNLOCK(cfd); fhold(nfp); nfp->f_offset = vdf->f_offset; fdrop(nfp, curtd); /* Restore f_flag XXX */ if (vdf->f_flag & FNONBLOCK) nfp->f_flag |= FNONBLOCK; /* Insert into restored objects list. */ ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); ro->type = VPS_DUMPOBJT_FILE; ro->orig_ptr = vdf->orig_ptr; /* Having an extra reference now for list. */ ro->new_ptr = nfp; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); fdrop(nfp, curtd); out: curtd->td_vps = save_vps; curtd->td_proc->p_fd->fd_rdir = save_rdir; curtd->td_proc->p_fd->fd_cdir = save_cdir; if (ncr) { curtd->td_ucred = save_ucred; crfree(ncr); ncr = NULL; } return (error); } VPSFUNC static int vps_restore_fdset_linkup(struct vps_snapst_ctx *ctx, struct vps *vps, struct vps_dump_filedesc *vdfd, struct proc *p, char is_final) { struct vps_restore_obj *ro; struct filedesc *nfd; struct file *nfp; int i; int error = 0; nfd = p->p_fd; /* Now all files should exist, so link them into fdset. */ for (i = 0; i < vdfd->fd_nfiles; i++) { DBGR("%s: vdfd->fd_entries[%d].fp = %p\n", __func__, i, vdfd->fd_entries[i].fp); if (vdfd->fd_entries[i].fp == NULL) continue; /* Look if already restored in a previous run. */ if (i <= nfd->fd_nfiles && nfd->fd_ofiles[i].fde_file != NULL) continue; /* Lookup in list of restored file objects. */ SLIST_FOREACH(ro, &ctx->obj_list, list) if (ro->type == VPS_DUMPOBJT_FILE && ro->orig_ptr == vdfd->fd_entries[i].fp) break; /* Only return error if this is the final run. */ if (is_final != 0 && ro == NULL) { ERRMSG(ctx, "%s: can't find file fp=%p\n", __func__, vdfd->fd_entries[i].fp); error = EINVAL; goto out; } else if (ro == NULL) { continue; } nfp = (struct file *)ro->new_ptr; fhold(nfp); FILEDESC_XLOCK(nfd); if (i >= nfd->fd_nfiles) fdgrowtable(nfd, i); nfd->fd_ofiles[i].fde_file = nfp; nfd->fd_ofiles[i].fde_flags = vdfd->fd_entries[i].flags; nfd->fd_ofiles[i].fde_rights = vdfd->fd_entries[i].rights; fdused(nfd, i); FILEDESC_XUNLOCK(nfd); DBGR("%s: linked up fp: idx=%d new=%p orig=%p\n", __func__, i, ro->new_ptr, ro->orig_ptr); } out: return (error); } VPSFUNC static int vps_restore_fdset(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p, struct filedesc *orig_fdp) { struct vps_dumpobj *o1; struct vps_dumpobj *o2; struct vps_dumpobj *o3; struct vps_dump_filedesc *vdfd; struct vps_restore_obj *ro; struct filedesc *nfd, *cfd; struct vps *save_vps; struct ucred *save_ucred; struct vnode *save_rdir; struct vnode *save_cdir; int error = 0; if (vdo_typeofnext(ctx) != VPS_DUMPOBJT_FDSET) { /* Lookup in list of restored file objects. */ SLIST_FOREACH(ro, &ctx->obj_list, list) if (ro->type == VPS_DUMPOBJT_FDSET && ro->orig_ptr == orig_fdp) break; if (ro == NULL) { ERRMSG(ctx, "%s: fdset orig_ptr=%p not found !\n", __func__, orig_fdp); return (EINVAL); } p->p_fd = fdshare(ro->new_ptr); DBGR("%s: linked shared fdset %p (orig %p) to proc %p/%d\n", __func__, p->p_fd, orig_fdp, p, p->p_pid); return (0); } /* verified type */ o1 = vdo_next(ctx); /* DBGR("%s: o1=%p: VPS_DUMPOBJT_FDSET size=%d\n", __func__, o1, o1->size); */ vdfd = (struct vps_dump_filedesc *)o1->data; DBGR("%s: fdset has %d entries\n", __func__, vdfd->fd_nfiles); p->p_fd = nfd = fdinit(NULL); cfd = curthread->td_proc->p_fd; if (vdfd->fd_have_cdir != 0) { - if ((error = vps_restore_pathtovnode(ctx, vps, + if ((error = vps_restore_vnode(ctx, vps, &p->p_fd->fd_cdir))) return (error); } if (vdfd->fd_have_rdir != 0) { - if ((error = vps_restore_pathtovnode(ctx, vps, + if ((error = vps_restore_vnode(ctx, vps, &p->p_fd->fd_rdir))) return (error); } if (vdfd->fd_have_jdir != 0) { - if ((error = vps_restore_pathtovnode(ctx, vps, + if ((error = vps_restore_vnode(ctx, vps, &p->p_fd->fd_jdir))) return (error); } save_vps = curthread->td_vps; save_ucred = curthread->td_ucred; save_rdir = curthread->td_proc->p_fd->fd_rdir; save_cdir = curthread->td_proc->p_fd->fd_cdir; curthread->td_vps = vps; curthread->td_proc->p_fd->fd_rdir = p->p_fd->fd_rdir; curthread->td_proc->p_fd->fd_cdir = p->p_fd->fd_cdir; /* * First only restore file objects with priority >= 0, * then the ones with priority < 0. * This is necessary because kqueue has to be restored * after all other file descriptors. */ o2 = vdo_getcur(ctx); while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_FILE) { if (vdo_peek(ctx)->prio < 0) { o3 = vdo_next(ctx); while (vdo_nextischild(ctx, o3)) vdo_next(ctx); continue; } if ((error = vps_restore_file(ctx, vps, p))) goto out; } if ((error = vps_restore_fdset_linkup(ctx, vps, vdfd, p, 0 /* not final run */)) != 0) goto out; vdo_setcur(ctx, o2); while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_FILE) { if (vdo_peek(ctx)->prio >= 0) { o3 = vdo_next(ctx); while (vdo_nextischild(ctx, o3)) vdo_next(ctx); continue; } if ((error = vps_restore_file(ctx, vps, p))) goto out; } if ((error = vps_restore_fdset_linkup(ctx, vps, vdfd, p, 1 /* final run */)) != 0) goto out; curthread->td_vps = save_vps; curthread->td_proc->p_fd->fd_rdir = save_rdir; curthread->td_proc->p_fd->fd_cdir = save_cdir; /* Insert into restored objects list. */ ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); ro->type = VPS_DUMPOBJT_FDSET; ro->orig_ptr = vdfd->fd_orig_ptr; ro->new_ptr = nfd; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); DBGR("%s: restored fdset orig=%p new=%p\n", __func__, vdfd->fd_orig_ptr, nfd); out: return (error); } VPSFUNC static void vps_restore_cleanup_fdset(struct vps_snapst_ctx *ctx, struct vps *vps, struct _vps_restore_obj_list *obj_list) { struct vps_restore_obj *obj, *obj2; SLIST_FOREACH_SAFE(obj, obj_list, list, obj2) { if (obj->type != VPS_DUMPOBJT_FILE) continue; if (obj->new_ptr) fdrop((struct file *)obj->new_ptr, curthread); /* Let the generic cleanup unlink and free the list item. */ } } VPSFUNC static int vps_restore_pargs(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct vps_dumpobj *o1; struct vps_dump_pargs *vdp; int error = 0; /* caller verified type */ o1 = vdo_next(ctx); vdp = (struct vps_dump_pargs *)o1->data; p->p_args = pargs_alloc(vdp->ar_length); memcpy(p->p_args->ar_args, vdp->ar_args, vdp->ar_length); DBGR("%s: len=%d [%s]\n", __func__, vdp->ar_length, p->p_args->ar_args); return (error); } /* * Return one logical memory page named by index, from the userspace dump. */ VPSFUNC static struct vm_page * vps_restore_getuserpage(struct vps_snapst_ctx *ctx, int idx, int test) { vm_map_t map; vm_object_t obj; vm_map_entry_t entry; vm_pindex_t index; vm_prot_t prot; vm_page_t m; boolean_t wired; vm_offset_t vaddr; vaddr = (vm_offset_t)(ctx->userpagesaddr + (idx << PAGE_SHIFT)); map = &curthread->td_proc->p_vmspace->vm_map; retry: if ((vm_map_lookup(&map, vaddr, VM_PROT_READ, &entry, &obj, &index, &prot, &wired))) { DBGR("%s: vm_map_lookup(): error\n", __func__); m = NULL; goto out; } VM_OBJECT_WLOCK(obj); m = vm_page_lookup(obj, index); vm_map_lookup_done(map, entry); /*DBGR("%s: userpage idx=%d at %p\n", __func__, idx, mem);*/ if (m == NULL) { /* Try to page in. */ /* Note: unlocks the object if it has to sleep */ m = vm_page_grab(obj, index, VM_ALLOC_NORMAL | VM_ALLOC_RETRY); if (m == NULL) panic("%s: vm_page_alloc() == NULL", __func__); if ((vm_pager_get_pages(obj, &m, 1, 0)) != VM_PAGER_OK) { vm_page_lock(m); vm_page_free(m); vm_page_unlock(m); VM_OBJECT_WUNLOCK(obj); m = NULL; goto out; } m = vm_page_lookup(obj, index); vm_page_wakeup(m); } /* * The pageout daemon might have already decided to swap out * this very page. */ if (m->oflags & VPO_SWAPINPROG || m->busy > 0) { DBGR("%s: m->oflags & VPO_SWAPINPROG || m->busy > 0 " "--> vm_page_sleep()\n", __func__); vm_page_sleep(m, "swpinp"); VM_OBJECT_WUNLOCK(obj); goto retry; } vm_page_busy(m); VM_OBJECT_WUNLOCK(obj); out: KASSERT( !(test == 0 && m == NULL), ("vps_restore_getuserpage: unable to retrieve page, " "idx=%d/offset=%d", idx, idx << PAGE_SHIFT)); return (m); } VPSFUNC static int vps_restore_vmobject(struct vps_snapst_ctx *ctx, struct vps *vps, struct vm_object **nvo_out) { struct vps_dumpobj *o1, *o2; struct vps_dump_vmobject *vdvmo; struct vps_dump_vmpages *vdvmp; struct vps_dump_filepath *vdfp; struct vps_restore_obj *vbo; struct vm_object *nvo, *vo2; struct vm_page *m; struct ucred *ncr; struct nameidata nd; int error = 0; /* DBGR("%s: o=%p: type=%d size=%d\n", __func__, o, o->type, o->size); */ *nvo_out = NULL; /* caller verified type */ o1 = vdo_next(ctx); vdvmo = (struct vps_dump_vmobject *)o1->data; DBGR("%s: old obj=%p: size=%d flags=%04x type=%02x cred=%p " "origptr=%p\n", __func__, vdvmo, (int)vdvmo->size, vdvmo->flags, vdvmo->type, vdvmo->cred, vdvmo->orig_ptr); if (vdvmo->type == OBJT_VNODE && vdvmo->have_vnode) { o2 = vdo_next(ctx); if (o2->type != VPS_DUMPOBJT_FILE_PATH) { ERRMSG(ctx, "%s: wrong object, expected " "VPS_DUMPOBJT_FILE_PATH\n", __func__); error = EINVAL; goto out; } vdfp = (struct vps_dump_filepath *)o2->data; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, vdfp->fp_path, curthread); if ((error = namei(&nd))) { ERRMSG(ctx, "%s: namei([%s]): error = %d\n", __func__, vdfp->fp_path, error); goto out; } if ((error = VOP_OPEN(nd.ni_vp, FREAD, curthread->td_ucred, curthread, NULL))) { ERRMSG(ctx, "%s: VOP_OPEN(...): error = %d\n", __func__, error); VOP_UNLOCK(nd.ni_vp, 0); NDFREE(&nd, NDF_ONLY_PNBUF); goto out; } nvo = nd.ni_vp->v_object; vm_object_reference(nvo); vrele(nd.ni_vp); VOP_UNLOCK(nd.ni_vp, 0); NDFREE(&nd, NDF_ONLY_PNBUF); KASSERT(nvo->cred == NULL, ("%s: nvo=%p ->cred=%p\n", __func__, nvo, nvo->cred)); DBGR("%s: path [%s] got vnode %p v_object %p\n", __func__, vdfp->fp_path, nd.ni_vp, nvo); } else if (vdvmo->type == OBJT_DEFAULT || vdvmo->type == OBJT_SWAP) { nvo = vm_object_allocate(OBJT_DEFAULT, vdvmo->size); } else if (vdvmo->type == OBJT_PHYS && vdvmo->is_sharedpageobj != 0) { DBGR("%s: shared_page_obj\n", __func__); nvo = shared_page_obj; vm_object_reference(nvo); } else if (vdvmo->type == OBJT_PHYS) { DBGR("%s: OBJT_PHYS\n", __func__); nvo = vm_object_allocate(OBJT_PHYS, vdvmo->size); } else { /* * XXX We don't support that yet. */ ERRMSG(ctx, "%s: unsupported vm object: vdvmo=%p type=%d\n", __func__, vdvmo, vdvmo->type); /* XXX missing the sibling list here */ error = EINVAL; goto out; } if (vdvmo->cred != NULL && vdo_typeofnext(ctx) == VPS_DUMPOBJT_UCRED) { vdo_next(ctx); } if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_UCRED && vdvmo->cred == NULL) DBGR("%s: have ucred but vdvmo->cred == NULL!!!\n", __func__); if (nvo != shared_page_obj) { nvo->flags = vdvmo->flags; nvo->charge = vdvmo->charge; KASSERT(nvo->cred == NULL, ("%s: nvo->cred = %p\n", __func__, nvo->cred)); /*DBGR("%s: charge=%lu\n", __func__, nvo->charge);*/ if (vdvmo->cred != NULL) { ncr = vps_restore_ucred_lookup(ctx, vps, vdvmo->cred); KASSERT(ncr != NULL, ("%s: ucred not found\n", __func__)); nvo->cred = ncr; swap_reserve_by_cred(nvo->charge, nvo->cred); } } if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_VMPAGE) { struct vps_dump_vmpageref *vdvmpr; int i; /* DBGR("%s: o2=%p: type=VMPAGE size=%d (pindex=%08x)\n", __func__, o2, o2->size, *((int*)o2->data) ); */ o2 = vdo_next(ctx); vdvmp = (struct vps_dump_vmpages *)o2->data; vdvmpr = (struct vps_dump_vmpageref *)(vdvmp + 1); DBGR("%s: vdvmp=%p count=%u vdvmpr=%p\n", __func__, vdvmp, (u_int)vdvmp->count, vdvmpr); for (i = 0; i < vdvmp->count; i++) { m = vps_restore_getuserpage(ctx, ctx->userpagesidx++, 0); vo2 = m->object; VM_OBJECT_WLOCK(nvo); VM_OBJECT_WLOCK(vo2); vm_page_lock(m); pmap_remove_all(m); KASSERT((m->oflags & VPO_SWAPINPROG) == 0, ("%s: m=%p oflags 0x%x & VPO_SWAPINPROG\n", __func__, m, m->oflags)); KASSERT(vdvmpr->pr_vmobject == vdvmo->orig_ptr, ("%s: object mismatch ! " "(vdvmpr->pr_vmobject=%p)\n", __func__, vdvmpr->pr_vmobject)); vm_page_rename(m, nvo, vdvmpr->pr_pindex); if (vdvmo->type == OBJT_PHYS) vm_page_wire(m); vm_page_unlock(m); VM_OBJECT_WUNLOCK(vo2); VM_OBJECT_WUNLOCK(nvo); /* Next mem page. */ vdvmpr++; } } if (vdvmo->backing_object) { /* * We search in our list for the referenced backing object. * If it's not there, something went wrong badly. */ SLIST_FOREACH(vbo, &ctx->obj_list, list) if (vbo->type == VPS_DUMPOBJT_VMOBJECT && vbo->orig_ptr == vdvmo->backing_object) { nvo->backing_object = vbo->new_ptr; break; } if (nvo->backing_object == NULL) { ERRMSG(ctx, "%s: backing object not found " "(orig_ptr=%p)\n", __func__, vdvmo->backing_object); error = EINVAL; goto out; } vm_object_reference(nvo->backing_object); LIST_INSERT_HEAD(&nvo->backing_object->shadow_head, nvo, shadow_list); nvo->backing_object->shadow_count++; nvo->backing_object->generation++; nvo->backing_object_offset = vdvmo->backing_object_offset; DBGR("%s: found backing object %p for object %p\n", __func__, nvo->backing_object, nvo); } /* * Add this object to our list of vm (backing) objects. * * Note: the reference that we have now is released * when this list is cleaned up. */ vbo = malloc(sizeof(*vbo), M_VPS_RESTORE, M_WAITOK); vbo->type = VPS_DUMPOBJT_VMOBJECT; vbo->orig_ptr = vdvmo->orig_ptr; vbo->new_ptr = nvo; SLIST_INSERT_HEAD(&ctx->obj_list, vbo, list); DBGR("%s: object=%p put in list of vm objects, orig_ptr=%p\n", __func__, nvo, vbo->orig_ptr); KASSERT(!(nvo->type != 2 && nvo != shared_page_obj && nvo->ref_count != 1), ("%s: nvo=%p ->ref_count = %d\n", __func__, nvo, nvo->ref_count)); *nvo_out = nvo; /*DBGR("%s: *nvo_out=%p nvo=%p\n", __func__, *nvo_out, nvo);*/ out: return (error); } VPSFUNC static int vps_restore_vmspace(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p, struct vmspace *orig_vmspace) { struct vps_dumpobj *o1, *o2; struct vps_restore_obj *ro; struct vps_dump_vmspace *vdvms; struct vps_dump_vmmapentry *vdvme; struct vps_restore_obj *vbo; struct vmspace *ns; struct vm_map_entry *nme; struct vm_object *nvo; struct ucred *ncr; int cow; int error = 0; ncr = NULL; if (vdo_typeofnext(ctx) != VPS_DUMPOBJT_VMSPACE) { /* Lookup in list of restored file objects. */ SLIST_FOREACH(ro, &ctx->obj_list, list) if (ro->type == VPS_DUMPOBJT_VMSPACE && ro->orig_ptr == orig_vmspace) break; if (ro == NULL) { ERRMSG(ctx, "%s: vmspace orig_ptr=%p not found !\n", __func__, orig_vmspace); return (EINVAL); } p->p_vmspace = ro->new_ptr; atomic_add_int(&p->p_vmspace->vm_refcnt, 1); DBGR("%s: linked shared vmspace %p (orig %p) " "to proc %p/%d\n", __func__, p->p_vmspace, orig_vmspace, p, p->p_pid); return (0); } /* verified type. */ o1 = vdo_next(ctx); vdvms = (struct vps_dump_vmspace *)o1->data; ns = vmspace_alloc(vdvms->vm_map.minoffset, vdvms->vm_map.maxoffset); DBGR("%s: map=%p\n", __func__, &ns->vm_map); /* o1 --> vmspace */ /* o2 --> map entry */ while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_VMMAPENTRY) { o2 = vdo_next(ctx); vdvme = (struct vps_dump_vmmapentry *)o2->data; nvo = NULL; DBGR("%s: vm_map_entry=%p: start=%p end=%p orig_obj=%p\n", __func__, vdvme, PTRFROM64(vdvme->start), PTRFROM64(vdvme->end), vdvme->map_object); if (vdvme->map_object == NULL) { /* No VM objects, next map entry follows. */ DBGR("%s: no object\n", __func__); /* Move on to next map entry. */ } else { /* Look if vm object is already restored (shared memory). */ SLIST_FOREACH(vbo, &ctx->obj_list, list) if (vbo->orig_ptr == vdvme->map_object) { nvo = vbo->new_ptr; break; } if (nvo) { /* Move on to next map entry. */ DBGR("%s: found vm_object (shared memory): " "orig=%p nvo=%p\n", __func__, vdvme->map_object, nvo); } } if (vdvme->cred != NULL) { if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_UCRED) vdo_next(ctx); ncr = vps_restore_ucred_lookup(ctx, vps, vdvme->cred); } else ncr = NULL; DBGR("%s: ncr=%p\n", __func__, ncr); while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_VMOBJECT) { if ((error = vps_restore_vmobject(ctx, vps, &nvo))) goto out; } /* This is the last vm object for this map entry, so insert the entry. */ cow = 0; DBGR("%s: entry (%016zx-%016zx, size=%016zx, prot=%02x, " "max_prot=%02x, object=%p, offset=%016zx) eflags=%08x; " "nvo=%p\n", __func__, (size_t)vdvme->start, (size_t)vdvme->end, (size_t)(vdvme->end - vdvme->start), vdvme->protection, vdvme->max_protection, vdvme->map_object, (size_t)vdvme->offset, vdvme->eflags, nvo); if (nvo != NULL) vm_object_reference(nvo); vm_map_lock(&ns->vm_map); if ((error = vm_map_insert(&ns->vm_map, nvo, vdvme->offset, vdvme->start, vdvme->end, vdvme->protection, vdvme->max_protection, cow))) { ERRMSG(ctx, "%s: vm_map_insert(): error %d\n", __func__, error); error = EINVAL; if (nvo) vm_object_deallocate(nvo); vm_map_unlock(&ns->vm_map); goto out; } vm_map_lookup_entry(&ns->vm_map, vdvme->start, &nme); nme->inheritance = vdvme->inheritance; if (vdvme->eflags & MAP_STACK_GROWS_DOWN) nme->eflags |= MAP_STACK_GROWS_DOWN; if (vdvme->eflags & MAP_STACK_GROWS_UP) nme->eflags |= MAP_STACK_GROWS_UP; if (vdvme->eflags & MAP_ENTRY_COW) nme->eflags |= MAP_ENTRY_COW; if (vdvme->eflags & MAP_ENTRY_NEEDS_COPY) nme->eflags |= MAP_ENTRY_NEEDS_COPY; if (vdvme->eflags & MAP_ENTRY_NOCOREDUMP) nme->eflags |= MAP_ENTRY_NOCOREDUMP; /* XXX audit this value */ if (vdvme->avail_ssize > 0) nme->avail_ssize = vdvme->avail_ssize; if (nme->cred != NULL) { swap_release_by_cred(nme->end - nme->start, nme->cred); crfree(nme->cred); nme->cred = NULL; } if (ncr != NULL) { nme->cred = crhold(ncr); swap_reserve_by_cred(nme->end - nme->start, nme->cred); } vm_map_unlock(&ns->vm_map); if (ncr != NULL) { crfree(ncr); ncr = NULL; } /* Next map entry. */ } ns->vm_tsize = vdvms->vm_tsize; ns->vm_dsize = vdvms->vm_dsize; ns->vm_ssize = vdvms->vm_ssize; /* Insert into restored objects list. */ ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); ro->type = VPS_DUMPOBJT_VMSPACE; ro->orig_ptr = vdvms->vm_orig_ptr; ro->new_ptr = ns; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); DBGR("%s: restored vmspace orig=%p new=%p\n", __func__, vdvms->vm_orig_ptr, ns); p->p_vmspace = ns; out: if (ncr != NULL) { crfree(ncr); ncr = NULL; } if (error) { ERRMSG(ctx, "%s: error = %d\n", __func__, error); } return (error); } VPSFUNC static void vps_restore_cleanup_vmspace(struct vps_snapst_ctx *ctx, struct vps *vps, struct _vps_restore_obj_list *obj_list) { struct vps_restore_obj *obj, *obj2; SLIST_FOREACH_SAFE(obj, obj_list, list, obj2) { if (obj->type != VPS_DUMPOBJT_VMOBJECT) continue; if (obj->new_ptr) vm_object_deallocate(obj->new_ptr); /* Let the generic cleanup unlink and free the list item. */ } } VPSFUNC static int vps_restore_thread_savefpu(struct vps_snapst_ctx *ctx, struct vps *vps, struct thread *td) { return (vps_md_restore_thread_savefpu(ctx, vps, td)); } VPSFUNC static int vps_restore_thread(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct vps_dumpobj *o1; struct vps_dump_thread *vdtd; struct thread *ntd; #if defined(VPS_ARCH_I386) || defined(VPS_ARCH_AMD64) void *pcb_save; #endif int kstack_pages; int error = 0; int i; /* caller verified type */ o1 = vdo_next(ctx); vdtd = (struct vps_dump_thread *)o1->data; DBGR("%s: old thread: tid=%d\n", __func__, vdtd->td_tid); kstack_pages = vdtd->td_kstack_pages; if (kstack_pages > KSTACK_MAX_PAGES) { ERRMSG(ctx, "%s: requested %d pages for kstack but system " "maximum is %d\n", __func__, kstack_pages, KSTACK_MAX_PAGES); error = EINVAL; goto out; } if ((ntd = thread_alloc(kstack_pages)) == NULL) { error = ENOMEM; goto out; } tidhash_add(ntd); memset(&ntd->td_startzero, 0, __rangeof(struct thread, td_startzero, td_endzero)); memset(&ntd->td_rux, 0, sizeof(ntd->td_rux)); ntd->td_rqindex = vdtd->td_rqindex; ntd->td_base_pri = vdtd->td_base_pri; ntd->td_priority = vdtd->td_priority; ntd->td_pri_class = vdtd->td_pri_class; ntd->td_user_pri = vdtd->td_user_pri; ntd->td_base_user_pri = vdtd->td_base_user_pri; ntd->td_sigstk.ss_sp = PTRFROM64(vdtd->td_sigstk.ss_sp); ntd->td_sigstk.ss_size = vdtd->td_sigstk.ss_size; ntd->td_sigstk.ss_flags = vdtd->td_sigstk.ss_flags; ntd->td_xsig = vdtd->td_xsig; ntd->td_dbgflags = vdtd->td_dbgflags; for (i = 0; i < _SIG_WORDS; i++) { ntd->td_sigmask.__bits[i] = vdtd->td_sigmask[i]; ntd->td_oldsigmask.__bits[i] = vdtd->td_oldsigmask[i]; } #if defined(VPS_ARCH_I386) || defined(VPS_ARCH_AMD64) /* Remember because it will be overwritten. */ pcb_save = ntd->td_pcb->pcb_save; #endif /* Restore kernel stack (this includes the PCB). */ memcpy((char *)ntd->td_kstack, vdtd->td_kstack, kstack_pages * PAGE_SIZE); #if defined(VPS_ARCH_I386) || defined(VPS_ARCH_AMD64) ntd->td_pcb->pcb_save = pcb_save; #endif /* * XXX * There are some registers/values restored along with the PCB * that have to be audited ! */ /* vps_md_print_thread(ntd); */ if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_SAVEFPU) { if ((error = vps_restore_thread_savefpu(ctx, vps, ntd))) goto out; } error = vps_md_restore_thread(vdtd, ntd, p); if (error != 0) goto out; ntd->td_proc = p; ntd->td_ucred = crhold(p->p_ucred); ntd->td_vps = vps; ntd->td_vps_acc = vps->vps_acc; thread_link(ntd, p); /* not yet */ if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_UMTX) vdo_next(ctx); DBGR("%s: created thread=%p tid=%d\n", __func__, ntd, ntd->td_tid); out: return (error); } VPSFUNC static int vps_restore_proc_one(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_dumpobj *o1; struct vps_dump_proc *vdp; struct ucred *save_ucred; struct proc *np; struct vps *save_vps; struct thread *ntd; struct nameidata nd; caddr_t cpos; int error = 0; int i; /* calling functions make sure the next object is of type _PROC */ o1 = vdo_next(ctx); DBGR("%s: o1=%p\n", __func__, o1); save_ucred = curthread->td_ucred; vdp = (struct vps_dump_proc *)o1->data; cpos = (caddr_t)(vdp + 1); /* DBGR("%s: dtd->td_tid=%d dtd->td_ucred=%p dp->p_ucred=%p\n", __func__, dtd->td_tid, dtd->td_ucred, dp->p_ucred); */ /* * We get a struct proc that already contains loads * of resources, e.g. a thread ... * So release them all first ... * XXX p_stats * XXX p_ksi */ np = uma_zalloc(proc_zone, M_WAITOK); if ((ntd = FIRST_THREAD_IN_PROC(np)) != NULL) { PROC_LOCK(np); thread_unlink(ntd); PROC_UNLOCK(np); thread_free(ntd); } bzero(&np->p_startzero, __rangeof(struct proc, p_startzero, p_endzero)); /* assemble proc */ np->p_magic = P_MAGIC; np->p_pid = vdp->p_pid; np->p_swtick = vdp->p_swtick; /* XXX apply delta */ /* sigacts */ /* XXX checks: vdp->p_sigacts.ps_maxsig == _SIG_MAXSIG vdp->p_sigacts.ps_sigwords == _SIG_WORDS */ np->p_sigacts = malloc(sizeof(struct sigacts), M_SUBPROC, M_WAITOK | M_ZERO); np->p_sigacts->ps_refcnt = 1; mtx_init(&np->p_sigacts->ps_mtx, "sigacts", NULL, MTX_DEF); np->p_sigacts->ps_flag = vdp->p_sigacts.ps_flag; for (i = 0; i < _SIG_MAXSIG; i++) { np->p_sigacts->ps_sigact[i] = PTRFROM64(vdp->p_sigacts.ps_sigact[i]); np->p_sigacts->ps_catchmask[i].__bits[0] = vdp->p_sigacts.ps_catchmask[i][0]; np->p_sigacts->ps_catchmask[i].__bits[1] = vdp->p_sigacts.ps_catchmask[i][1]; np->p_sigacts->ps_catchmask[i].__bits[2] = vdp->p_sigacts.ps_catchmask[i][2]; np->p_sigacts->ps_catchmask[i].__bits[3] = vdp->p_sigacts.ps_catchmask[i][3]; } for (i = 0; i < _SIG_WORDS; i++) { np->p_sigacts->ps_sigonstack.__bits[i] = vdp->p_sigacts.ps_sigonstack[i]; np->p_sigacts->ps_sigintr.__bits[i] = vdp->p_sigacts.ps_sigintr[i]; np->p_sigacts->ps_sigreset.__bits[i] = vdp->p_sigacts.ps_sigreset[i]; np->p_sigacts->ps_signodefer.__bits[i] = vdp->p_sigacts.ps_signodefer[i]; np->p_sigacts->ps_siginfo.__bits[i] = vdp->p_sigacts.ps_siginfo[i]; np->p_sigacts->ps_sigignore.__bits[i] = vdp->p_sigacts.ps_sigignore[i]; np->p_sigacts->ps_sigcatch.__bits[i] = vdp->p_sigacts.ps_sigcatch[i]; np->p_sigacts->ps_freebsd4.__bits[i] = vdp->p_sigacts.ps_freebsd4[i]; np->p_sigacts->ps_osigset.__bits[i] = vdp->p_sigacts.ps_osigset[i]; np->p_sigacts->ps_usertramp.__bits[i] = vdp->p_sigacts.ps_usertramp[i]; } /* plimit */ /* XXX check: vdp->p_limit.pl_nlimits == RLIM_NLIMITS */ np->p_limit = lim_alloc(); for (i = 0; i < RLIM_NLIMITS; i++) { np->p_limit->pl_rlimit[i].rlim_cur = vdp->p_limit.pl_rlimit[i].rlim_cur; np->p_limit->pl_rlimit[i].rlim_max = vdp->p_limit.pl_rlimit[i].rlim_max; } /* --- */ np->p_cpulimit = vdp->p_cpulimit; knlist_init(&np->p_klist, &np->p_mtx, NULL, NULL, NULL, NULL); STAILQ_INIT(&np->p_ktr); strlcpy(np->p_comm, vdp->p_comm, sizeof(np->p_comm)); np->p_flag = vdp->p_flag; np->p_stops = vdp->p_stops; np->p_oppid = vdp->p_oppid; np->p_xstat = vdp->p_xstat; np->p_sigparent = vdp->p_sigparent; /* XXX */ np->p_stype = vdp->p_stype; np->p_step = vdp->p_step; np->p_args = NULL; DBGR("%s: pid=%d p_flag=%08x p_oppid=%d\n", __func__, np->p_pid, np->p_flag, np->p_oppid); /* --> from proc_linkup() */ sigqueue_init(&np->p_sigqueue, np); np->p_ksi = ksiginfo_alloc(1); np->p_ksi->ksi_flags = KSI_EXT | KSI_INS; LIST_INIT(&np->p_mqnotifier); np->p_numthreads = 0; /* ucred */ if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_UCRED) vdo_next(ctx); np->p_ucred = vps_restore_ucred_lookup(ctx, vps, vdp->p_ucred); KASSERT(np->p_ucred != NULL, ("%s: np->p_ucred == NULL\n", __func__)); (void)chgproccnt(np->p_ucred->cr_ruidinfo, 1, 0); curthread->td_ucred = np->p_ucred; prison_proc_hold(np->p_ucred->cr_prison); /* sysentvec */ if ((error = vps_restore_sysentvec(ctx, vps, np))) goto out; /* ktrace */ if (vdp->p_have_tracevp) { - if ((error = vps_restore_pathtovnode(ctx, vps, + if ((error = vps_restore_vnode(ctx, vps, &np->p_tracevp))) goto out; /* DBGR("%s: p_tracevp: path [%s] got vnode %p\n", __func__, o2->data, np->p_tracevp); */ /* XXX - could be different than p->p_ucred */ if (vdp->p_tracecred != NULL) np->p_tracecred = crhold(np->p_ucred); np->p_traceflag = vdp->p_traceflag; } callout_init(&np->p_itcallout, CALLOUT_MPSAFE); /* textvp */ if (vdp->p_have_textvp) { - if ((error = vps_restore_pathtovnode(ctx, vps, + if ((error = vps_restore_vnode(ctx, vps, &np->p_textvp))) goto out; DBGR("%s: p_textvp: path [...] got vnode %p\n", __func__, np->p_textvp); } /* vmspace */ if ((error = vps_restore_vmspace(ctx, vps, np, vdp->p_vmspace))) goto out; TAILQ_INIT(&np->p_threads); while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_THREAD) { if ((error = vps_restore_thread(ctx, vps, np))) goto out; vps_account(vps, VPS_ACC_THREADS, VPS_ACC_ALLOC, 1); } /* XXX lookup by id */ /* if (vdp->p_xthread_id != 0) np->p_xthread = FIRST_THREAD_IN_PROC(np); */ vps_account(vps, VPS_ACC_PROCS, VPS_ACC_ALLOC, 1); if ((error = vps_restore_fdset(ctx, vps, np, vdp->p_fd))) goto out; while (vdo_nextischild(ctx, o1)) { switch (vdo_typeofnext(ctx)) { case VPS_DUMPOBJT_PARGS: if ((error = vps_restore_pargs(ctx, vps, np))) goto out; break; case VPS_DUMPOBJT_SYSVSEM_PROC: if (vps_func->sem_restore_proc && (error = vps_func->sem_restore_proc(ctx, vps, np))) goto out; break; case VPS_DUMPOBJT_SYSVSHM_PROC: if (vps_func->shm_restore_proc && (error = vps_func->shm_restore_proc(ctx, vps, np))) goto out; break; case VPS_DUMPOBJT_SYSVMSG_PROC: if (vps_func->msg_restore_proc && (error = vps_func->msg_restore_proc(ctx, vps, np))) goto out; break; default: DBGR("%s: unknown type=%d\n", __func__, vdo_typeofnext(ctx)); break; } } /* proc tree */ save_vps = curthread->td_vps; curthread->td_vps = vps; sx_xlock(&VPS_VPS(vps, allproc_lock)); sx_xlock(&VPS_VPS(vps, proctree_lock)); LIST_INSERT_HEAD(&VPS_VPS(vps, allproc), np, p_list); LIST_INSERT_HEAD(&VPS_VPS(vps, pidhashtbl)[(np->p_pid) & VPS_VPS(vps, pidhash)], np, p_hash); VPS_VPS(vps, nprocs)++; /* These are ids rather than pointers. */ np->p_pptr = (struct proc *)((size_t)vdp->p_pptr_id); np->p_peers = (struct proc *)((size_t)vdp->p_peers_id); np->p_leader = (struct proc *)((size_t)vdp->p_leader_id); np->p_pgrp = (struct pgrp *)((size_t)vdp->p_pgrp_id); sx_xunlock(&VPS_VPS(vps, proctree_lock)); sx_xunlock(&VPS_VPS(vps, allproc_lock)); if (TAILQ_EMPTY(&np->p_threads)) { ERRMSG(ctx, "%s: process has no threads !\n", __func__); error = 0; goto out; } np->p_state = vdp->p_state; /* Add the extra lock that was set in vps_suspend(). */ np->p_lock++; PROC_LOCK(np); FOREACH_THREAD_IN_PROC(np, ntd) { /* Sets scheduler lock and cpuset, besides some other stuff. */ thread_lock(curthread); sched_fork_thread(curthread, ntd); thread_unlock(curthread); np->p_suspcount++; TD_SET_SUSPENDED(ntd); } PROC_UNLOCK(np); /* Enable ktrace for debugging. */ if (debug_restore_ktrace) { int error1, flags; struct vnode *vp; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, "/tmp/ktrace-all.out", curthread); flags = FREAD | FWRITE | O_NOFOLLOW; error1 = vn_open(&nd, &flags, 0, NULL); if (error1 == 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; VOP_UNLOCK(vp, 0); /* ktrops() unlocks proc */ PROC_LOCK(np); curthread->td_pflags |= TDP_INKTRACE; ktrops(curthread, np, KTROP_SET, 0xffffffff & ~KTRFAC_CSW, vp); curthread->td_pflags &= ~TDP_INKTRACE; (void)vn_close(vp, FWRITE, curthread->td_ucred, curthread); } else ERRMSG(ctx, "%s: ktrace / vn_open error: %d\n", __func__, error1); } out: curthread->td_ucred = save_ucred; if (error) ERRMSG(ctx, "%s: error = %d\n", __func__, error); return (error); } VPSFUNC static int vps_restore_proc_session(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_dumpobj *o1; struct vps_dump_session *vdsess; struct vps_dump_filepath *vdfp; struct session *nsess; struct vps_restore_obj *ro; struct nameidata nd; int error = 0; o1 = vdo_next(ctx); vdsess = (struct vps_dump_session *)o1->data; nsess = malloc(sizeof(*nsess), M_SESSION, M_WAITOK | M_ZERO); mtx_init(&nsess->s_mtx, "session", NULL, MTX_DEF | MTX_DUPOK); nsess->s_count = vdsess->s_count; /* This is a pid, fixup later. */ nsess->s_ttyvp = NULL; nsess->s_ttyp = NULL; nsess->s_sid = vdsess->s_sid; memcpy(nsess->s_login, vdsess->s_login, sizeof(nsess->s_login)); if (vdsess->s_have_ttyvp && vdo_typeofnext(ctx) == VPS_DUMPOBJT_FILE_PATH) { o1 = vdo_next(ctx); vdfp = (struct vps_dump_filepath *)o1->data; if ((strncmp(vdfp->fp_path, "/dev/pts/", 9)) == 0) { struct posix_openpt_args args; int unit; args.flags = O_RDWR; if ((sscanf(vdfp->fp_path, "/dev/pts/%d", &unit)) != 1) { ERRMSG(ctx, "%s: unable to find unit " "number in pts name [%s]\n", __func__, vdfp->fp_path); error = EINVAL; goto out; } DBGR("%s: pts unit number=%d\n", __func__, unit); if ((error = sys_posix_openpt_unit(curthread, &args, unit))) { ERRMSG(ctx, "%s: sys_posix_openpt_unit() " "error: %d\n", __func__, error); goto out; } /* is used later in vps_restore_fdset */ } NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, vdfp->fp_path, curthread); if ((error = namei(&nd))) { ERRMSG(ctx, "%s: namei([%s]): error = %d\n", __func__, vdfp->fp_path, error); goto out; } /* XXX VREF() ?! */ nsess->s_ttyvp = nd.ni_vp; VOP_UNLOCK(nd.ni_vp, 0); NDFREE(&nd, NDF_ONLY_PNBUF); DBGR("%s: path [%s] got vnode %p\n", __func__, vdfp->fp_path, nd.ni_vp); if ( ! (nsess->s_ttyvp->v_type == VCHR && nsess->s_ttyvp->v_rdev)) { ERRMSG(ctx, "%s: not a device !\n", __func__); error = EINVAL; goto out; } nsess->s_ttyp = nsess->s_ttyvp->v_rdev->si_drv1; } ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); ro->type = VPS_DUMPOBJT_SESSION; ro->new_ptr = nsess; ro->orig_id = nsess->s_sid; ro->spare[0] = (void *)(size_t)vdsess->s_leader_id; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); DBGR("%s: restored session %p/%d ttyvp=%p ttyp=%p\n", __func__, nsess, nsess->s_sid, nsess->s_ttyvp, nsess->s_ttyp); out: return (error); } VPSFUNC static int vps_restore_proc(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_dumpobj *o1; struct vps_dump_pgrp *vdpg; struct pgrp *npg, *pg; struct session *nsess; struct proc *p; struct vps_restore_obj *ro, *ro1; struct vps *save_vps; struct ucred *save_ucred; struct vnode *saverootvnode; struct tty *ttyp; int found; int error = 0; /* XXX See comment in vps_restore_mounts(). */ save_vps = curthread->td_vps; save_ucred = curthread->td_ucred; curthread->td_vps = vps; curthread->td_ucred = ctx->vps_ucred; /* * Temporarily change our root directory to the one of the vps * instance to be restored. * Makes namei() lookups much easier. */ FILEDESC_XLOCK(curthread->td_proc->p_fd); VREF(vps->_rootvnode); saverootvnode = curthread->td_proc->p_fd->fd_rdir; curthread->td_proc->p_fd->fd_rdir = vps->_rootvnode; FILEDESC_XUNLOCK(curthread->td_proc->p_fd); while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_PGRP) { o1 = vdo_next(ctx); ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK | M_ZERO); vdpg = (struct vps_dump_pgrp *)o1->data; npg = malloc(sizeof(*npg), M_PGRP, M_WAITOK | M_ZERO); mtx_init(&npg->pg_mtx, "process group", NULL, MTX_DEF | MTX_DUPOK); LIST_INIT(&npg->pg_members); npg->pg_id = vdpg->pg_id; npg->pg_jobc = vdpg->pg_jobc; /* This is the session id, fixup later. */ npg->pg_session = NULL; /* XXX Restore this stuff too. */ SLIST_INIT(&npg->pg_sigiolst); ro->type = VPS_DUMPOBJT_PGRP; ro->new_ptr = npg; ro->orig_id = npg->pg_id; ro->spare[0] = (void *)(size_t)vdpg->pg_session_id; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); DBGR("%s: restored pgrp %p/%d\n", __func__, npg, npg->pg_id); if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_SESSION) { error = vps_restore_proc_session(ctx, vps); if (error != 0) goto out; } } while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_PROC) { if ((error = vps_restore_proc_one(ctx, vps))) goto out; } /* Now fixup stuff like the proc tree. */ curthread->td_vps = vps; /* Fix up sessions. */ SLIST_FOREACH(ro, &ctx->obj_list, list) if (ro->type == VPS_DUMPOBJT_SESSION) { nsess = (struct session *)ro->new_ptr; if ((nsess->s_leader = pfind((size_t)ro->spare[0]))) PROC_UNLOCK(nsess->s_leader); DBGR("%s: fixed up session=%p s_leader=%p/%d\n", __func__, nsess, nsess->s_leader, nsess->s_leader ? nsess->s_leader->p_pid : -1); /* XXX check if every pointer is fixed up now. */ } /* For every pgrp, lookup session. */ SLIST_FOREACH(ro, &ctx->obj_list, list) if (ro->type == VPS_DUMPOBJT_PGRP) { pg = ro->new_ptr; found = 0; SLIST_FOREACH(ro1, &ctx->obj_list, list) if (ro1->type == VPS_DUMPOBJT_SESSION && ro1->orig_id == (size_t)ro->spare[0]) { DBGR("%s: found session %p/%d for " "pgrp %p/%d\n", __func__, ro1->new_ptr, ro1->orig_id, pg, pg->pg_id); pg->pg_session = ro1->new_ptr; found = 1; /* XXX check if every pointer is fixed up now. */ } KASSERT(found == 1, ("%s: no session found for " "pgrp=%p/%d\n", __func__, pg, pg->pg_id)); LIST_INSERT_HEAD(&VPS_VPS(vps, pgrphashtbl) [pg->pg_id & VPS_VPS(vps, pgrphash)], pg, pg_hash); DBGR("%s: inserted pgrp %p/%d\n", __func__, pg, pg->pg_id); } /* Fix up ttys. */ sx_slock(&VPS_VPS(vps, proctree_lock)); SLIST_FOREACH(ro, &ctx->obj_list, list) { struct tty *tp; if (ro->type != VPS_DUMPOBJT_PTS) continue; tp = ro->new_ptr; DBGR("%s: tp=%p tp->t_pgrp = %zu\n", __func__, tp, (intptr_t)tp->t_pgrp); tp->t_pgrp = pgfind((intptr_t)tp->t_pgrp); if (tp->t_pgrp) PGRP_UNLOCK(tp->t_pgrp); DBGR("%s: tp=%p tp->t_pgrp = %p\n", __func__, tp, tp->t_pgrp); } sx_sunlock(&VPS_VPS(vps, proctree_lock)); /* Fix up unix domain sockets. */ if ((error = vps_restore_fixup_unixsockets(ctx, vps))) goto out; /* Traverse V_allproc, lookup pgrp for each one. */ sx_xlock(&VPS_VPS(vps, proctree_lock)); LIST_FOREACH(p, &VPS_VPS(vps, allproc), p_list) { if ((pg = pgfind((intptr_t) p->p_pgrp))) PGRP_UNLOCK(pg); DBGR("%s: pgfind: pg=%p/%zu\n", __func__, pg, (intptr_t)p->p_pgrp); /* XXX check NULL */ //PROC_LOCK(p); p->p_pgrp = pg; LIST_INSERT_HEAD(&pg->pg_members, p, p_pglist); /* XXX not needed fixjobc(p, p->p_pgrp, 1); */ if ((p->p_peers = pfind((intptr_t)p->p_peers))) PROC_UNLOCK(p->p_peers); if ((p->p_leader = pfind((intptr_t)p->p_leader))) PROC_UNLOCK (p->p_leader); if ((p->p_pptr = pfind((intptr_t)p->p_pptr))) PROC_UNLOCK(p->p_pptr); if (p->p_pptr) LIST_INSERT_HEAD(&p->p_pptr->p_children, p, p_sibling); if (p->p_pgrp->pg_session && SESS_LEADER(p) && p->p_session->s_ttyp) { ttyp = p->p_session->s_ttyp; tty_lock(ttyp); ttyp->t_session = p->p_session; ttyp->t_sessioncnt++; tty_unlock(ttyp); DBGR("%s: set controlling session %p for ttyp %p\n", __func__, p->p_session, p->p_session->s_ttyp); } //PROC_UNLOCK(p); DBGR("%s: fixed up proc=%p/%d p->p_pgrp=%p p->p_session=%p " "p->p_pptr=%p p->p_stype=%08x\n", __func__, p, p->p_pid, p->p_pgrp, p->p_session, p->p_pptr, p->p_stype); } if ((VPS_VPS(vps, initpgrp) = pgfind((intptr_t)VPS_VPS(vps, initpgrp)))) PGRP_UNLOCK(VPS_VPS(vps, initpgrp)); /* XXX pfind() locks allproc_lock again ! */ if ((VPS_VPS(vps, initproc) = pfind((intptr_t)VPS_VPS(vps, initproc)))) PROC_UNLOCK(VPS_VPS(vps, initproc)); DBGR("%s: V_initpgrp=%p V_initproc=%p\n", __func__, VPS_VPS(vps, initpgrp), VPS_VPS(vps, initproc)); /* debug */ if (1) { if (PTRTO64(VPS_VPS(vps, initpgrp)) < 0x1000 || PTRTO64(VPS_VPS(vps, initproc)) < 0x1000) { ERRMSG(ctx, "%s: STOP\n", __func__); return (EINVAL); } } sx_xunlock(&VPS_VPS(vps, proctree_lock)); out: FILEDESC_XLOCK(curthread->td_proc->p_fd); curthread->td_proc->p_fd->fd_rdir = saverootvnode; vrele(vps->_rootvnode); FILEDESC_XUNLOCK(curthread->td_proc->p_fd); curthread->td_vps = save_vps; curthread->td_ucred = save_ucred; if (error) ERRMSG(ctx, "%s: error = %d\n", __func__, error); return (error); } VPSFUNC static int vps_restore_arg(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_dumpobj *o1; struct vps_dump_arg *vda; struct vps_dump_arg_ip4 *vda4; struct vps_dump_arg_ip6 *vda6; struct vps_dump_accounting *vdacc; size_t privsetsize_round; caddr_t cpos; int len; int i; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_ARG) { ERRMSG(ctx, "%s: VPS_DUMPOBJT_ARG missing !\n", __func__); return (-1); } vda = (struct vps_dump_arg *)o1->data; cpos = o1->data + sizeof(*vda); if (vda->privset_size != PRIV_SET_SIZE) { ERRMSG(ctx, "%s: vda->privset_size (%d) != " "PRIV_SET_SIZE (%d)\n", __func__, vda->privset_size, PRIV_SET_SIZE); return (EINVAL); } /* * XXX We must check here if the current vps is allowed * the privs and networks etc. that we are about to restore. * (In case of nested vps instances.) */ privsetsize_round = roundup(vda->privset_size, 8); len = vda->privset_size > PRIV_SET_SIZE ? PRIV_SET_SIZE : vda->privset_size; memcpy(vps->priv_allow_set, cpos, len); cpos += privsetsize_round; memcpy(vps->priv_impl_set, cpos, len); cpos += privsetsize_round; if (vda->ip4net_cnt > 0) { len = sizeof(struct vps_arg_ip4) * vda->ip4net_cnt; vps->vps_ip4 = malloc(len, M_VPS_CORE, M_WAITOK); for (i = 0; i < vda->ip4net_cnt; i++) { vda4 = (struct vps_dump_arg_ip4 *)cpos; memcpy(&vps->vps_ip4[i].addr, vda4->a4_addr, 0x4); memcpy(&vps->vps_ip4[i].mask, vda4->a4_mask, 0x4); cpos = (caddr_t)(vda4 + 1); } vps->vps_ip4_cnt = vda->ip4net_cnt; } if (vda->ip6net_cnt > 0) { len = sizeof(struct vps_arg_ip6) * vda->ip6net_cnt; vps->vps_ip6 = malloc(len, M_VPS_CORE, M_WAITOK); for (i = 0; i < vda->ip6net_cnt; i++) { vda6 = (struct vps_dump_arg_ip6 *)cpos; memcpy(&vps->vps_ip6[i].addr, vda6->a6_addr, 0x10); vps->vps_ip6[i].plen = vda6->a6_plen; cpos = (caddr_t)(vda6 + 1); } vps->vps_ip6_cnt = vda->ip6net_cnt; } if (vda->have_accounting) { vdacc = (struct vps_dump_accounting *)cpos; #define FILL_ACCVAL(x) \ vps->vps_acc->x.soft = vdacc->x.soft; \ vps->vps_acc->x.hard = vdacc->x.hard; \ vps->vps_acc->x.hits_soft = vdacc->x.hits_soft; \ vps->vps_acc->x.hits_hard = vdacc->x.hits_hard FILL_ACCVAL(virt); FILL_ACCVAL(phys); FILL_ACCVAL(kmem); FILL_ACCVAL(kernel); FILL_ACCVAL(buffer); FILL_ACCVAL(pctcpu); FILL_ACCVAL(blockio); FILL_ACCVAL(threads); FILL_ACCVAL(procs); #undef FILL_ACCVAL } return (0); } VPSFUNC static int vps_restore_mounts(struct vps_snapst_ctx *ctx, struct vps *vps, char *rootfspath) { struct vps_dump_mount_opt *dvmopt; struct vps_dump_mount *dvm; struct vps_dumpobj *o1; struct vps *savevps; struct ucred *saveucred, *ncr; struct vnode *save_rdir, *save_cdir; struct mntarg *ma; int errmsg_len; char *fspath; char *errmsg; int error = 0; int i; ncr = NULL; if (vps != NULL) { savevps = curthread->td_vps; saveucred = curthread->td_ucred; save_rdir = curthread->td_proc->p_fd->fd_rdir; save_cdir = curthread->td_proc->p_fd->fd_cdir; } else { savevps = NULL; saveucred = NULL; save_rdir = save_cdir = NULL; } errmsg_len = 0xf0; errmsg = malloc(errmsg_len, M_TEMP, M_WAITOK); while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_MOUNT) { o1 = vdo_next(ctx); dvm = (struct vps_dump_mount *)o1->data; ncr = NULL; /* Only do root fs mount now. */ if (vps == NULL && strcmp(dvm->mnton, rootfspath)) continue; /* Do all mounts now except root fs. */ if (vps != NULL && !strcmp(dvm->mnton, rootfspath)) continue; ma = NULL; if (dvm->optcnt * sizeof(*dvmopt) > (o1->size - sizeof(*dvm))) { ERRMSG(ctx, "%s: dvm->optcnt=%d seems invalid !\n", __func__, dvm->optcnt); error = EINVAL; goto out; } dvmopt = (struct vps_dump_mount_opt *)(dvm+1); for (i = 0; i < dvm->optcnt; i++) { DBGR("%s: opt name=[%s] value=%p len=%u\n", __func__, dvmopt->name, dvmopt->value, dvmopt->len); if (!strcmp(dvm->fstype, "nfs")) { /* if (!strcmp(dvmopt->name, "addr") || !strcmp(dvmopt->name, "fh") || !strcmp(dvmopt->name, "hostname")) */ if (1) ma = mount_arg(ma, dvmopt->name, dvmopt->value, dvmopt->len); } dvmopt += 1; } if (vdo_typeofnext(ctx) == VPS_DUMPOBJT_UCRED) { vdo_next(ctx); } DBGR("dvm=%p mntfrom=[%s] mnton=[%s] fstype=[%s] flags=%zx " "vpsmount=%d\n", dvm, dvm->mntfrom, dvm->mnton, dvm->fstype, (size_t)dvm->flags, dvm->vpsmount); if (vps != NULL && dvm->vpsmount) { curthread->td_vps = vps; curthread->td_proc->p_fd->fd_rdir = vps->_rootvnode; curthread->td_proc->p_fd->fd_cdir = vps->_rootvnode; /* Note that ucred gets duplicated in vfs_mount_alloc(). */ if (dvm->mnt_cred != NULL) { ncr = vps_restore_ucred_lookup(ctx, vps, dvm->mnt_cred); if (ncr == NULL) { ERRMSG(ctx, "%s: ucred not found " "!\n", __func__); error = EINVAL; goto out; } curthread->td_ucred = ncr; } } if (dvm->vpsmount) /* dvm->mnton is always absolute, so we have to strip it. */ fspath = dvm->mnton + strlen(rootfspath); else fspath = dvm->mnton; /*DBGR("%s: fspath=[%s]\n", __func__, fspath);*/ ma = mount_arg(ma, "fstype", dvm->fstype, -1); ma = mount_arg(ma, "fspath", fspath, -1); if (!strcmp(dvm->fstype, "nullfs") || !strcmp(dvm->fstype, "vpsfs")) ma = mount_arg(ma, "target", dvm->mntfrom, -1); ma = mount_arg(ma, "from", dvm->mntfrom, -1); ma = mount_arg(ma, "errmsg", errmsg, errmsg_len); memset(errmsg, 0, errmsg_len); error = kernel_mount(ma, dvm->flags); if (error) { ERRMSG(ctx, "%s: kernel_mount() error: %d [%s]\n", __func__, error, errmsg); goto out; } if (vps != NULL && dvm->vpsmount) { curthread->td_vps = savevps; curthread->td_ucred = saveucred; curthread->td_proc->p_fd->fd_rdir = save_rdir; curthread->td_proc->p_fd->fd_cdir = save_cdir; if (ncr != NULL) crfree(ncr); } } out: free(errmsg, M_TEMP); return (error); } VPSFUNC static void vps_restore_prison_fixup(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_restore_obj *obj1; struct prison *pr; SLIST_FOREACH(obj1, &ctx->obj_list, list) { if (obj1->type != VPS_DUMPOBJT_PRISON) continue; pr = (struct prison *)obj1->new_ptr; /* Clear the extra references that we had while restoring. */ prison_lock(pr); pr->pr_ref--; pr->pr_uref--; prison_unlock(pr); } } VPSFUNC static struct prison * vps_restore_prison_lookup(struct vps_snapst_ctx *ctx, struct vps *vps, struct prison *old_pr) { struct vps_restore_obj *obj1; struct prison *new_pr; new_pr = NULL; SLIST_FOREACH(obj1, &ctx->obj_list, list) { if (obj1->type != VPS_DUMPOBJT_PRISON) continue; if (obj1->orig_ptr == old_pr) { new_pr = obj1->new_ptr; DBGR("%s: found new prison ptr: orig=%p new=%p\n", __func__, obj1->orig_ptr, obj1->new_ptr); break; } } KASSERT(new_pr != NULL, ("%s: old_pr=%p new_pr==NULL\n", __func__, old_pr)); return (new_pr); } VPSFUNC static int vps_restore_prison_one(struct vps_snapst_ctx *ctx, struct vps *vps) { struct prison *npr, *ppr, *tpr; struct vps_dumpobj *o1; struct vps_dump_prison *vdpr; struct vps_restore_obj *ro; caddr_t cpos; int error = 0; int i; o1 = vdo_next(ctx); KASSERT(o1->type == VPS_DUMPOBJT_PRISON, ("%s: o1=%p o1->type = %d\n", __func__, o1, o1->type)); vdpr = (struct vps_dump_prison *)o1->data; DBGR("%s: orig_ptr=%p id=%d name=[%s]\n", __func__, vdpr->pr_origptr, vdpr->pr_id, vdpr->pr_name); if (vdpr->pr_parent != NULL) { npr = malloc(sizeof(*npr), M_PRISON, M_WAITOK|M_ZERO); LIST_INIT(&npr->pr_children); mtx_init(&npr->pr_mtx, "jail mutex", NULL, MTX_DEF | MTX_DUPOK); npr->pr_id = vdpr->pr_id; strlcpy(npr->pr_name, vdpr->pr_name, sizeof(npr->pr_name)); /* * We copy this from dumped one, but root vnode must be * restored from old vnode, since the path might have * been moved ! */ strlcpy(npr->pr_path, vdpr->pr_path, sizeof(vdpr->pr_path)); - if ((error = vps_restore_pathtovnode(ctx, vps, + if ((error = vps_restore_vnode(ctx, vps, &npr->pr_root))) return (error); npr->pr_parent = vps_restore_prison_lookup(ctx, vps, vdpr->pr_parent); ppr = npr->pr_parent; ppr->pr_ref++; ppr->pr_uref++; if (vdpr->pr_flags & PR_PERSIST) { npr->pr_flags |= PR_PERSIST; npr->pr_ref++; npr->pr_uref++; } /* We free them after processes are attached. */ npr->pr_ref++; npr->pr_uref++; /* XXX */ cpuset_create_root(ppr, &npr->pr_cpuset); /* XXX */ npr->pr_flags = vdpr->pr_flags; /* XXX check against parent's values */ npr->pr_securelevel = vdpr->pr_securelevel; npr->pr_childmax = vdpr->pr_childmax; npr->pr_allow = vdpr->pr_allow; npr->pr_enforce_statfs = vdpr->pr_enforce_statfs; npr->pr_ip4s = vdpr->pr_ip4s; npr->pr_ip6s = vdpr->pr_ip6s; npr->pr_ip4 = malloc(sizeof(npr->pr_ip4[0]) * npr->pr_ip4s, M_PRISON, M_WAITOK); npr->pr_ip6 = malloc(sizeof(npr->pr_ip6[0]) * npr->pr_ip6s, M_PRISON, M_WAITOK); cpos = vdpr->pr_ipdata; /* vps_print_ascii(vdpr->pr_ipdata, roundup(vdpr->pr_ip4s * 0x4, 8) + vdpr->pr_ip6s * 0x10); */ for (i = 0; i < vdpr->pr_ip4s; i++) { memcpy(&npr->pr_ip4[i], cpos, 0x4); cpos += 0x4; } cpos = (caddr_t)roundup((size_t)cpos, 8); for (i = 0; i < vdpr->pr_ip6s; i++) { memcpy(&npr->pr_ip6[i], cpos, 0x10); cpos += 0x10; } if (vdpr->pr_flags & PR_VNET) { npr->pr_flags |= PR_VNET; if ((error = vps_restore_vnet(ctx, vps, &npr->pr_vnet))) { ERRMSG(ctx, "%s: vps_restore_vnet(): %d\n", __func__, error); return (error); } DBGR("%s: PR_VNET: npr->pr_vnet = %p\n", __func__, npr->pr_vnet); } else { npr->pr_vnet = ppr->pr_vnet; DBGR("%s: inherit vnet: npr->pr_vnet = %p\n", __func__, npr->pr_vnet); } TAILQ_FOREACH(tpr, &VPS_VPS(vps, allprison), pr_list) if (tpr->pr_id >= npr->pr_id) { TAILQ_INSERT_BEFORE(tpr, npr, pr_list); break; } if (tpr == NULL) TAILQ_INSERT_TAIL(&VPS_VPS(vps, allprison), npr, pr_list); LIST_INSERT_HEAD(&ppr->pr_children, npr, pr_sibling); for (tpr = ppr; tpr != NULL; tpr = tpr->pr_parent) tpr->pr_childcount++; } else { /* this is dumped prison0 */ npr = VPS_VPS(vps, prison0); /* Skip over filepath object. */ vdo_next(ctx); } ro = malloc(sizeof(*ro), M_VPS_RESTORE, M_WAITOK|M_ZERO); ro->type = VPS_DUMPOBJT_PRISON; ro->orig_ptr = vdpr->pr_origptr; ro->new_ptr = npr; SLIST_INSERT_HEAD(&ctx->obj_list, ro, list); return (error); } VPSFUNC static int vps_restore_prison(struct vps_snapst_ctx *ctx, struct vps *vps) { int error = 0; while (vdo_typeofnext(ctx) == VPS_DUMPOBJT_PRISON) { if ((error = vps_restore_prison_one(ctx, vps))) goto out; } out: return (error); } /* * Restore vps instance. */ VPSFUNC static int vps_restore_vps(struct vps_snapst_ctx *ctx, const char *vps_name, struct vps **vps_in) { struct vps_param vps_pr; struct vps_dumpobj *o1; struct vps_dump_vps *vdi; struct vps *vps; int nexttype; int error = 0; memset(&vps_pr, 0, sizeof(vps_pr)); *vps_in = NULL; o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_VPS) { ERRMSG(ctx, "%s: wrong object type: %p type=%d\n", __func__, o1, o1->type); error = EINVAL; goto out; } vdi = (struct vps_dump_vps *)o1->data; // XXX dvps->vps_name check string termination // XXX dvps->_rootpath check string termination /* Name */ if (vps_name[0]) strlcpy(vps_pr.name, vps_name, sizeof(vps_pr.name)); else strlcpy(vps_pr.name, vdi->vps_name, sizeof(vps_pr.name)); /* Filesystem root */ strlcpy(vps_pr.fsroot, vdi->rootpath, sizeof(vps_pr.fsroot)); /* Restore vfs root mount. */ if ((error = vps_restore_mounts(ctx, NULL, vps_pr.fsroot))) { goto out; } /* * Actually allocating a vps. * * XXX Doing this first and then mounting the root filesystem * would make things much easier. */ if ((vps = vps_alloc(curthread->td_vps, &vps_pr, vps_pr.name, &error)) == NULL) { goto out; } *vps_in = vps; vps->vps_status = VPS_ST_SUSPENDED; /* * Get a generic ucred we can use for various vps system things. */ ctx->vps_ucred = crdup(curthread->td_ucred); vps_deref(ctx->vps_ucred->cr_vps, ctx->vps_ucred); ctx->vps_ucred->cr_vps = vps; vps_ref(ctx->vps_ucred->cr_vps, ctx->vps_ucred); prison_free(ctx->vps_ucred->cr_prison); ctx->vps_ucred->cr_prison = VPS_VPS(vps, prison0); prison_hold(ctx->vps_ucred->cr_prison); DBGR("%s: ctx->vps_ucred = %p\n", __func__, ctx->vps_ucred); /* Restore all ucreds at once. */ if ((error = vps_restore_ucred_all(ctx, vps))) { goto out; } /* Restore all the remaining vfs mounts. */ ctx->curobj = o1; if ((error = vps_restore_mounts(ctx, vps, vps_pr.fsroot))) { goto out; } if ((error = vps_restore_arg(ctx, vps))) { goto out; } /* Restore network related stuff. */ if ((error = vps_restore_vnet(ctx, vps, &vps->vnet))) { goto out; } /* Restore prisons (jails). */ if ((error = vps_restore_prison(ctx, vps))) { goto out; } /* Link prisons back into ucreds */ if ((error = vps_restore_ucred_fixup(ctx, vps))) { goto out; } while ((nexttype = vdo_typeofnext(ctx)) != VPS_DUMPOBJT_PGRP) { switch (nexttype) { case VPS_DUMPOBJT_SYSVSEM_VPS: if (vps_func->sem_restore_vps) error = vps_func->sem_restore_vps(ctx, vps); else error = EOPNOTSUPP; break; case VPS_DUMPOBJT_SYSVSHM_VPS: if (vps_func->shm_restore_vps) error = vps_func->shm_restore_vps(ctx, vps); else error = EOPNOTSUPP; break; case VPS_DUMPOBJT_SYSVMSG_VPS: if (vps_func->msg_restore_vps) error = vps_func->msg_restore_vps(ctx, vps); else error = EOPNOTSUPP; break; default: /* Just ignore and skip. */ vdo_next(ctx); break; } if (error) goto out; } VPS_VPS(vps, initproc) = (struct proc *) ((long)vdi->initproc_id); /* ID */ VPS_VPS(vps, initpgrp) = (struct pgrp *) ((long)vdi->initpgrp_id); /* ID */ strlcpy(VPS_VPS(vps, hostname), vdi->hostname, sizeof(VPS_VPS(vps, hostname))); VPS_VPS(vps, boottimebin).sec = vdi->boottime.tv_sec; VPS_VPS(vps, boottimebin).frac = 0; VPS_VPS(vps, boottime).tv_sec = vdi->boottime.tv_sec; VPS_VPS(vps, boottime).tv_usec = vdi->boottime.tv_usec; VPS_VPS(vps, lastpid) = vdi->lastpid; vps->restore_count = vdi->restore_count + 1; /* * Restore processes. */ if ((error = vps_restore_proc(ctx, vps))) { goto out; } VPS_VPS(vps, lastpid) = vdi->lastpid; /* * Various fixup routines. */ vps_restore_prison_fixup(ctx, vps); if (vps_func->sem_restore_fixup && (error = vps_func->sem_restore_fixup(ctx, vps))) goto out; if (vps_func->shm_restore_fixup && (error = vps_func->shm_restore_fixup(ctx, vps))) goto out; if (vps_func->msg_restore_fixup && (error = vps_func->msg_restore_fixup(ctx, vps))) goto out; out: return (error); } /* * Go through the dump image and link every dump object into this list. */ #if 0 VPSFUNC static int vps_restore_fixup_objlist(struct vps_snapst_ctx *ctx, struct vps_dumpheader *dumphdr, struct vps_dumpobj *o) { int error = 0; int cnt = 0; DBGR("%s: \n", __func__); SLIST_INIT(&vdi->dumpobj_list); ctx->dumpobj_list = &vdi->dumpobj_list; while ( ( ((caddr_t)o) + sizeof(*o) < ctx->data + ctx->dsize) && ( ((caddr_t)o) > ctx->data) && o->type && o->size) { /* DBGR("%s: o=%p o->type=%d o->size=%d\n", __func__, o, o->type, o->size); */ SLIST_INSERT_HEAD(ctx->dumpobj_list, o, list); ++cnt; /* * If the end-of-snapshot record is missing, the range check * breaks the loop. */ if (o->type == VPS_DUMPOBJT_END) { /* Reached end of snapshot. */ break; } /* Next object. */ o = (struct vps_dumpobj *)(o->data + o->subsize); } DBGR("%s: got %d elements\n", __func__, cnt); return (error); } #endif /* 0 */ /* * The user supplies an userspace address, where the snapshot * dump is located. * The first part of this dump contains the kernel memory parts, * which will be copied or newly assembled anyway. * * The second (usually much larger) part is the userspace memory * of the dumped processes. The pages will be moved into the * new processes vmspaces without copying. */ VPSFUNC static int vps_restore_copyin(struct vps_snapst_ctx *ctx, struct vps_arg_snapst *va) { struct vps_dumpheader *dumphdr; struct vps_dumpobj *o1; vm_offset_t kvaddr; u_int checksum1, checksum2; vm_page_t m; int npages; int i; int error = 0; DBGR("%s: \n", __func__); ctx->data = NULL; ctx->vmobj = NULL; /* Snapshot must be page-aligned! */ if ((void *)trunc_page((unsigned long)va->database) != va->database) { ERRMSG(ctx, "%s: dump must be page aligned but is not: " "%p\n", __func__, va->database); error = EFAULT; goto fail; } if (vm_page_count_min()) { ERRMSG(ctx, "%s: low on memory: v_free_min=%u > " "(v_free_count=%u + v_cache_count=%u)\n", __func__, cnt.v_free_min, cnt.v_free_count, cnt.v_cache_count); ERRMSG(ctx, "%s: cnt.v_inactive_count=%u\n", __func__, cnt.v_inactive_count); error = ENOMEM; goto fail; } DBGR("%s: v_free_min=%u v_free_count=%u + v_cache_count=%u\n", __func__, cnt.v_free_min, cnt.v_free_count, cnt.v_cache_count); ctx->data = malloc(4 << PAGE_SHIFT, M_VPS_RESTORE, M_WAITOK | M_ZERO); /* * First copy only the first page in, to look how much kernel * memory is needed. */ if ((error = copyin(va->database, ctx->data, PAGE_SIZE))) goto fail; dumphdr = (struct vps_dumpheader *)ctx->data; if (vld_checkheader(dumphdr)) { ERRMSG(ctx, "%s: dump is invalid\n", __func__); vld_printheader(dumphdr); error = EINVAL; goto fail; } #ifdef DIAGNOSTIC if (debug_restore) vld_printheader(dumphdr); #endif if (vps_md_restore_checkarch(dumphdr->ptrsize, dumphdr->byteorder) != 0) { ERRMSG(ctx, "%s: wrong architecture: ptrsize=%x " "byteorder=%x !\n", __func__, dumphdr->ptrsize, dumphdr->byteorder); error = EINVAL; goto fail; } if (dumphdr->version != VPS_DUMPH_VERSION) { ERRMSG(ctx, "%s: unsupported dump version %08x, " "want %08x\n", __func__, dumphdr->version, VPS_DUMPH_VERSION); error = EINVAL; goto fail; } /* * Since there is swap not all has to fit in memory, * but try to avoid total memory exhaustion. */ #ifndef INVARIANTS if (1 && (dumphdr->nuserpages + dumphdr->nsyspages) / 2 > (cnt.v_free_count + cnt.v_cache_count + cnt.v_inactive_count)) { DBGR("%s: (dumphdr->nuserpages=%u + dumphdr->nsyspages=%u) " "/ 2 > \n" " (v_free_count=%u + v_cache_count=%u + " "v_inactive_count=%u)\n", __func__, dumphdr->nuserpages, dumphdr->nsyspages, cnt.v_free_count, cnt.v_cache_count, cnt.v_inactive_count); ERRMSG(ctx, "%s: low on memory, not restoring " "VPS instance (pid=%u)\n", __func__, curthread->td_proc->p_pid); error = ENOMEM; goto fail; } #endif ctx->nuserpages = dumphdr->nuserpages; ctx->nsyspages = dumphdr->nsyspages; ctx->dsize = dumphdr->nsyspages << PAGE_SHIFT; ctx->userpagesaddr = ((caddr_t)va->database) + ctx->dsize; ctx->userpagesidx = 0; SLIST_INIT(&ctx->obj_list); free(ctx->data, M_VPS_RESTORE); ctx->vmobj = vm_object_allocate(OBJT_DEFAULT, ctx->nsyspages); if (ctx->vmobj == NULL) { error = ENOMEM; goto fail; } vm_map_lock(kernel_map); /* Find free space in kernel virtual address space. */ if (vm_map_findspace(kernel_map, vm_map_min(kernel_map), ctx->nsyspages << PAGE_SHIFT, &kvaddr) != KERN_SUCCESS) { vm_map_unlock(kernel_map); vm_object_deallocate(ctx->vmobj); ctx->vmobj = NULL; ERRMSG(ctx, "%s: vm_map_findspace failed\n", __func__); error = ENOMEM; goto fail; } if (vm_map_insert(kernel_map, ctx->vmobj, kvaddr - VM_MIN_KERNEL_ADDRESS, kvaddr, kvaddr + (ctx->vmobj->size << PAGE_SHIFT), VM_PROT_ALL, VM_PROT_ALL, 0) != KERN_SUCCESS) { vm_map_unlock(kernel_map); vm_object_deallocate(ctx->vmobj); ctx->vmobj = NULL; ERRMSG(ctx, "%s: vm_map_insert failed\n", __func__); error = ENOMEM; goto fail; } vm_map_unlock(kernel_map); VM_OBJECT_WLOCK(ctx->vmobj); for (i = 0; i < ctx->vmobj->size; i++) { do { m = vm_page_alloc(ctx->vmobj, i, VM_ALLOC_NORMAL|VM_ALLOC_WIRED); if (m == NULL) { /* Assume that object will not be modified. */ VM_OBJECT_WUNLOCK(ctx->vmobj); vm_waitpfault(); VM_OBJECT_WLOCK(ctx->vmobj); } } while (m == NULL); pmap_qenter(kvaddr + (i << PAGE_SHIFT), &m, 1); vm_page_wakeup(m); } VM_OBJECT_WUNLOCK(ctx->vmobj); DBGR("%s: mapped syspages vm object %p at %zx - %zx\n", __func__, ctx->vmobj, (size_t)kvaddr, (size_t)(kvaddr + (ctx->vmobj->size << PAGE_SHIFT))); ctx->data = ctx->cpos = (void *)kvaddr; DBGR("%s: ctx->data=%p ctx->dsize=%zx\n", __func__, ctx->data, (size_t)ctx->dsize); if ((error = copyin(va->database, ctx->data, ctx->dsize))) goto fail; dumphdr = (struct vps_dumpheader *)ctx->data; ctx->dumphdr = dumphdr; checksum1 = dumphdr->checksum; dumphdr->checksum = 0; checksum2 = vps_cksum(ctx->data, ctx->dsize); if (checksum1 != checksum2) { ERRMSG(ctx, "%s: CHECKSUM mismatch: snapshot info: " "%08x calculated: %08x\n", __func__, checksum1, checksum2); error = EINVAL; goto fail; } else DBGR("%s: CHECKSUM is valid !\n", __func__); dumphdr->checksum = checksum1; o1 = (struct vps_dumpobj *)(dumphdr + 1); ctx->rootobj = o1; ctx->relative = 1; ctx->elements = -1; if (vdo_checktree(ctx)) { ERRMSG(ctx, "%s: dump tree is invalid !\n", __func__); vdo_printtree(ctx); return (EINVAL); } vdo_makeabsolute(ctx); DBGR("%s: dump tree is valid and contains %d elements\n", __func__, ctx->elements); /* XXX maybe link together wil lists/tailqs ... */ vm_map_protect(kernel_map, kvaddr, kvaddr + (ctx->vmobj->size << PAGE_SHIFT), VM_PROT_READ, 0); DBGR("%s: set map entry to readonly\n", __func__); /* Check if the user really has the memory containing the userpages. */ if ((m = vps_restore_getuserpage(ctx, ctx->nuserpages - 1, 1)) == NULL) { ERRMSG(ctx, "%s: user supplied memory range " "inaccessible !\n", __func__); error = EFAULT; goto fail; } VM_OBJECT_WLOCK(m->object); vm_page_wakeup(m); VM_OBJECT_WUNLOCK(m->object); return (0); fail: if (ctx->data != NULL && ctx->vmobj == NULL) { free(ctx->data, M_VPS_RESTORE); } else if (ctx->vmobj != NULL) { npages = ctx->vmobj->size; pmap_qremove(kvaddr, npages); VM_OBJECT_WLOCK(ctx->vmobj); while (npages) { npages--; m = TAILQ_LAST(&ctx->vmobj->memq, pglist); vm_page_lock(m); vm_page_unwire(m, 0); vm_page_free(m); vm_page_unlock(m); } VM_OBJECT_WUNLOCK(ctx->vmobj); /* This also destroys the vm object. */ (void)vm_map_remove(kernel_map, kvaddr, kvaddr + (ctx->vmobj->size << PAGE_SHIFT)); } return (error); } VPSFUNC int vps_restore(struct vps_dev_ctx *dev_ctx, struct vps_arg_snapst *va) { struct vps_snapst_ctx *ctx; struct vps_dumpheader *dumphdr; struct vps *vps, *vps_save; struct vps_dumpobj *o1; time_t starttime; void *ptr; int error = 0; #ifdef DIAGNOSTIC vps_snapst_print_errormsgs = debug_restore; #endif starttime = time_second; vps_restore_mod_refcnt++; DBGR("%s: \n", __func__); vps = NULL; vps_save = curthread->td_vps; ctx = malloc(sizeof(*ctx), M_VPS_RESTORE, M_WAITOK|M_ZERO); LIST_INIT(&ctx->errormsgs); if (vps_func->vps_dumpobj_create == NULL) { ERRMSG(ctx, "%s: vps_libdump module not loaded\n", __func__); error = EOPNOTSUPP; goto out; } #ifdef INVARIANTS /* XXX debugging: if more than 3 dead vpses, stop */ { struct vps *vps2; int dead_cnt = 0; LIST_FOREACH(vps2, &vps_head, vps_all) if (vps2->vps_status == VPS_ST_DEAD) dead_cnt++; if (dead_cnt > 3) { printf("%s: more than 3 dead vps instances\n", __func__); kdb_enter(KDB_WHY_BREAK, "VPS break to debugger"); } } #endif if ((error = vps_restore_copyin(ctx, va))) goto out; ctx->cpos = ctx->data; dumphdr = ctx->dumphdr; o1 = ctx->rootobj; if (o1->type != VPS_DUMPOBJT_ROOT) { ERRMSG(ctx, "%s: missing root object\n", __func__); error = EINVAL; goto out; } o1 = vdo_next(ctx); if (o1->type != VPS_DUMPOBJT_SYSINFO) { ERRMSG(ctx, "%s: missing sysinfo object\n", __func__); error = EINVAL; goto out; } ctx->old_sysinfo = (struct vps_dump_sysinfo *)o1->data; if ((error = vps_restore_vps(ctx, va->vps_name, &vps))) { goto out; } /* * Free resources. */ vps_restore_cleanup_vmspace(ctx, vps, &ctx->obj_list); vps_restore_cleanup_pipe(ctx, vps, &ctx->obj_list); vps_restore_cleanup_fdset(ctx, vps, &ctx->obj_list); #ifdef INVARIANTS vps_restore_ucred_checkall(ctx, vps); #endif vps_restore_cleanup_ucred(ctx, vps, &ctx->obj_list); while ( ! SLIST_EMPTY(&ctx->obj_list)) { ptr = SLIST_FIRST(&ctx->obj_list); SLIST_REMOVE_HEAD(&ctx->obj_list, list); free(ptr, M_VPS_RESTORE); } if (ctx->vps_ucred) { DBGR("%s: ctx->vps_ucred->cr_ref=%d\n", __func__, ctx->vps_ucred->cr_ref); crfree(ctx->vps_ucred); } out: if (ctx->vmobj != NULL) { vm_offset_t kvaddr; vm_page_t m; int npages; kvaddr = (vm_offset_t)ctx->data; npages = ctx->vmobj->size; pmap_qremove(kvaddr, npages); VM_OBJECT_WLOCK(ctx->vmobj); while (npages) { npages--; m = TAILQ_LAST(&ctx->vmobj->memq, pglist); /* On early error condition we might have less than npages */ if (m == NULL) break; vm_page_lock(m); vm_page_unwire(m, 0); vm_page_free(m); vm_page_unlock(m); } VM_OBJECT_WUNLOCK(ctx->vmobj); /* This also destroys the vm object. */ (void)vm_map_remove(kernel_map, kvaddr, kvaddr + (ctx->vmobj->size << PAGE_SHIFT)); } if (1) { struct vps_snapst_errmsg *msg; char *buf, *bufpos; size_t buflen; int error2; buflen = 0; LIST_FOREACH(msg, &ctx->errormsgs, list) { printf("%s\n", msg->str); buflen += strlen(msg->str); } buf = bufpos = malloc(buflen + 1, M_TEMP, M_WAITOK); LIST_FOREACH(msg, &ctx->errormsgs, list) { memcpy(bufpos, msg->str, strlen(msg->str)); bufpos += strlen(msg->str); } *(bufpos) = '\0'; bufpos += 1; DBGR("%s: va->msgbase=%p va->msglen=%zu\n", __func__, va->msgbase, va->msglen); if (va->msgbase != NULL && va->msglen > 0) { if (va->msglen < (bufpos - buf)) printf("%s: warning: user-supplied buffer " "too small for error messages\n", __func__); if ((error2 = copyout(buf, va->msgbase, min(va->msglen, (bufpos - buf))))) printf("%s: error messages copyout=%d\n", __func__, error2); } free(buf, M_TEMP); while (!LIST_EMPTY(&ctx->errormsgs)) { msg = LIST_FIRST(&ctx->errormsgs); LIST_REMOVE(msg, list); free(msg, M_TEMP); } } free(ctx, M_VPS_RESTORE); if (error) { /* XXX Perform a proper cleanup ! */ if (vps != NULL) { vps->vps_status = VPS_ST_DEAD; snprintf(vps->vps_name, sizeof(vps->vps_name), "dead_%p", vps); } DBGR("%s: error = %d\n", __func__, error); } curvnet = NULL; curthread->td_vps = vps_save; KASSERT(curthread->td_vps == curthread->td_ucred->cr_vps, ("%s: curthread->td_vps=%p curthread->td_ucred->cr_vps=%p\n", __func__, curthread->td_vps, curthread->td_ucred->cr_vps)); vps_restore_mod_refcnt--; DBGR("%s: total time: %lld seconds\n", __func__, (long long int)(time_second - starttime)); return (error); } /* XXX This code should be protected by module refcount as well. */ void vps_restore_return(struct thread *td, struct trapframe *frame) { cpu_set_syscall_retval(td, td->td_errno); userret(td, frame); #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrstruct("VPS", "VPS RESTORED", 13); #endif mtx_assert(&Giant, MA_NOTOWNED); } static int vps_restore_modevent(module_t mod, int type, void *data) { int error; error = 0; switch (type) { case MOD_LOAD: vps_restore_mod_refcnt = 0; vps_func->vps_restore = vps_restore; vps_func->vps_restore_ucred = vps_restore_ucred; vps_func->vps_restore_ucred_lookup = vps_restore_ucred_lookup; vps_func->vps_restore_return = vps_restore_return; break; case MOD_UNLOAD: if (vps_restore_mod_refcnt > 0) return (EBUSY); vps_func->vps_restore = NULL; vps_func->vps_restore_ucred = NULL; vps_func->vps_restore_ucred_lookup = NULL; vps_func->vps_restore_return = NULL; break; default: error = EOPNOTSUPP; break; } return (error); } static moduledata_t vps_restore_mod = { "vps_restore", vps_restore_modevent, 0 }; DECLARE_MODULE(vps_restore, vps_restore_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); #endif /* VPS */ /* EOF */ Index: projects/vps/sys/vps/vps_snapst.c =================================================================== --- projects/vps/sys/vps/vps_snapst.c (revision 256999) +++ projects/vps/sys/vps/vps_snapst.c (revision 257000) @@ -1,3615 +1,3701 @@ /*- * Copyright (c) 2009-2013 Klaus P. Ohrhallinger * All rights reserved. * * Development of this software was partly funded by: * TransIP.nl * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ static const char vpsid[] = "$Id: vps_snapst.c 180 2013-06-14 16:52:13Z klaus $"; #include #include "opt_ddb.h" #include "opt_global.h" #include "opt_compat.h" #ifdef VPS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vps_account.h" #include "vps_user.h" #include "vps.h" #include "vps2.h" #include #define _VPS_SNAPST_H_ALL #include "vps_snapst.h" #include "vps_libdump.h" #define ERRMSG vps_snapst_pusherrormsg #ifdef DIAGNOSTIC #define DBGS if (debug_snapst) printf static int debug_snapst = 1; SYSCTL_INT(_debug, OID_AUTO, vps_snapst_debug, CTLFLAG_RW, &debug_snapst, 0, ""); #else #define DBGS(x, ...) #endif /* DIAGNOSTIC */ MALLOC_DEFINE(M_VPS_SNAPST, "vps_snapst", "Virtual Private Systems Snapshot memory"); static int vps_snapshot_sysinfo(struct vps_snapst_ctx *ctx, struct vps *vps); static int vps_snapshot_vps(struct vps_snapst_ctx *ctx, struct vps *vps); static int vps_snapshot_arg(struct vps_snapst_ctx *ctx, struct vps *vps); static int vps_snapshot_mounts(struct vps_snapst_ctx *ctx, struct vps *vps); static int vps_snapshot_prison(struct vps_snapst_ctx *ctx, struct vps *vps); static int vps_snapshot_proc(struct vps_snapst_ctx *ctx, struct vps *vps); static int vps_snapshot_proc_one(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p); static int vps_snapshot_vnet(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet *vnet); static int vps_snapshot_vnet_route_table(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet *vnet, int fibnum, int af); static int vps_snapshot_ucred(struct vps_snapst_ctx *ctx, struct vps *vps, struct ucred *cr, int how); static int vps_snapst_mod_refcnt; /* * * * * * Support functions. * * * * */ /* * * * * * Snapshot memory functions. * * * * * * Since the required amount of memory for storing the snapshot is unkown * in the beginning and can't be determined in advance (as soon as locks * are released objects may change) memory has to be allocated dynamically. * Working with memory chunks or reallocating and copying everything, * including change of memory adresses, lots of times, is slow and tedious. * So we reserve a virtual adress space big enough to hold the whole kernel * part of the snapshot. On 32bit architectures it *could* be a problem * because in default configuration the kernel address space is only 1 GB, * but is still the best solution, IMHO. On 64bit architectures there is * enough address space to feed pigs with. * The base of the snapshot memory (ctx->data) never changes. Before copying * data vps_ctx_extend() must be called. This function allocates and wires * physical pages, if called with M_NOWAIT only what is absolutely * necessary, if called with M_WAITOK some extra pages get allocated, * so the next couple of runs don't need to actually allocate memory. */ VPSFUNC static int vps_ctx_alloc(struct vps_snapst_ctx *ctx, struct vps *vps) { vm_offset_t kvaddr; int npages; int error; /* * This space must be big enough to hold the whole kernel part * of the snapshot. * On 64bit systems we can set it to something like 1 GB. * * npages --> 10240 --> 40 mbyte (assuming PAGE_SIZE == 4K) */ npages = VPS_MD_SNAPCTX_NPAGES; ctx->maxsize = npages << PAGE_SHIFT; ctx->dsize2 = 0; ctx->vmobj = vm_object_allocate(OBJT_DEFAULT, npages); if (ctx->vmobj == NULL) { error = ENOMEM; ERRMSG(ctx, "%s: vm_object_allocate failed\n", __func__); return (error); } vm_map_lock(kernel_map); /* Find free space in kernel virtual address space. */ if (vm_map_findspace(kernel_map, vm_map_min (kernel_map), npages << PAGE_SHIFT, &kvaddr) != KERN_SUCCESS) { error = ENOMEM; vm_map_unlock(kernel_map); vm_object_deallocate(ctx->vmobj); ERRMSG(ctx, "%s: vm_map_findspace failed\n", __func__); return (error); } /* * Reserve it by mapping the object. * Note that if an address in this space gets accessed * prior to calling vps_ctx_extend() a page fault gets * triggered and kernel panics ! */ if (vm_map_insert(kernel_map, ctx->vmobj, kvaddr - VM_MIN_KERNEL_ADDRESS, kvaddr, kvaddr + (npages << PAGE_SHIFT), VM_PROT_ALL, VM_PROT_ALL, 0) != KERN_SUCCESS) { error = ENOMEM; vm_map_unlock(kernel_map); vm_object_deallocate(ctx->vmobj); ERRMSG(ctx, "%s: vm_map_insert failed\n", __func__); return (error); } vm_map_unlock(kernel_map); ctx->data = ctx->cpos = (void *)kvaddr; ctx->syspagelist = malloc(npages * sizeof(struct vm_page *), M_VPS_SNAPST, M_WAITOK | M_ZERO); vps_snapst_mod_refcnt++; return (0); } VPSFUNC static int vps_ctx_free(struct vps_snapst_ctx *ctx, struct vps *vps) { vm_offset_t kvaddr; vm_page_t m; int npages, maxpages; kvaddr = (vm_offset_t)ctx->data; maxpages = ctx->maxsize >> PAGE_SHIFT; npages = ctx->dsize2 >> PAGE_SHIFT; pmap_qremove(kvaddr, npages); VM_OBJECT_WLOCK(ctx->vmobj); while (npages) { npages--; m = TAILQ_LAST(&ctx->vmobj->memq, pglist); vm_page_lock(m); vm_page_unwire(m, 0); vm_page_free(m); vm_page_unlock(m); } VM_OBJECT_WUNLOCK(ctx->vmobj); (void)vm_map_unwire(kernel_map, kvaddr, kvaddr + (maxpages << PAGE_SHIFT), 0); /* This also destroys the vm object. */ (void)vm_map_remove(kernel_map, kvaddr, kvaddr + (maxpages << PAGE_SHIFT)); free(ctx->syspagelist, M_VPS_SNAPST); vps_snapst_mod_refcnt--; return (0); } /* * If called with M_NOWAIT the return value must be checked ! */ /* * Inline the code that gets executed on every single call. */ /* * We actually have to allocate. */ VPSFUNC static int vps_ctx_extend_hard(struct vps_snapst_ctx *ctx, struct vps *vps, size_t size, int how) { int npages; int pagenum; int i; int flags; vm_offset_t new; vm_page_t m; #if 0 /* XXX Can't sleep here because of vmobject lock :-( */ KASSERT(how == M_WAITOK, ("%s: how != M_WAITOK\n", __func__)); #endif if (how == M_WAITOK) WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "%s: size=%d", __func__, size); npages = round_page(size) >> PAGE_SHIFT; if (how == M_WAITOK) { /* Allocate more here since we can sleep now. */ npages += 10; } flags = VM_ALLOC_NORMAL|VM_ALLOC_WIRED; VM_OBJECT_WLOCK(ctx->vmobj); new = (vm_offset_t)((caddr_t)ctx->data + ctx->dsize2); m = TAILQ_LAST(&ctx->vmobj->memq, pglist); pagenum = (m != NULL) ? m->pindex + 1 : 0; for (i = 0; i < npages; i++) { do { m = vm_page_alloc(ctx->vmobj, pagenum + i, flags); if (m == NULL) { /* Assume that object will not be modified. */ VM_OBJECT_WUNLOCK(ctx->vmobj); vm_waitpfault(); VM_OBJECT_WLOCK(ctx->vmobj); } } while (m == NULL && how == M_WAITOK); if (m == NULL) { ERRMSG(ctx, "%s: ENOMEM (size=%zu)\n", __func__, size); /* XXX undo what we've done so far */ return (ENOMEM); } pmap_qenter(new + (i << PAGE_SHIFT), &m, 1); bzero((void *)(new + (i << PAGE_SHIFT)), PAGE_SIZE); ctx->syspagelist[pagenum + i] = m; } VM_OBJECT_WUNLOCK(ctx->vmobj); ctx->dsize2 += npages << PAGE_SHIFT; DBGS("%s: extended by %d pages, total now %d\n", __func__, npages, pagenum + npages); return (0); } /* * * * * * Snapshot functions. * * * * */ /* * This function does the actual snapshot and prepares the dump. */ VPSFUNC int vps_snapshot(struct vps_dev_ctx *dev_ctx, struct vps *vps, struct vps_arg_snapst *va) { struct vps_snapst_ctx *ctx; struct vps_dumpheader *dumphdr; time_t starttime; int error = 0; #ifdef DIAGNOSTIC vps_snapst_print_errormsgs = debug_snapst; #endif starttime = time_second; sx_assert(&vps->vps_lock, SX_XLOCKED); dev_ctx->snapst = NULL; ctx = malloc(sizeof (*ctx), M_VPS_SNAPST, M_WAITOK | M_ZERO); LIST_INIT(&ctx->errormsgs); dev_ctx->snapst = ctx; if (vps_func->vps_dumpobj_create == NULL) { ERRMSG(ctx, "%s: vps_libdump module not loaded\n", __func__); error = EOPNOTSUPP; goto out; } if (vps->vps_status != VPS_ST_SUSPENDED) { ERRMSG(ctx, "%s: vps is not VPS_ST_SUSPENDED\n", __func__); error = EBUSY; goto out; } dev_ctx->cmd = VPS_IOC_SNAPST; ctx->vps = vps; ctx->pagesread = 0; vps->vps_status = VPS_ST_SNAPSHOOTING; if (vps_ctx_alloc(ctx, vps)) { error = ENOMEM; goto out; } vps_ctx_extend(ctx, vps, 10 << PAGE_SHIFT, M_WAITOK); ctx->dumpobj_list = NULL; dumphdr = (struct vps_dumpheader *)ctx->data; memset(dumphdr, 0, sizeof(*dumphdr)); ctx->cpos = (caddr_t)ctx->data + sizeof(*dumphdr); ctx->dsize = sizeof(*dumphdr); ctx->nuserpages = 0; /* Gets extended dynamically on demand. */ ctx->userpagelistlength = 1024; /* 1024 items */ ctx->userpagelist = malloc(ctx->userpagelistlength * sizeof(void *), M_VPS_SNAPST, M_WAITOK|M_ZERO); ctx->nuserpages = 0; /* Gets extended dynamically on demand. */ ctx->page_ref_size = 1024; /* 1024 items */ ctx->page_ref = malloc(ctx->page_ref_size * sizeof(struct vps_page_ref), M_VPS_SNAPST, M_WAITOK|M_ZERO); SLIST_INIT(&ctx->obj_list); ctx->rootobj = vdo_create(ctx, VPS_DUMPOBJT_ROOT, M_WAITOK); if ((error = vps_snapshot_sysinfo(ctx, vps))) goto out; /* Leaves the vps dump object open */ if ((error = vps_snapshot_vps(ctx, vps))) goto out; if ((error = vps_snapshot_mounts(ctx, vps))) goto out; if ((error = vps_snapshot_arg(ctx, vps))) goto out; if ((error = vps_snapshot_vnet(ctx, vps, vps->vnet))) goto out; if ((error = vps_snapshot_prison(ctx, vps))) goto out; if (vps_func->sem_snapshot_vps && (error = vps_func->sem_snapshot_vps(ctx, vps))) goto out; if (vps_func->shm_snapshot_vps && (error = vps_func->shm_snapshot_vps(ctx, vps))) goto out; if (vps_func->msg_snapshot_vps && (error = vps_func->msg_snapshot_vps(ctx, vps))) goto out; if ((error = vps_snapshot_proc(ctx, vps))) goto out; /* Close the vps object. */ vdo_close(ctx); /* Close the root object. */ vdo_close(ctx); ctx->nsyspages = (((caddr_t)ctx->cpos - (caddr_t)ctx->data) >> PAGE_SHIFT); /* If not exactly a multiple of PAGE_SIZE, add one page. */ if (ctx->nsyspages << PAGE_SHIFT != (caddr_t)ctx->cpos - (caddr_t)ctx->data) ctx->nsyspages += 1; va->datalen = (ctx->nsyspages + ctx->nuserpages) << PAGE_SHIFT; va->database = NULL; dumphdr->byteorder = VPS_MD_DUMPHDR_BYTEORDER; dumphdr->ptrsize = VPS_MD_DUMPHDR_PTRSIZE; dumphdr->pageshift = PAGE_SHIFT; dumphdr->version = VPS_DUMPH_VERSION; dumphdr->magic = VPS_DUMPH_MAGIC; dumphdr->time = time_second; dumphdr->nsyspages = ctx->nsyspages; dumphdr->nuserpages = ctx->nuserpages; /* The userspace utility relies on this value. */ dumphdr->size = va->datalen; if (vdo_checktree(ctx)) { ERRMSG(ctx, "%s: dump tree is invalid !\n", __func__); /* vdo_printtree(ctx); */ return (EINVAL); } vdo_makerelative(ctx); DBGS("%s: dump tree is valid and contains %d elements\n", __func__, ctx->elements); /* This has to be the last write access to syspages dump *grr* !!! */ dumphdr->checksum = 0; dumphdr->checksum = vps_cksum((char *)ctx->data, dumphdr->nsyspages << PAGE_SHIFT); DBGS("%s: snapshot dev_ctx=%p dev_ctx->snapst=%p vps=%p " "syspages=%d cksum=%08llx userpages=%d PREPARED\n", __func__, dev_ctx, dev_ctx->snapst, vps, dumphdr->nsyspages, (long long unsigned int)dumphdr->checksum, dumphdr->nuserpages); /* allocate userspace pages vm object and map it */ if ((ctx->vps_vmobject = vps_pager_ops.pgo_alloc(ctx, ctx->nsyspages + ctx->nuserpages, VM_PROT_READ, 0, curthread->td_proc->p_ucred)) == NULL) { ERRMSG(ctx, "%s: vm_object_allocate: %d\n", __func__, error); /* XXX */ return (error); } ctx->user_map = &curthread->td_proc->p_vmspace->vm_map; vm_map_lock(ctx->user_map); if (vm_map_findspace(ctx->user_map, vm_map_min(ctx->user_map), (ctx->nsyspages + ctx->nuserpages) << PAGE_SHIFT, &ctx->user_map_start) != KERN_SUCCESS) { error = ENOMEM; vm_map_unlock(ctx->user_map); ERRMSG(ctx, "%s: vm_map_findspace: %d\n", __func__, error); /* XXX */ return (error); } error = vm_map_insert(ctx->user_map, ctx->vps_vmobject, 0, ctx->user_map_start, ctx->user_map_start + ((ctx->nsyspages + ctx->nuserpages) << PAGE_SHIFT), VM_PROT_READ, VM_PROT_READ, 0); if (error != KERN_SUCCESS) panic("%s: vm_map_insert: error=%d\n", __func__, error); vm_map_unlock(ctx->user_map); DBGS("%s: snapshot at %zx - %zx\n", __func__, (size_t)ctx->user_map_start, (size_t)(ctx->user_map_start + ((ctx->nsyspages + ctx->nuserpages) << PAGE_SHIFT))); va->database = (void *)ctx->user_map_start; /* * Resources are freed in vps_snapshot_finish(). */ out: if (error) { ERRMSG(ctx, "%s: error=%d\n", __func__, error); } { struct vps_snapst_errmsg *msg; char *buf, *bufpos; size_t buflen; int error2; buflen = 0; LIST_FOREACH(msg, &ctx->errormsgs, list) { printf("%s\n", msg->str); buflen += strlen(msg->str); } buf = bufpos = malloc(buflen + 1, M_TEMP, M_WAITOK); LIST_FOREACH(msg, &ctx->errormsgs, list) { memcpy(bufpos, msg->str, strlen(msg->str)); bufpos += strlen(msg->str); } *(bufpos) = '\0'; bufpos += 1; DBGS("%s: va->msgbase=%p va->msglen=%zu\n", __func__, va->msgbase, va->msglen); if (va->msgbase != NULL && va->msglen > 0) { if (va->msglen < (bufpos - buf)) printf("%s: warning: user-supplied buffer " "too small for error messages\n", __func__); if ((error2 = copyout(buf, va->msgbase, min(va->msglen, (bufpos - buf))))) printf("%s: error messages copyout=%d\n", __func__, error2); } free(buf, M_TEMP); while (!LIST_EMPTY(&ctx->errormsgs)) { msg = LIST_FIRST(&ctx->errormsgs); LIST_REMOVE(msg, list); free(msg, M_TEMP); } } DBGS("%s: total time: %lld seconds\n", __func__, (long long int)(time_second - starttime)); return (error); } /* * Create FILE_PATH dump object for vnode vp at ctx->cpos. */ VPSFUNC static int vps_snapshot_vnodepath(struct vps_snapst_ctx *ctx, struct vps *vps, - struct vnode *vp, int howalloc) + struct vnode *vp, int howalloc) { struct vps_dumpobj *o1; struct vps_dump_filepath *vdfp; /* struct ucred *save_ucred; struct vps *save_vps; struct thread *td; */ char *retbuf, *buf; int error; if (vp == NULL) return (0); - vref(vp); howalloc &= (M_WAITOK|M_NOWAIT); if (howalloc == 0) return (EINVAL); + + vref(vp); buf = malloc(MAXPATHLEN, M_TEMP, howalloc|M_ZERO); if (buf == NULL) { vrele(vp); return (ENOMEM); } #if 0 // not yet ... for devfs lookups td = curthread; save_ucred = td->td_ucred; save_vps = td->td_vps; td->td_ucred = crdup(save_ucred); vps_deref(td->td_ucred->cr_vps, td->td_ucred); td->td_ucred->cr_vps = vps; vps_ref(td->td_ucred->cr_vps, td->td_ucred); td->td_vps = vps; #endif retbuf = "-"; error = vn_fullpath1_failsafe(curthread, vp, vps->_rootvnode, buf, &retbuf, MAXPATHLEN); #if 0 crfree(td->td_ucred); td->td_ucred = save_ucred; td->td_vps = save_vps; #endif if (error != 0) { free(buf, M_TEMP); vrele(vp); ERRMSG(ctx, "%s: vn_fullpath1() failed for vp=%p\n", __func__, vp); return (error); } DBGS("%s: vnode=%p path=[%s] error=%d\n", __func__, vp, retbuf, error); o1 = vdo_create(ctx, VPS_DUMPOBJT_FILE_PATH, howalloc); if (o1 == NULL) { free(buf, M_TEMP); vrele(vp); return (ENOMEM); } if ((vdfp = vdo_space(ctx, sizeof(*vdfp) + roundup(strlen(retbuf) + 1, 8), howalloc)) == NULL) { vdo_discard(ctx, o1); free(buf, M_TEMP); vrele(vp); return (ENOMEM); } vdfp->fp_size = strlen(retbuf); strlcpy(vdfp->fp_path, retbuf, vdfp->fp_size + 1); vdo_close(ctx); free(buf, M_TEMP); vrele(vp); return (0); } +VPSFUNC +static int +vps_snapshot_vnodeinodenum(struct vps_snapst_ctx *ctx, struct vps *vps, + struct vnode *vp, int howalloc) +{ + struct vps_dump_fileinodenum *vdfi; + struct vps_dumpobj *o1; + struct vattr vattr; + int error; + + if (vp == NULL) + return (0); + + howalloc &= (M_WAITOK|M_NOWAIT); + if (howalloc == 0) + return (EINVAL); + + vref(vp); + vn_lock(vp, LK_SHARED | LK_RETRY); + error = VOP_GETATTR(vp, &vattr, curthread->td_ucred); + VOP_UNLOCK(vp, 0); + + if (error != 0) { + DBGS("%s: vnode=%p VOP_GETATTR(): error=%d\n", + __func__, vp, error); + vrele(vp); + return (error); + } + + DBGS("%s: vnode=%p fsid=%u fileid=%ld error=%d\n", + __func__, vp, vattr.va_fsid, vattr.va_fileid, error); + + o1 = vdo_create(ctx, VPS_DUMPOBJT_FILE_INODENUM, howalloc); + if (o1 == NULL) { + vrele(vp); + return (ENOMEM); + } + if ((vdfi = vdo_space(ctx, sizeof(*vdfi), howalloc)) == NULL) { + vdo_discard(ctx, o1); + vrele(vp); + return (ENOMEM); + } + + vdfi->fsid = vattr.va_fsid; + vdfi->fileid = vattr.va_fileid; + + vdo_close(ctx); + vrele(vp); + + return (0); +} + /* + * EXPERIMENTAL: + * Depending of kind of filesystem choose whether we snapshot + * a path to the vnode (unreliable and sometimes slow) or + * the inode number (aka file id). + */ +VPSFUNC +static int +vps_snapshot_vnode(struct vps_snapst_ctx *ctx, struct vps *vps, + struct vnode *vp, int howalloc) +{ + int error; + + if (vp == NULL) + return (0); + + vref(vp); + + /* + nfs doesn't support vfs_vget() + if (!strcmp(vp->v_tag, "newnfs")) { + */ + if (0) { + error = vps_snapshot_vnodeinodenum(ctx, vps, vp, howalloc); + } else { + error = vps_snapshot_vnodepath(ctx, vps, vp, howalloc); + } + + vrele(vp); + + return (error); +} + +/* * Pseudo teletype device. */ VPSFUNC static int vps_snapshot_pts(struct vps_snapst_ctx *ctx, struct vps *vps, struct tty *tp) { struct vps_dump_pts *vdp; struct vps_dump_filepath *vdf; struct pts_softc *psc; int i; if (tp == NULL) return (0); vdo_create(ctx, VPS_DUMPOBJT_PTS, M_WAITOK); vdp = vdo_space(ctx, sizeof(*vdp), M_WAITOK); psc = tty_softc(tp); vdp->pt_index = psc->pts_unit; if (tp->t_pgrp != NULL) vdp->pt_pgrp_id = tp->t_pgrp->pg_id; else vdp->pt_pgrp_id = 0; vdp->pt_flags = psc->pts_flags; vdp->pt_cred = psc->pts_cred; vdp->pt_termios.c_iflag = tp->t_termios.c_iflag; vdp->pt_termios.c_oflag = tp->t_termios.c_oflag; vdp->pt_termios.c_cflag = tp->t_termios.c_cflag; vdp->pt_termios.c_lflag = tp->t_termios.c_lflag; vdp->pt_termios.c_ispeed = tp->t_termios.c_ispeed; vdp->pt_termios.c_ospeed = tp->t_termios.c_ospeed; KASSERT(NCCS <= (sizeof(vdp->pt_termios.c_cc) / sizeof(vdp->pt_termios.c_cc[0])), ("%s: vdp->pt_termios.c_cc smaller than NCCS\n", __func__)); memset(vdp->pt_termios.c_cc, 0, sizeof(vdp->pt_termios.c_cc)); for (i = 0; i < NCCS; i++) vdp->pt_termios.c_cc[i] = tp->t_termios.c_cc[i]; vps_snapshot_ucred(ctx, vps, psc->pts_cred, M_WAITOK); vdo_create(ctx, VPS_DUMPOBJT_FILE_PATH, M_WAITOK); vdf = vdo_space(ctx, sizeof(*vdf), M_WAITOK); vdf->fp_size = strlen(tty_devname(tp)); vdo_space(ctx, roundup(vdf->fp_size + 1, 8), M_WAITOK); memcpy(vdf->fp_path, tty_devname(tp), vdf->fp_size); DBGS("%s: data=%p path=[%s]\n", __func__, tp, vdf->fp_path); vdo_close(ctx); /* VPS_DUMPOBJT_FILE_PATH */ vdo_close(ctx); /* VPS_DUMPOBJT_PTS */ return (0); } VPSFUNC static int vps_snapshot_sysinfo(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_dump_sysinfo *vds; vdo_create(ctx, VPS_DUMPOBJT_SYSINFO, M_WAITOK); vds = vdo_space(ctx, sizeof(*vds), M_WAITOK); strncpy(vds->kernel, version, MAXHOSTNAMELEN); strncpy(vds->hostname, VPS_VPS(vps, hostname), MAXHOSTNAMELEN); vds->shared_page_obj = shared_page_obj; DBGS("%s: shared_page_obj = %p\n", __func__, shared_page_obj); vdo_close(ctx); return (0); } VPSFUNC static int vps_snapshot_vps(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_dump_vps *vdi; /* Dump vps specific data. */ vdo_create(ctx, VPS_DUMPOBJT_VPS, M_WAITOK); vdi = vdo_space(ctx, sizeof(*vdi), M_WAITOK); strlcpy(vdi->hostname, VPS_VPS(vps, hostname), sizeof(vdi->hostname)); vdi->boottime.tv_sec = VPS_VPS(vps, boottime).tv_sec; vdi->boottime.tv_usec = VPS_VPS(vps, boottime).tv_usec; vdi->lastpid = VPS_VPS(vps, lastpid); vdi->restore_count = vps->restore_count; vdi->initpgrp_id = (VPS_VPS(vps, initpgrp) != NULL) ? VPS_VPS(vps, initpgrp)->pg_id : 0; vdi->initproc_id = (VPS_VPS(vps, initproc) != NULL) ? VPS_VPS(vps, initproc)->p_pid : 0; strlcpy(vdi->vps_name, vps->vps_name, sizeof(vdi->vps_name)); strlcpy(vdi->rootpath, vps->_rootpath, sizeof(vdi->rootpath)); /* Object is closed in calling function. */ return (0); } VPSFUNC static int vps_snapshot_arg(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_dump_arg *vda; struct vps_dump_arg_ip4 *vda4; struct vps_dump_arg_ip6 *vda6; struct vps_dump_accounting *vdacc; size_t privsetsize_round; caddr_t cpos; int error = 0; int i; /* XXX locking */ /* If we ran out of memory previously, we try again. again: */ vdo_create(ctx, VPS_DUMPOBJT_ARG, M_WAITOK); vda = vdo_space(ctx, sizeof(*vda), M_WAITOK); vda->privset_size = PRIV_SET_SIZE; vda->ip4net_cnt = vps->vps_ip4_cnt; vda->ip6net_cnt = vps->vps_ip6_cnt; vda->have_accounting = (vps->vps_acc != NULL) ? 1 : 0; privsetsize_round = roundup(vda->privset_size, 8); cpos = vdo_space(ctx, privsetsize_round, M_WAITOK); memcpy(cpos, vps->priv_allow_set, vda->privset_size); cpos = vdo_space(ctx, privsetsize_round, M_WAITOK); memcpy(cpos, vps->priv_impl_set, vda->privset_size); if (vps->vps_ip4_cnt > 0) { cpos = vdo_space(ctx, vps->vps_ip4_cnt * sizeof(struct vps_dump_arg_ip4), M_WAITOK); for (i = 0; i < vps->vps_ip4_cnt; i++) { vda4 = (struct vps_dump_arg_ip4 *)cpos; memcpy(vda4->a4_addr, &vps->vps_ip4[i].addr, 0x4); memcpy(vda4->a4_mask, &vps->vps_ip4[i].mask, 0x4); cpos = (caddr_t)(vda4 + 1); } } if (vps->vps_ip6_cnt > 0) { cpos = vdo_space(ctx, vps->vps_ip6_cnt * sizeof(struct vps_dump_arg_ip6), M_WAITOK); for (i = 0; i < vps->vps_ip6_cnt; i++) { vda6 = (struct vps_dump_arg_ip6 *)cpos; memcpy(vda6->a6_addr, &vps->vps_ip6[i].addr, 0x10); vda6->a6_plen = vps->vps_ip6[i].plen; cpos = (caddr_t)(vda6 + 1); } } if (vda->have_accounting) { vdacc = vdo_space(ctx, sizeof(*vdacc), M_WAITOK); #define FILL_ACCVAL(x) \ vdacc->x.cur = vps->vps_acc->x.cur; \ vdacc->x.soft = vps->vps_acc->x.soft; \ vdacc->x.hard = vps->vps_acc->x.hard; \ vdacc->x.hits_soft = vps->vps_acc->x.hits_soft; \ vdacc->x.hits_hard = vps->vps_acc->x.hits_hard FILL_ACCVAL(virt); FILL_ACCVAL(phys); FILL_ACCVAL(kmem); FILL_ACCVAL(kernel); FILL_ACCVAL(buffer); FILL_ACCVAL(pctcpu); FILL_ACCVAL(blockio); FILL_ACCVAL(threads); FILL_ACCVAL(procs); #undef FILL_ACCVAL } vdo_close(ctx); return (error); } VPSFUNC static int vps_snapshot_mounts(struct vps_snapst_ctx *ctx, struct vps *vps) { int error = 0; struct vps_dumpobj *o1, *o2; struct vps_dump_mount *vdm; struct vps_dump_mount_opt *vdmopt; struct vfsopt *opt; struct mount *mp; char *mntfrom, *mnton, *fstype, *vpsroot; int len; /* * Find all mountpoints that are in the vps's root directory * (this includes the root directory itself, if a mountpoint). * * Assume that the mountlist is in correct order regarding * dependencies. * * XXX If vps's rootdir is '/', things will be odd. */ DBGS("%s: vps's rootpath=[%s] vnode=%p\n", __func__, vps->_rootpath, vps->_rootvnode); vpsroot = strdup(vps->_rootpath, M_TEMP); if (vpsroot[strlen(vpsroot) - 1] == '/') vpsroot[strlen(vpsroot) - 1] = '\0'; len = strlen(vpsroot); /* If we ran out of memory previously, we try again. */ again: o1 = NULL; mtx_lock(&mountlist_mtx); TAILQ_FOREACH(mp, &mountlist, mnt_list) { mntfrom = mp->mnt_stat.f_mntfromname; mnton = mp->mnt_stat.f_mntonname; fstype = mp->mnt_stat.f_fstypename; DBGS("%s: mp=%p: flag=%zx type=[%s] from=[%s] on=[%s]\n", __func__, mp, (size_t)mp->mnt_flag, fstype, mntfrom, mnton); if (!(strncmp(vpsroot, mnton, len) == 0 && (mnton[len] == '\0' || mnton[len] == '/'))) continue; #if 0 /* Mounts performed by jails are not yet supported. */ if (mp->mnt_cred->cr_prison != VPS_VPS(vps, prison0)) { ERRMSG(ctx, "%s: unsupported mount by jail: " "mp=%p\n", __func__, mp); mtx_unlock(&mountlist_mtx); ctx->cpos = (caddr_t)o1; free(vpsroot, M_TEMP); return (EINVAL); } #endif if ((o2 = vdo_create(ctx, VPS_DUMPOBJT_MOUNT, M_NOWAIT)) == NULL) { mtx_unlock(&mountlist_mtx); if (o1 != NULL) vdo_discard(ctx, o1); goto again; } /* Remember the dump object of the first mount. */ if (o1 == NULL) o1 = o2; vdm = vdo_space(ctx, sizeof(*vdm), M_NOWAIT); if (vdm == NULL) { mtx_unlock(&mountlist_mtx); vdo_discard(ctx, o1); goto again; } strlcpy(vdm->mntfrom, mntfrom, sizeof(vdm->mntfrom)); strlcpy(vdm->mnton, mnton, sizeof(vdm->mnton)); strlcpy(vdm->fstype, fstype, sizeof(vdm->fstype)); vdm->flags = mp->mnt_flag; vdm->optcnt = 0; /* Mounted from inside vps ? */ if (mp->mnt_cred->cr_vps == vps) vdm->vpsmount = 1; else vdm->vpsmount = 0; TAILQ_FOREACH(opt, mp->mnt_opt, link) { vdmopt = vdo_space(ctx, sizeof(*vdmopt), M_NOWAIT); if (vdmopt == NULL) { mtx_unlock(&mountlist_mtx); vdo_discard(ctx, o1); goto again; } if (opt->len >= sizeof(vdmopt->value)) { ERRMSG(ctx, "%s: opt->len=%d (name=[%s]) too big\n", __func__, opt->len, opt->name); mtx_unlock(&mountlist_mtx); free(vpsroot, M_TEMP); return (EINVAL); } strlcpy(vdmopt->name, opt->name, sizeof(vdmopt->name)); memcpy(vdmopt->value, opt->value, opt->len); vdmopt->len = opt->len; vdm->optcnt += 1; DBGS("%s: opt name=[%s] value=%p len=%d\n", __func__, opt->name, opt->value, opt->len); } if (vdm->vpsmount) { vdm->mnt_cred = mp->mnt_cred; if (vps_snapshot_ucred(ctx, vps, mp->mnt_cred, M_NOWAIT)) { mtx_unlock(&mountlist_mtx); vdo_discard(ctx, o1); goto again; } } else { vdm->mnt_cred = NULL; } DBGS("%s: [%s] is in [%s]; vpsmount=%d vdm->mnt_cred=%p\n", __func__, mnton, vpsroot, vdm->vpsmount, vdm->mnt_cred); /* Next. */ vdo_close(ctx); } mtx_unlock(&mountlist_mtx); free(vpsroot, M_TEMP); return (error); } VPSFUNC static int vps_snapshot_vnet_route(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet *vnet) { int error = 0; vps_snapshot_vnet_route_table(ctx, vps, vnet, 0, AF_INET); vps_snapshot_vnet_route_table(ctx, vps, vnet, 0, AF_INET6); return (error); } VPSFUNC static int vps_snapshot_vnet_route_one(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet *vnet, struct radix_node *rn) { struct vps_dumpobj *o1; struct vps_dump_route *vdr; struct vps_dump_vnet_sockaddr *vds; struct rtentry *rt; if ((o1 = vdo_create(ctx, VPS_DUMPOBJT_VNET_ROUTE, M_NOWAIT)) == NULL) return (ENOMEM); if ((vdr = vdo_space(ctx, sizeof(*vdr), M_NOWAIT)) == NULL) { vdo_discard(ctx, o1); return (ENOMEM); } rt = (struct rtentry *)rn; vdr->rt_have_mask = 0; vdr->rt_have_gateway = 0; vdr->rt_have_ifp = 0; vdr->rt_have_ifa = 0; vdr->rt_flags = rt->rt_flags; vdr->rt_fibnum = rt->rt_fibnum; vdr->rt_rmx.rmx_mtu = rt->rt_rmx.rmx_mtu; vdr->rt_rmx.rmx_expire = rt->rt_rmx.rmx_expire; vdr->rt_rmx.rmx_pksent = rt->rt_rmx.rmx_pksent; vdr->rt_rmx.rmx_weight = rt->rt_rmx.rmx_weight; if (rt_key(rt) != NULL) { if ((vds = vdo_space(ctx, sizeof(*vds), M_NOWAIT)) == NULL) { vdo_discard(ctx, o1); return (ENOMEM); } vds->sa_len = rt_key(rt)->sa_len; vds->sa_family = rt_key(rt)->sa_family; memcpy(vds->sa_data, rt_key(rt)->sa_data, vds->sa_len); } else { vdo_discard(ctx, o1); return (0); } if (rt_mask(rt) != NULL) { if ((vds = vdo_space(ctx, sizeof(*vds), M_NOWAIT)) == NULL) { vdo_discard(ctx, o1); return (ENOMEM); } vds->sa_len = rt_mask(rt)->sa_len; vds->sa_family = rt_mask(rt)->sa_family; memcpy(vds->sa_data, rt_mask(rt)->sa_data, vds->sa_len); vdr->rt_have_mask = 1; } if (rt->rt_gateway != NULL) { if ((vds = vdo_space(ctx, sizeof(*vds), M_NOWAIT)) == NULL) { vdo_discard(ctx, o1); return (ENOMEM); } vds->sa_len = rt->rt_gateway->sa_len; vds->sa_family = rt->rt_gateway->sa_family; memcpy(vds->sa_data, rt->rt_gateway->sa_data, vds->sa_len); vdr->rt_have_gateway = 1; } if (rt->rt_ifa != NULL ) { //&& rt->rt_ifa->ifa_addr->sa_len > 0) { if ((vds = vdo_space(ctx, sizeof(*vds), M_NOWAIT)) == NULL) { vdo_discard(ctx, o1); return (ENOMEM); } vds->sa_len = rt->rt_ifa->ifa_addr->sa_len; vds->sa_family = rt->rt_ifa->ifa_addr->sa_family; memcpy(vds->sa_data, rt->rt_ifa->ifa_addr->sa_data, vds->sa_len); vdr->rt_have_ifa = 1; } DBGS("%s: rt=%p rt_flags=%p rt_refcnt=%d rt_fibnum=%d\n", __func__, rt, (void*)(intptr_t)rt->rt_flags, rt->rt_refcnt, rt->rt_fibnum); DBGS("%s: rt_have_mask=%d rt_have_gateway=%d rt_have_ifa=%d\n", __func__, vdr->rt_have_mask, vdr->rt_have_gateway, vdr->rt_have_ifa); vdo_close(ctx); return (0); } VPSFUNC static int vps_snapshot_vnet_route_table(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet *vnet, int fibnum, int af) { struct radix_node_head *rnh; struct radix_node **stack; struct radix_node **sp; struct radix_node *rn; struct vps_dumpobj *o1; int error = 0; /* * Because of the small kernel stack we can't just use a recursive * function for dumping the routing tree. */ /* * I don't remember what I did but now I'm wondering why I * didn't use rn_walktree(), but there propably was a problem * involved. */ /* XXX determine required size or extend stack dynamically */ stack = malloc(sizeof(struct radix_node) * 0x100, M_TEMP, M_WAITOK); /* If we couldn't allocate memory we start over from here. */ again: memset(stack, 0, sizeof(struct radix_node) * 0x100); o1 = vdo_create(ctx, VPS_DUMPOBJT_VNET_ROUTETABLE, M_WAITOK); vdo_append(ctx, &fibnum, sizeof(fibnum), M_WAITOK); vdo_append(ctx, &af, sizeof(af), M_WAITOK); CURVNET_SET_QUIET(vnet); rnh = rt_tables_get_rnh(fibnum, af); DBGS("%s: fibnum=%d af=%d rnh=%p\n", __func__, fibnum, af, rnh); RADIX_NODE_HEAD_RLOCK(rnh); sp = &stack[0xff]; *sp = rnh->rnh_treetop; while (sp <= &stack[0xff] && *sp) { if (sp < &stack[0x02]) { /* Stack is full. */ ERRMSG(ctx, "%s: stack is full, skipping remaining " "routing table entries !\n", __func__); /* XXX raise error or better restart with a bigger stack ! */ break; } /* pop */ rn = *sp; *sp = NULL; sp++; if (!(rn->rn_flags & RNF_ACTIVE)) continue; if (rn->rn_bit < 0) { if ((rn->rn_flags & RNF_ROOT) == 0) { /* leaf */ error = vps_snapshot_vnet_route_one(ctx, vps, vnet, rn); if (error == ENOMEM) { vdo_discard(ctx, o1); RADIX_NODE_HEAD_RUNLOCK(rnh); goto again; } else if (error != 0) { goto out; } } if ((rn = rn->rn_dupedkey)) { /* push */ sp--; *sp = rn; } } else { /* tree forks */ /* push */ sp--; *sp = rn->rn_right; /* push */ sp--; *sp = rn->rn_left; } /* DBGS("stack: %p %p %p %p %p %p\n", stack[0xff], stack[0xfe], stack[0xfd], stack[0xfc], stack[0xfb], stack[0xfa]); DBGS("stack position: %d\n", &stack[0xff] - sp); */ } out: RADIX_NODE_HEAD_RUNLOCK(rnh); free(stack, M_TEMP); CURVNET_RESTORE(); if (error) vdo_discard(ctx, o1); else vdo_close(ctx); return (error); } VPSFUNC static int vps_snapshot_vnet_iface_ifaddr(struct vps_snapst_ctx *ctx, struct vps *vps, struct ifaddr *ifa) { struct vps_dump_vnet_ifaddr *vdifaddr; struct vps_dump_vnet_sockaddr *vdsaddr; struct vps_dump_vnet_inet6_lifetime *vdia6lt; struct in6_ifaddr *ifaddr6p; DBGS("%s: ifa=%p: \n", __func__, ifa); vdo_create(ctx, VPS_DUMPOBJT_VNET_ADDR, M_WAITOK); vdifaddr = vdo_space(ctx, sizeof(*vdifaddr), M_WAITOK); memset(vdifaddr, 0, sizeof(*vdifaddr)); if (ifa->ifa_addr && ifa->ifa_addr->sa_len != 0) { vdifaddr->have_addr = 1; vdsaddr = vdo_space(ctx, sizeof(*vdsaddr), M_WAITOK); vdsaddr->sa_len = ifa->ifa_addr->sa_len; vdsaddr->sa_family = ifa->ifa_addr->sa_family; KASSERT(vdsaddr->sa_len <= sizeof(vdsaddr->sa_data), ("%s: sa_len too big\n", __func__)); memcpy(vdsaddr->sa_data, ifa->ifa_addr->sa_data, vdsaddr->sa_len); } if (ifa->ifa_dstaddr && ifa->ifa_dstaddr->sa_len != 0) { vdifaddr->have_dstaddr = 1; vdsaddr = vdo_space(ctx, sizeof(*vdsaddr), M_WAITOK); vdsaddr->sa_len = ifa->ifa_dstaddr->sa_len; vdsaddr->sa_family = ifa->ifa_dstaddr->sa_family; KASSERT(vdsaddr->sa_len <= sizeof(vdsaddr->sa_data), ("%s: sa_len too big\n", __func__)); memcpy(vdsaddr->sa_data, ifa->ifa_dstaddr->sa_data, vdsaddr->sa_len); } if (ifa->ifa_netmask && ifa->ifa_netmask->sa_len != 0) { vdifaddr->have_netmask = 1; vdsaddr = vdo_space(ctx, sizeof(*vdsaddr), M_WAITOK); vdsaddr->sa_len = ifa->ifa_netmask->sa_len; vdsaddr->sa_family = ifa->ifa_netmask->sa_family; KASSERT(vdsaddr->sa_len <= sizeof(vdsaddr->sa_data), ("%s: sa_len too big\n", __func__)); memcpy(vdsaddr->sa_data, ifa->ifa_netmask->sa_data, vdsaddr->sa_len); } if (ifa->ifa_addr->sa_family == AF_INET6) { ifaddr6p = ifatoia6(ifa); vdia6lt = vdo_space(ctx, sizeof(*vdia6lt), M_WAITOK); vdia6lt->ia6t_expire = ifaddr6p->ia6_lifetime.ia6t_expire; vdia6lt->ia6t_preferred = ifaddr6p->ia6_lifetime.ia6t_preferred; vdia6lt->ia6t_vltime = ifaddr6p->ia6_lifetime.ia6t_vltime; vdia6lt->ia6t_pltime = ifaddr6p->ia6_lifetime.ia6t_pltime; } /* XXX - metric, ifa_flags, ... */ DBGS("%s: ifa: have_addr=%d have_dstaddr=%d have_netmask=%d \n", __func__, vdifaddr->have_addr, vdifaddr->have_dstaddr, vdifaddr->have_netmask); vdo_close(ctx); return (0); } VPSFUNC static int vps_snapshot_vnet_iface(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet *vnet) { struct ifnet *ifnetp; struct ifaddr *ifaddrp; struct vps_dump_vnet_ifnet *vdifnet; int last_was_epair = 0; int error = 0; /* XXX locking */ CURVNET_SET_QUIET(vnet); DBGS("%s: curvnet=%p\n", __func__, curvnet); TAILQ_FOREACH(ifnetp, &V_ifnet, if_link) { again: vdo_create(ctx, VPS_DUMPOBJT_VNET_IFACE, M_WAITOK); DBGS("%s: ifnetp=%p: if_xname=[%s]\n", __func__, ifnetp, ifnetp->if_xname); vdifnet = vdo_space(ctx, sizeof(*vdifnet), M_WAITOK); strlcpy(vdifnet->if_dname, ifnetp->if_dname, sizeof(vdifnet->if_dname)); strlcpy(vdifnet->if_xname, ifnetp->if_xname, sizeof(vdifnet->if_xname)); vdifnet->if_dunit = ifnetp->if_dunit; vdifnet->if_flags = ifnetp->if_flags; TAILQ_FOREACH(ifaddrp, &ifnetp->if_addrhead, ifa_link) { error = vps_snapshot_vnet_iface_ifaddr(ctx, vps, ifaddrp); if (error != 0) goto out; } if (last_was_epair == 0 && strcmp(ifnetp->if_dname, "epair") == 0) { /* The outside interface of the epair needs to be dumped too. */ ifnetp = ((struct epair_softc *) (ifnetp->if_softc))->oifp; last_was_epair = 1; vdo_close(ctx); goto again; } else last_was_epair = 0; vdo_close(ctx); } out: CURVNET_RESTORE(); return (error); } VPSFUNC static int vps_snapshot_vnet(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnet *vnet) { struct vps_dump_vnet *vdvnet; struct vps_dumpobj *o1; int error = 0; o1 = vdo_create(ctx, VPS_DUMPOBJT_VNET, M_WAITOK); vdvnet = vdo_space(ctx, sizeof(*vdvnet), M_WAITOK); vdvnet->orig_ptr = vnet; if ((error = vps_snapshot_vnet_iface(ctx, vps, vnet))) goto out; if ((error = vps_snapshot_vnet_route(ctx, vps, vnet))) goto out; out: if (error) vdo_discard(ctx, o1); else vdo_close(ctx); return (error); } VPSFUNC static int vps_snapshot_ucred(struct vps_snapst_ctx *ctx, struct vps *vps, struct ucred *cr, int how) { struct vps_restore_obj *vbo; struct vps_dumpobj *o1; struct vps_dump_ucred *vdcr; int size; int i; DBGS("%s: cr=%p\n", __func__, cr); if (cr == NULL) return (0); SLIST_FOREACH(vbo, &ctx->obj_list, list) if (vbo->type == VPS_DUMPOBJT_UCRED && vbo->orig_ptr == cr) return (0); o1 = vdo_create(ctx, VPS_DUMPOBJT_UCRED, how); if (o1 == NULL) return (ENOMEM); size = sizeof(*vdcr) + sizeof(vdcr->cr_groups[0]) * cr->cr_ngroups; if ((vdcr = vdo_space(ctx, size, how)) == NULL) { vdo_discard(ctx, o1); return (ENOMEM); } vdcr->cr_uid = cr->cr_uid; vdcr->cr_ruid = cr->cr_ruid; vdcr->cr_svuid = cr->cr_svuid; vdcr->cr_rgid = cr->cr_rgid; vdcr->cr_svgid = cr->cr_svgid; vdcr->cr_flags = cr->cr_flags; vdcr->cr_ngroups = cr->cr_ngroups; vdcr->cr_origptr = cr; /* if (cr->cr_prison == VPS_VPS(vps, prison0)) vdcr->cr_prison = NULL; else */ vdcr->cr_prison = cr->cr_prison; KASSERT(cr->cr_prison != VPS_VPS(vps0, prison0), ("%s: ucred=%p cr_prison == VPS_VPS(vps0, prison0)\n", __func__, cr)); vdcr->cr_vps = cr->cr_vps; vdcr->cr_ref = cr->cr_ref; for (i = 0; i < cr->cr_ngroups; i++) vdcr->cr_groups[i] = cr->cr_groups[i]; vdo_close(ctx); /* Insert into list of dumped objects. */ vbo = malloc(sizeof(*vbo), M_TEMP, how); if (vbo == NULL) { vdo_discard(ctx, o1); return (ENOMEM); } vbo->type = VPS_DUMPOBJT_UCRED; vbo->orig_ptr = crhold(cr); vbo->new_ptr = NULL; SLIST_INSERT_HEAD(&ctx->obj_list, vbo, list); DBGS("%s: cr=%p not seen before --> dumped\n", __func__, cr); return (0); } VPSFUNC static int vps_snapshot_prison_one(struct vps_snapst_ctx *ctx, struct vps *vps, struct prison *pr) { struct vps_dump_prison *vdpr; struct vps_dumpobj *o1; struct vnode *rootvp; caddr_t cpos; int error = 0; int i; DBGS("%s: prison=%p pr_id=%d pr_name=[%s]\n", __func__, pr, pr->pr_id, pr->pr_name); mtx_assert(&pr->pr_mtx, MA_OWNED); pr->pr_ref++; prison_unlock(pr); o1 = vdo_create(ctx, VPS_DUMPOBJT_PRISON, M_WAITOK); vdpr = vdo_space(ctx, sizeof(*vdpr), M_WAITOK); prison_lock(pr); pr->pr_ref--; vdpr->pr_origptr = pr; if (pr == VPS_VPS(vps, prison0)) vdpr->pr_parent = NULL; else vdpr->pr_parent = pr->pr_parent; vdpr->pr_id = pr->pr_id; vdpr->pr_securelevel = pr->pr_securelevel; vdpr->pr_enforce_statfs = pr->pr_enforce_statfs; vdpr->pr_childmax = pr->pr_childmax; vdpr->pr_ip4s = pr->pr_ip4s; vdpr->pr_ip6s = pr->pr_ip6s; vdpr->pr_flags = pr->pr_flags; vdpr->pr_allow = pr->pr_allow; strlcpy(vdpr->pr_name, pr->pr_name, sizeof(vdpr->pr_name)); strlcpy(vdpr->pr_path, pr->pr_path, sizeof(vdpr->pr_path)); vdpr->pr_root = NULL; vdo_space(ctx, roundup(vdpr->pr_ip4s * 0x4, 8) + vdpr->pr_ip6s * 0x10, M_WAITOK); cpos = vdpr->pr_ipdata; for (i = 0; i < vdpr->pr_ip4s; i++) { memcpy(cpos, &pr->pr_ip4[i], 0x4); cpos += 0x4; } /* if (((size_t)cpos % 8) != 0) cpos += 4; */ cpos = (caddr_t)roundup((size_t)cpos, 8); for (i = 0; i < vdpr->pr_ip6s; i++) { memcpy(cpos, &pr->pr_ip6[i], 0x10); cpos += 0x10; } /* vps_print_ascii(vdpr->pr_ipdata, roundup(vdpr->pr_ip4s * 0x4, 8) + vdpr->pr_ip6s * 0x10); */ rootvp = pr->pr_root; vref(rootvp); pr->pr_ref++; prison_unlock(pr); - if ((error = vps_snapshot_vnodepath(ctx, vps, rootvp, M_WAITOK))) { + if ((error = vps_snapshot_vnode(ctx, vps, rootvp, M_WAITOK))) { vrele(rootvp); - DBGS("%s: vps_snapshot_vnodepath: %d\n", __func__, error); + DBGS("%s: vps_snapshot_vnode: %d\n", __func__, error); goto out; } vrele(rootvp); if (pr != VPS_VPS(vps, prison0) && pr->pr_flags & PR_VNET) { KASSERT(pr->pr_vnet != vps->vnet, ("%s: prison=%p has PR_VNET but pr->pr_vnet == " "vps->vnet\n", __func__, pr)); if ((error = vps_snapshot_vnet(ctx, vps, pr->pr_vnet))) goto out; } out: prison_lock(pr); pr->pr_ref--; //out_locked: if (error) vdo_discard(ctx, o1); else vdo_close(ctx); return (error); } VPSFUNC static int vps_snapshot_prison(struct vps_snapst_ctx *ctx, struct vps *vps) { struct prison *ppr, *cpr; int level, descend; int error = 0; sx_slock(&allprison_lock); ppr = VPS_VPS(vps, prison0); prison_lock(ppr); error = vps_snapshot_prison_one(ctx, vps, ppr); prison_unlock(ppr); FOREACH_PRISON_DESCENDANT_LOCKED_LEVEL(ppr, cpr, descend, level) { DBGS("%s: ppr=%p cpr=%p descend=%d level=%d\n", __func__, ppr, cpr, descend, level); /* * In case of error continue looping in order * to unlock prisons, * but don't dump anything anymore. */ if (error == 0) error = vps_snapshot_prison_one(ctx, vps, cpr); } sx_sunlock(&allprison_lock); return (error); } VPSFUNC static int vps_snapshot_proc(struct vps_snapst_ctx *ctx, struct vps *vps) { struct vps_restore_obj *vbo; struct vps_dumpobj *o1; struct vps_dump_pgrp *vdpg; struct vps_dump_session *vdsess; struct session *sess; struct pgrp *pg; struct proc *p, *p2; int found; int i; int error = 0; sx_slock(&VPS_VPS(vps, proctree_lock)); /* Dump all process groups (pgrp), and all sessions. */ for (i = 0; i <= VPS_VPS(vps, pgrphash); i++) LIST_FOREACH(pg, &VPS_VPS(vps, pgrphashtbl)[i], pg_hash) { DBGS("%s: pgrp=%p pg_id=%d pg_session=%p\n", __func__, pg, pg->pg_id, pg->pg_session); o1 = vdo_create(ctx, VPS_DUMPOBJT_PGRP, M_WAITOK); vdpg = vdo_space(ctx, sizeof(*vdpg), M_WAITOK); vdpg->pg_id = pg->pg_id; vdpg->pg_jobc = pg->pg_jobc; vdpg->pg_session_id = pg->pg_session->s_sid; /* XXX necessary ? SLIST_INSERT_HEAD(ctx->dumpobj_list, o1, list); */ found = 0; SLIST_FOREACH(vbo, &ctx->obj_list, list) if (vbo->type == VPS_DUMPOBJT_SESSION && vbo->orig_ptr == pg->pg_session) found = 1; if (found == 1) { vdo_close(ctx); /* pgrp */ continue; } /* Dump session. */ vdo_create(ctx, VPS_DUMPOBJT_SESSION, M_WAITOK); sess = pg->pg_session; vdsess = vdo_space(ctx, sizeof(*vdsess), M_WAITOK); vdsess->s_sid = sess->s_sid; vdsess->s_count = sess->s_count; vdsess->s_leader_id = (sess->s_leader != NULL) ? sess->s_leader->p_pid : 0; vdsess->s_have_ttyvp = (sess->s_ttyvp != NULL) ? 1 : 0; KASSERT(sizeof(sess->s_login) <= sizeof(vdsess->s_login), ("%s: sess->s_login too big\n", __func__)); memcpy(vdsess->s_login, sess->s_login, sizeof(sess->s_login)); if (sess->s_ttyvp) - vps_snapshot_vnodepath(ctx, vps, + vps_snapshot_vnode(ctx, vps, sess->s_ttyvp, M_WAITOK); vdo_close(ctx); /* Insert into list of dumped objects. */ vbo = malloc(sizeof(*vbo), M_TEMP, M_WAITOK); vbo->type = VPS_DUMPOBJT_SESSION; vbo->orig_ptr = pg->pg_session; vbo->new_ptr = NULL; SLIST_INSERT_HEAD(&ctx->obj_list, vbo, list); DBGS("%s: dump session %p/%d: s_ttyvp=%p s_ttyp=%p" "\n", __func__, pg->pg_session, sess->s_sid, sess->s_ttyvp, sess->s_ttyp); vdo_close(ctx); /* pgrp */ } sx_sunlock(&VPS_VPS(vps, proctree_lock)); sx_slock(&VPS_VPS(vps, allproc_lock)); LIST_FOREACH_SAFE(p, &VPS_VPS(vps, allproc), p_list, p2) { if ((error = vps_snapshot_proc_one(ctx, vps, p))) { ERRMSG(ctx, "%s: vps_snapshot_proc_one(p=%p) " "returned error\n", __func__, p); goto out; } } out: sx_sunlock(&VPS_VPS(vps, allproc_lock)); return (error); } VPSFUNC static int vps_snapshot_sysentvec(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct vps_dump_sysentvec *vds; struct sysentvec *sv; long svtype; sv = p->p_sysent; if (vps_md_snapshot_sysentvec(sv, &svtype) != 0) { ERRMSG(ctx, "%s: proc=%p/%u unknown sysentvec %p\n", __func__, p, p->p_pid, sv); return (EINVAL); } vdo_create(ctx, VPS_DUMPOBJT_SYSENTVEC, M_WAITOK); vds = vdo_space(ctx, sizeof(*vds), M_WAITOK); vds->sv_type = svtype; vdo_close(ctx); return (0); } VPSFUNC static int vps_snapshot_pipe(struct vps_snapst_ctx *ctx, struct vps *vps, struct pipe *pi) { struct vps_restore_obj *vbo; struct vps_dump_pipe *vdp; struct pipepair *pp; int error = 0; char f_dump; vdo_create(ctx, VPS_DUMPOBJT_PIPE, M_WAITOK); pp = pi->pipe_pair; DBGS("%s: pipe=%p pipepair=%p\n", __func__, pi, pp); /* Check in list of dumped objects, if already dumped. */ f_dump = 1; SLIST_FOREACH(vbo, &ctx->obj_list, list) if (vbo->type == VPS_DUMPOBJT_PIPE && vbo->spare[0] == pp) f_dump = 0; #if 0 //delete /* Save the pointers. */ vdp.have_dumped_pipe = f_dump; vdp.thisend = pi; vdp.pair = pp; vdp.rpipe = &pp->pp_rpipe; vdp.wpipe = &pp->pp_wpipe; vdo_append(ctx, &vdp, sizeof(vdp), M_WAITOK); #endif vdp = vdo_space(ctx, sizeof(*vdp), M_WAITOK); vdp->pi_have_dumped_pipe = f_dump; vdp->pi_localend = pi; vdp->pi_pair = pp; vdp->pi_rpipe = &pp->pp_rpipe; vdp->pi_wpipe = &pp->pp_wpipe; if (f_dump) { DBGS("%s: dumping ... \n", __func__); /* not used by restore function vdo_append(ctx, pp, sizeof(*pp), M_WAITOK); */ /* XXX dump buffered data if any */ if (pp->pp_rpipe.pipe_buffer.cnt > 0) DBGS("%s: has data: rpipe->pipe_buffer.cnt=%u\n", __func__, pp->pp_rpipe.pipe_buffer.cnt); if (pp->pp_rpipe.pipe_map.cnt > 0) DBGS("%s: has data: rpipe->pipe_map.cnt=%zu\n", __func__, pp->pp_rpipe.pipe_map.cnt); if (pp->pp_wpipe.pipe_buffer.cnt > 0) DBGS("%s: has data: wpipe->pipe_buffer.cnt=%u\n", __func__, pp->pp_wpipe.pipe_buffer.cnt); if (pp->pp_wpipe.pipe_map.cnt > 0) DBGS("%s: has data: wpipe->pipe_map.cnt=%zu\n", __func__, pp->pp_wpipe.pipe_map.cnt); /* Insert into list of dumped objects. */ vbo = malloc(sizeof(*vbo), M_TEMP, M_WAITOK); vbo->type = VPS_DUMPOBJT_PIPE; vbo->orig_ptr = pi; vbo->new_ptr = NULL; vbo->spare[0] = pp; SLIST_INSERT_HEAD(&ctx->obj_list, vbo, list); } vdo_close(ctx); return (error); } VPSFUNC static int vps_snapshot_mbufchain(struct vps_snapst_ctx *ctx, struct vps *vps, struct mbuf *m) { struct vps_dump_mbufchain *vdmc; struct vps_dump_mbuf *vdmb; struct vps_dumpobj *o1; struct mbuf *m2; int error = 0; int i; if ((o1 = vdo_create(ctx, VPS_DUMPOBJT_MBUFCHAIN, M_NOWAIT)) == NULL) return (ENOMEM); if ((vdmc = vdo_space(ctx, sizeof(*vdmc), M_NOWAIT)) == NULL) { vdo_discard(ctx, o1); return (ENOMEM); } vdmc->mc_mbcount = 0; m2 = m; /* Count the chain length. */ while (m2) { m2 = m2->m_next; vdmc->mc_mbcount++; } /* Actually dump a chain. */ m2 = m; for (i = 0; i < vdmc->mc_mbcount; i++) { if ((vdmb = vdo_space(ctx, sizeof(*vdmb), M_NOWAIT)) == NULL) { vdo_discard(ctx, o1); return (ENOMEM); } vdmb->mb_orig_ptr = m2; vdmb->mb_type = m2->m_type; vdmb->mb_len = m2->m_len; vdmb->mb_flags = m2->m_flags; vdmb->mb_have_dat = 0; vdmb->mb_have_ext = 0; vdmb->mb_have_data = 0; vdmb->mb_payload_size = 0; DBGS("%s: m=%p type=%d flags=%d len=%d next=%p " "nextpkt=%p\n", __func__, m2, m2->m_type, m2->m_flags, m2->m_len, m2->m_next, m2->m_nextpkt); /* Force EXT_PACKET --> EXT_CLUSTER */ if (m2->m_flags & M_EXT && m2->m_ext.ext_type == EXT_PACKET) //dm2->m_ext.ext_type = EXT_CLUSTER; ; if ((m2->m_flags & M_EXT) == 0) { //vps_print_ascii(m2->m_dat, MLEN); vdmb->mb_have_dat = 1; vdmb->mb_payload_size = MLEN; if (vdo_append(ctx, m2->m_dat, roundup(MLEN, 8), M_NOWAIT)) { vdo_discard(ctx, o1); return (ENOMEM); } if (m2->m_data != NULL) { vdmb->mb_have_data = 1; vdmb->mb_data_off = m2->m_data - m2->m_dat; } } else if (m2->m_flags & M_EXT) { DBGS("%s: M_EXT ext_type=%d ext_size=%u\n", __func__, m2->m_ext.ext_type, m2->m_ext.ext_size); if (m2->m_ext.ext_type != EXT_CLUSTER && m2->m_ext.ext_type != EXT_JUMBOP && m2->m_ext.ext_type != EXT_JUMBO9 && m2->m_ext.ext_type != EXT_JUMBO16 && m2->m_ext.ext_type != EXT_PACKET) { ERRMSG(ctx, "%s: DON'T KNOW HOW TO HANDLE " "MBUF M_EXT TYPE!\n", __func__); return (EINVAL); } /* checksum */ { u_int32_t sum = 0, i; for (i = 0; i < m2->m_ext.ext_size; i++) sum += (u_char)m2->m_ext.ext_buf[i]; DBGS("%s: checksum=%08x\n", __func__, sum); vdmb->mb_checksum = sum; } if (vdo_append(ctx, m2->m_ext.ext_buf, roundup(m2->m_ext.ext_size, 8), M_NOWAIT)) { vdo_discard(ctx, o1); return (ENOMEM); } vdmb->mb_have_ext = 1; vdmb->mb_payload_size = m2->m_ext.ext_size; if (m2->m_data != NULL) { vdmb->mb_have_data = 1; vdmb->mb_data_off = m2->m_data - m2->m_ext.ext_buf; } /* vps_print_ascii(m2->m_ext.ext_buf, m2->m_ext.ext_size); */ } else { ERRMSG(ctx, "%s: DON'T KNOW HOW TO HANDLE MBUF!\n", __func__); return (EINVAL); } if (m2->m_flags & M_PKTHDR) { DBGS("%s: m2=%p header=%p rcvif=%p \n", __func__, m2, m2->m_pkthdr.header, m2->m_pkthdr.rcvif); } DBGS("%s: vdmb: mb_have_dat=%d mb_have_ext=%d " "mb_payload_size=%u\n", __func__, vdmb->mb_have_dat, vdmb->mb_have_ext, vdmb->mb_payload_size); m2 = m2->m_next; } vdo_close(ctx); DBGS("%s: %d mbufs dumped\n", __func__, vdmc->mc_mbcount); return (error); } VPSFUNC static int vps_snapshot_sockbuf(struct vps_snapst_ctx *ctx, struct vps *vps, struct sockbuf *sb) { struct vps_dumpobj *o1; struct vps_dump_sockbuf *vdsb; int error = 0; if (sb->sb_upcall != NULL) { ERRMSG(ctx, "%s: sb->sb_upcall != NULL\n", __func__); return (EINVAL); } if ((o1 = vdo_create(ctx, VPS_DUMPOBJT_SOCKBUF, M_NOWAIT)) == NULL) return (ENOMEM); if ((vdsb = vdo_space(ctx, sizeof(*vdsb), M_NOWAIT)) == NULL) { vdo_discard(ctx, o1); return (ENOMEM); } vdsb->sb_mb = sb->sb_mb; vdsb->sb_mbtail = sb->sb_mbtail; vdsb->sb_lastrecord = sb->sb_lastrecord; vdsb->sb_sndptr = sb->sb_sndptr; vdsb->sb_state = sb->sb_state; vdsb->sb_flags = sb->sb_flags; vdsb->sb_sndptroff = sb->sb_sndptroff; vdsb->sb_cc = sb->sb_cc; vdsb->sb_hiwat = sb->sb_hiwat; vdsb->sb_mbcnt = sb->sb_mbcnt; vdsb->sb_mcnt = sb->sb_mcnt; vdsb->sb_ccnt = sb->sb_ccnt; vdsb->sb_mbmax = sb->sb_mbmax; vdsb->sb_ctl = sb->sb_ctl; vdsb->sb_lowat = sb->sb_lowat; vdsb->sb_timeo = sb->sb_timeo; if (sb->sb_mb) { if ((error = vps_snapshot_mbufchain(ctx, vps, sb->sb_mb))) { vdo_discard(ctx, o1); return (error); } } DBGS("%s: sb=%p sb_mb=%p sb_mbtail=%p sb_lastrecord=%p " "sb_sndptr=%p\n", __func__, sb, sb->sb_mb, sb->sb_mbtail, sb->sb_lastrecord, sb->sb_sndptr); DBGS("%s: sb=%p sb->sb_cc=%u sb->sb_mb=%p sb->sb_sndptroff=%u\n", __func__, sb, sb->sb_cc, sb->sb_mb, sb->sb_sndptroff); vdo_close(ctx); return (error); } VPSFUNC static int vps_snapshot_socket_unix(struct vps_snapst_ctx *ctx, struct vps *vps, struct socket *so) { struct vps_dump_vnet_sockaddr *vdsaddr; struct vps_dump_unixpcb *vdunpcb; struct unpcb *un_pcb; int error; int i; error = 0; un_pcb = (struct unpcb *)so->so_pcb; if ((vdunpcb = vdo_space(ctx, sizeof(*vdunpcb), M_NOWAIT)) == NULL) { error = ENOMEM; goto drop; } vdunpcb->unp_have_conn = 0; vdunpcb->unp_have_addr = 0; vdunpcb->unp_have_vnode = 0; vdunpcb->unp_conn_socket = NULL; vdunpcb->unp_socket = un_pcb->unp_socket; vdunpcb->unp_flags = un_pcb->unp_flags; vdunpcb->unp_cc = un_pcb->unp_cc; vdunpcb->unp_mbcnt = un_pcb->unp_mbcnt; vdunpcb->unp_peercred.cr_uid = un_pcb->unp_peercred.cr_uid; vdunpcb->unp_peercred.cr_ngroups = min(un_pcb->unp_peercred.cr_ngroups, 16); for (i = 0; i < vdunpcb->unp_peercred.cr_ngroups; i++) vdunpcb->unp_peercred.cr_groups[i] = un_pcb->unp_peercred.cr_groups[i]; if (un_pcb->unp_conn != NULL) { vdunpcb->unp_have_conn = 1; vdunpcb->unp_conn_socket = un_pcb->unp_conn->unp_socket; } if (un_pcb->unp_addr != NULL) { vdunpcb->unp_have_addr = 1; if ((vdsaddr = vdo_space(ctx, sizeof(*vdsaddr), M_NOWAIT)) == NULL) { error = ENOMEM; goto drop; } vdsaddr->sa_len = un_pcb->unp_addr->sun_len; vdsaddr->sa_family = un_pcb->unp_addr->sun_family; memcpy(vdsaddr->sa_data, un_pcb->unp_addr->sun_path, vdsaddr->sa_len); } if (un_pcb->unp_vnode != NULL) { vdunpcb->unp_have_vnode = 1; /* - if ((error = vps_snapshot_vnodepath(ctx, vps, + if ((error = vps_snapshot_vnode(ctx, vps, un_pcb->unp_vnode, M_NOWAIT))) goto drop; */ } drop: return (error); } VPSFUNC static int vps_snapshot_socket_inet(struct vps_snapst_ctx *ctx, struct vps *vps, struct socket *so) { struct vps_dump_inetpcb *vdinpcb; struct vps_dump_udppcb *vdudpcb; struct vps_dump_tcppcb *vdtcpcb; struct inpcb *inpcb; struct udpcb *udp_pcb; struct tcpcb *tcp_pcb; int error; error = 0; inpcb = (struct inpcb *)so->so_pcb; if ((vdinpcb = vdo_space(ctx, sizeof(*vdinpcb), M_NOWAIT)) == NULL) { error = ENOMEM; goto drop; } vdinpcb->inp_vflag = inpcb->inp_vflag; vdinpcb->inp_flags = inpcb->inp_flags; vdinpcb->inp_flags2 = inpcb->inp_flags2; vdinpcb->inp_ip_p = inpcb->inp_ip_p; vdinpcb->inp_have_ppcb = 0; vdinpcb->inp_inc.inc_flags = inpcb->inp_inc.inc_flags; vdinpcb->inp_inc.inc_len = inpcb->inp_inc.inc_len; vdinpcb->inp_inc.inc_fibnum = inpcb->inp_inc.inc_fibnum; vdinpcb->inp_inc.ie_fport = inpcb->inp_inc.inc_ie.ie_fport; vdinpcb->inp_inc.ie_lport = inpcb->inp_inc.inc_ie.ie_lport; if (vdinpcb->inp_vflag & INP_IPV6) { memcpy(vdinpcb->inp_inc.ie_ufaddr, &inpcb->inp_inc.inc6_faddr, 0x10); memcpy(vdinpcb->inp_inc.ie_uladdr, &inpcb->inp_inc.inc6_laddr, 0x10); } else { memcpy(vdinpcb->inp_inc.ie_ufaddr, &inpcb->inp_inc.inc_faddr, 0x4); memcpy(vdinpcb->inp_inc.ie_uladdr, &inpcb->inp_inc.inc_laddr, 0x4); } if (inpcb->inp_ppcb == NULL) return (0); vdinpcb->inp_have_ppcb = 1; INP_INFO_RLOCK(inpcb->inp_pcbinfo); INP_RLOCK(inpcb); /* inpcb->inp_ip_p seems to be 0 and only used for raw ip and divert sockets! */ switch (so->so_proto->pr_protocol) { case IPPROTO_TCP: DBGS("%s: IPPROTO_TCP\n", __func__); tcp_pcb = (struct tcpcb *)inpcb->inp_ppcb; if ((vdtcpcb = vdo_space(ctx, sizeof(*vdtcpcb), M_NOWAIT)) == NULL) { INP_RUNLOCK(inpcb); INP_INFO_RUNLOCK(inpcb->inp_pcbinfo); error = ENOMEM; goto drop; } vdtcpcb->t_state = tcp_pcb->t_state; vdtcpcb->t_flags = tcp_pcb->t_flags; vdtcpcb->snd_una = tcp_pcb->snd_una; vdtcpcb->snd_max = tcp_pcb->snd_max; vdtcpcb->snd_nxt = tcp_pcb->snd_nxt; vdtcpcb->snd_up = tcp_pcb->snd_up; vdtcpcb->snd_wl1 = tcp_pcb->snd_wl1; vdtcpcb->snd_wl2 = tcp_pcb->snd_wl2; vdtcpcb->iss = tcp_pcb->iss; vdtcpcb->irs = tcp_pcb->irs; vdtcpcb->rcv_nxt = tcp_pcb->rcv_nxt; vdtcpcb->rcv_adv = tcp_pcb->rcv_adv; vdtcpcb->rcv_wnd = tcp_pcb->rcv_wnd; vdtcpcb->rcv_up = tcp_pcb->rcv_up; vdtcpcb->snd_wnd = tcp_pcb->snd_wnd; vdtcpcb->snd_cwnd = tcp_pcb->snd_cwnd; vdtcpcb->snd_ssthresh = tcp_pcb->snd_ssthresh; break; case IPPROTO_UDP: DBGS("%s: IPPROTO_UDP\n", __func__); udp_pcb = (struct udpcb *)inpcb->inp_ppcb; if (udp_pcb->u_tun_func != NULL) { ERRMSG(ctx, "%s: udp socket with tunneling " "function set, skipping !\n", __func__); INP_RUNLOCK(inpcb); INP_INFO_RUNLOCK(inpcb->inp_pcbinfo); error = EINVAL; goto drop; } if ((vdudpcb = vdo_space(ctx, sizeof(*vdudpcb), M_NOWAIT)) == NULL) { INP_RUNLOCK(inpcb); INP_INFO_RUNLOCK(inpcb->inp_pcbinfo); error = ENOMEM; goto drop; } vdudpcb->u_have_tun_func = 0; vdudpcb->u_flags = udp_pcb->u_flags; break; case IPPROTO_ICMP: case IPPROTO_RAW: /* Nothing to do. */ break; default: ERRMSG(ctx, "%s: unhandled IPPROTO %d\n", __func__, inpcb->inp_ip_p); error = EINVAL; INP_RUNLOCK(inpcb); INP_INFO_RUNLOCK(inpcb->inp_pcbinfo); goto drop; break; } INP_INFO_RUNLOCK(inpcb->inp_pcbinfo); INP_RUNLOCK(inpcb); drop: return (error); } VPSFUNC static int vps_snapshot_socket(struct vps_snapst_ctx *ctx, struct vps *vps, struct socket *so) { struct vps_restore_obj *vbo; struct vps_dumpobj *o1; struct vps_dump_socket *vds; struct socket *so2; int error = 0; SLIST_FOREACH(vbo, &ctx->obj_list, list) if (vbo->type == VPS_DUMPOBJT_SOCKET && vbo->orig_ptr == so) { DBGS("%s: socket=%p already dumped\n", __func__, so); return (0); } /* If we couldn't allocate memory we try again. */ again: sblock(&so->so_snd, SBL_WAIT | SBL_NOINTR); sblock(&so->so_rcv, SBL_WAIT | SBL_NOINTR); SOCKBUF_LOCK(&so->so_snd); SOCKBUF_LOCK(&so->so_rcv); if ((o1 = vdo_create(ctx, VPS_DUMPOBJT_SOCKET, M_NOWAIT)) == NULL) { error = ENOMEM; goto drop; } if ((vds = vdo_space(ctx, sizeof(*vds), M_NOWAIT)) == NULL) { error = ENOMEM; goto drop; } vds->so_family = so->so_proto->pr_domain->dom_family; vds->so_protocol = so->so_proto->pr_protocol; vds->so_type = so->so_proto->pr_type; vds->so_options = so->so_options; vds->so_state = so->so_state; vds->so_qlimit = so->so_qlimit; vds->so_qstate = so->so_qstate; vds->so_qlen = so->so_qlen; vds->so_incqlen = so->so_incqlen; vds->so_cred = so->so_cred; vds->so_orig_ptr = so; DBGS("%s: socket protocol family=%d protocol=%d type=%d\n", __func__, vds->so_family, vds->so_protocol, vds->so_type); switch (vds->so_family) { case PF_UNIX: error = vps_snapshot_socket_unix(ctx, vps, so); if (error != 0) goto drop; break; case PF_INET: case PF_INET6: error = vps_snapshot_socket_inet(ctx, vps, so); if (error != 0) goto drop; break; default: ERRMSG(ctx, "%s: unhandled protocol family %d\n", __func__, vds->so_family); error = EINVAL; goto drop; break; } if ((error = vps_snapshot_ucred(ctx, vps, so->so_cred, M_NOWAIT))) goto drop; if ((error = vps_snapshot_sockbuf(ctx, vps, &so->so_rcv))) goto drop; if ((error = vps_snapshot_sockbuf(ctx, vps, &so->so_snd))) goto drop; SOCKBUF_UNLOCK(&so->so_rcv); SOCKBUF_UNLOCK(&so->so_snd); sbunlock(&so->so_rcv); sbunlock(&so->so_snd); vdo_close(ctx); /* Sockets that are on the accept queue of this socket. */ if (so->so_qlen > 0 || so->so_incqlen > 0) { DBGS("%s: so=%p so_qlen=%d so_incqlen=%d\n", __func__, so, so->so_qlen, so->so_incqlen); TAILQ_FOREACH(so2, &so->so_comp, so_list) { DBGS("%s: so_comp: so2=%p \n", __func__, so2); if ((error = vps_snapshot_socket(ctx, vps, so2))) goto drop; } TAILQ_FOREACH(so2, &so->so_incomp, so_list) { DBGS("%s: so_incomp: so2=%p \n", __func__, so2); if ((error = vps_snapshot_socket(ctx, vps, so2))) goto drop; } } /* Insert into list of dumped objects. */ vbo = malloc(sizeof(*vbo), M_TEMP, M_WAITOK); vbo->type = VPS_DUMPOBJT_SOCKET; vbo->orig_ptr = so; vbo->new_ptr = NULL; SLIST_INSERT_HEAD(&ctx->obj_list, vbo, list); DBGS("%s: socket=%p not seen before --> dumped\n", __func__, so); return (0); drop: SOCKBUF_UNLOCK(&so->so_rcv); SOCKBUF_UNLOCK(&so->so_snd); sbunlock(&so->so_rcv); sbunlock(&so->so_snd); /* SOCK_UNLOCK(so); */ DBGS("%s: error = %d\n", __func__, error); if (o1 != NULL) vdo_discard(ctx, o1); if (error == ENOMEM) goto again; return (error); } /* XXX */ int kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int waitok); int kqueue_acquire(struct file *fp, struct kqueue **kqp); void kqueue_release(struct kqueue *kq, int locked); VPSFUNC __attribute__((unused)) static int vps_snapshot_kqueue(struct vps_snapst_ctx *ctx, struct vps *vps, struct file *fp) { struct vps_dump_knote *vdkn; struct kevent *kev; struct kqueue *kq; struct knote *kn; int error; int i; error = 0; vdo_create(ctx, VPS_DUMPOBJT_KQUEUE, M_WAITOK); kq = NULL; if ((error = kqueue_acquire(fp, &kq)) != 0) { ERRMSG(ctx, "%s: kqueue_acquire(): error=%d\n", __func__, error); goto out; } for (i = 0; i < kq->kq_knlistsize; i++) { /*DBGS("%s: kqueue=%p i=%d\n", __func__, kq, i);*/ SLIST_FOREACH(kn, &kq->kq_knlist[i], kn_link) { DBGS("%s: knote=%p kn_status=%08x\n", __func__, kn, kn->kn_status); if (kn != NULL && (kn->kn_status & KN_INFLUX) == KN_INFLUX) { DBGS("%s: kn->kn_status & KN_INFLUX\n", __func__); /* XXX have to sleep here */ } kev = &kn->kn_kevent; DBGS("kevent: ident = 0x%016zx\n", (size_t)kev->ident); DBGS("kevent: filter = 0x%04hx\n", kev->filter); DBGS("kevent: flags = 0x%04hx\n", kev->flags); DBGS("kevent: fflags = 0x%08x\n", kev->fflags); DBGS("kevent: data = 0x%016zx\n", (size_t)kev->data); DBGS("kevent: udata = 0x%016lx\n", (long unsigned int)kev->udata); vdo_create(ctx, VPS_DUMPOBJT_KNOTE, M_WAITOK); vdkn = vdo_space(ctx, sizeof(*vdkn), M_WAITOK); vdkn->kn_status = kn->kn_status; vdkn->ke_ident = kev->ident; vdkn->ke_filter = kev->filter; vdkn->ke_flags = kev->flags; vdkn->ke_fflags = kev->fflags; vdkn->ke_data = kev->data; vdkn->ke_udata = kev->udata; vdo_close(ctx); } } out: if (kq != NULL) kqueue_release(kq, 0); vdo_close(ctx); return (error); } VPSFUNC static int vps_snapshot_fdset(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct vps_restore_obj *vbo; struct vps_dump_filedesc *vdfd; struct vps_dump_file *vdf; struct vps_dumpobj *o1; struct filedesc *fdp; struct file *fp; int error = 0; int found; int i; fdp = p->p_fd; SLIST_FOREACH(vbo, &ctx->obj_list, list) if (vbo->type == VPS_DUMPOBJT_FDSET && vbo->orig_ptr == fdp) break; if (vbo != NULL) { DBGS("%s: fdset %p already dumped\n", __func__, fdp); return (0); } DBGS("%s: dumping fdset %p\n", __func__, fdp); FILEDESC_XLOCK(fdp); vdo_create(ctx, VPS_DUMPOBJT_FDSET, M_WAITOK); vdfd = vdo_space(ctx, sizeof(*vdfd), M_WAITOK); vdfd->fd_orig_ptr = fdp; vdfd->fd_nfiles = fdp->fd_nfiles; vdfd->fd_have_cdir = (fdp->fd_cdir != NULL) ? 1 : 0; vdfd->fd_have_rdir = (fdp->fd_rdir != NULL) ? 1 : 0; vdfd->fd_have_jdir = (fdp->fd_jdir != NULL) ? 1 : 0; vdo_space(ctx, sizeof(vdfd->fd_entries[0]) * vdfd->fd_nfiles, M_WAITOK); - if ((error = vps_snapshot_vnodepath(ctx, vps, fdp->fd_cdir, + if ((error = vps_snapshot_vnode(ctx, vps, fdp->fd_cdir, M_WAITOK))) goto out; - if ((error = vps_snapshot_vnodepath(ctx, vps, fdp->fd_rdir, + if ((error = vps_snapshot_vnode(ctx, vps, fdp->fd_rdir, M_WAITOK))) goto out; - if ((error = vps_snapshot_vnodepath(ctx, vps, fdp->fd_jdir, + if ((error = vps_snapshot_vnode(ctx, vps, fdp->fd_jdir, M_WAITOK))) goto out; for (i = 0; i < fdp->fd_nfiles; i++) { fp = fget_locked(fdp, i); vdfd->fd_entries[i].fp = fp; if (fp == NULL) continue; vdfd->fd_entries[i].flags = fdp->fd_ofiles[i].fde_flags; vdfd->fd_entries[i].rights = fdp->fd_ofiles[i].fde_rights; DBGS("%s: idx=%d fp=%p\n", __func__, i, fp); found = 0; SLIST_FOREACH(vbo, &ctx->obj_list, list) if (vbo->type == VPS_DUMPOBJT_FILE && vbo->orig_ptr == fp) found = 1; if (found == 1) { DBGS("%s: fp %p already dumped\n", __func__, fp); continue; } o1 = vdo_create(ctx, VPS_DUMPOBJT_FILE, M_WAITOK); vdf = vdo_space(ctx, sizeof(*vdf), M_WAITOK); vdf->orig_ptr = fp; vdf->flags = fdp->fd_ofiles[i].fde_flags; vdf->f_type = fp->f_type; vdf->f_flag = fp->f_flag; vdf->f_offset = fp->f_offset; vdf->f_cred = fp->f_cred; DBGS("%s: file=%p flags=%08x offset=%d\n", __func__, fp, fp->f_flag, (int)fp->f_offset); vps_snapshot_ucred(ctx, vps, fp->f_cred, M_WAITOK); switch (fp->f_type) { case DTYPE_VNODE: /* XXX e.g. named pipes are not dtype_vnode but do refer to a vnode ? */ if (fp->f_vnode == NULL) break; - if ((error = vps_snapshot_vnodepath(ctx, vps, + if ((error = vps_snapshot_vnode(ctx, vps, fp->f_vnode, M_WAITOK))) { - ERRMSG(ctx, "%s: vps_snapshot_vnodepath(): " + ERRMSG(ctx, "%s: vps_snapshot_vnode(): " "%d\n", __func__, error); goto out; } break; case DTYPE_PTS: if (fp->f_data == NULL) break; if ((error = vps_snapshot_pts(ctx, vps, fp->f_data))) goto out; break; case DTYPE_SOCKET: if (fp->f_data == NULL) break; if ((error = vps_snapshot_socket(ctx, vps, fp->f_data))) goto out; break; case DTYPE_PIPE: if (fp->f_data == NULL) break; if ((error = vps_snapshot_pipe(ctx, vps, fp->f_data))) goto out; break; case DTYPE_KQUEUE: DBGS("%s: KQUEUE fp=%p f_data=%p\n", __func__, fp, fp->f_data); if (fp->f_data == NULL) break; if ((error = vps_snapshot_kqueue(ctx, vps, fp))) goto out; /* kqueue has to be restored last */ o1->prio = -100; break; default: ERRMSG(ctx, "%s: unhandled dtype %d\n", __func__, fp->f_type); error = EINVAL; goto out; break; } vdo_close(ctx); /* Insert into list of dumped objects. */ vbo = malloc(sizeof(*vbo), M_TEMP, M_WAITOK); vbo->type = VPS_DUMPOBJT_FILE; vbo->orig_ptr = fp; vbo->new_ptr = NULL; SLIST_INSERT_HEAD(&ctx->obj_list, vbo, list); } /* Insert into list of dumped objects. */ vbo = malloc(sizeof(*vbo), M_TEMP, M_WAITOK); vbo->type = VPS_DUMPOBJT_FDSET; vbo->orig_ptr = fdp; vbo->new_ptr = NULL; SLIST_INSERT_HEAD(&ctx->obj_list, vbo, list); out: vdo_close(ctx); FILEDESC_XUNLOCK(fdp); return (error); } VPSFUNC static int vps_snapshot_pargs(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct vps_dump_pargs *vdp; struct pargs *pargs; int error = 0; int len2; pargs = p->p_args; if (pargs == NULL) return (0); len2 = roundup(pargs->ar_length, 8); vdo_create(ctx, VPS_DUMPOBJT_PARGS, M_WAITOK); vdp = vdo_space(ctx, sizeof(*vdp) + len2, M_WAITOK); vdp->ar_length = pargs->ar_length; memcpy(vdp->ar_args, pargs->ar_args, pargs->ar_length); vdo_close(ctx); DBGS("%s: [%s]\n", __func__, p->p_args->ar_args); return (error); } VPSFUNC static int vps_snapshot_vmpages(struct vps_snapst_ctx *ctx, struct vps *vps, vm_object_t vmo) { struct vps_dumpobj *o1; struct vps_dump_vmpages *vdvmp; struct vps_dump_vmpageref *vdvmpr; int npages; void *newarr; vm_pindex_t pidx; npages = 0; if ((o1 = vdo_create(ctx, VPS_DUMPOBJT_VMPAGE, M_NOWAIT)) == NULL) { VM_OBJECT_WUNLOCK(vmo); vps_ctx_extend(ctx, NULL, PAGE_SIZE, M_WAITOK); vdo_discard(ctx, o1); return (ENOMEM); } if ((vdvmp = vdo_space(ctx, sizeof(*vdvmp), M_NOWAIT)) == NULL) { VM_OBJECT_WUNLOCK(vmo); vps_ctx_extend(ctx, NULL, PAGE_SIZE, M_WAITOK); vdo_discard(ctx, o1); return (ENOMEM); } for (pidx = 0; pidx < vmo->size; pidx++) { /* * If there is anything it this address, * it is either: * a) a resident page * b) a swapped out page * We do not care if resident pages get swapped out * until vpsctl reads them, because we lookup or swap * them in again by that time. */ if (vm_page_lookup(vmo, pidx) == NULL) { if (vmo->type==OBJT_DEFAULT || vmo->type==OBJT_VNODE || vmo->type==OBJT_PHYS) // not having it continue; else if (vmo->type==OBJT_SWAP && vm_pager_has_page(vmo, pidx, NULL, NULL) == FALSE) // not having it continue; } if (ctx->nuserpages == ctx->page_ref_size) { /* Extend array by one page. */ newarr = malloc(ctx->nuserpages * sizeof(struct vps_page_ref) + PAGE_SIZE, M_VPS_SNAPST, M_NOWAIT | M_ZERO); if (newarr == NULL) { VM_OBJECT_WUNLOCK(vmo); vps_ctx_extend(ctx, NULL, PAGE_SIZE, M_WAITOK); ctx->nuserpages -= npages; vdo_discard(ctx, o1); return (ENOMEM); } memcpy(newarr, ctx->page_ref, ctx->nuserpages * sizeof(struct vps_page_ref)); free(ctx->page_ref, M_VPS_SNAPST); ctx->page_ref = newarr; ctx->page_ref_size = (ctx->nuserpages * sizeof(struct vps_page_ref)+PAGE_SIZE) / sizeof(struct vps_page_ref); } ctx->page_ref[ctx->nuserpages].obj = vmo; ctx->page_ref[ctx->nuserpages].pidx = pidx; ++npages; ++ctx->nuserpages; if ((vdvmpr = vdo_space(ctx, sizeof(*vdvmpr), M_NOWAIT)) == NULL) { VM_OBJECT_WUNLOCK(vmo); vps_ctx_extend(ctx, NULL, PAGE_SIZE, M_WAITOK); vdo_discard(ctx, o1); ctx->nuserpages -= npages; return (ENOMEM); } vdvmpr->pr_vmobject = vmo; vdvmpr->pr_pindex = pidx; } vdvmp->count = npages; DBGS("%s: dumped %d pages\n", __func__, npages); vdo_close(ctx); return (0); } VPSFUNC static int vps_snapshot_vpsfs_getuppervn(struct vps_snapst_ctx *ctx, struct vps *vps, struct vnode **vpp) { struct vnode *lvp, *uvp; struct mount *mp; int error; /* * If using vpsfs, vmobject only has a reference to the lower * vnode. So we must try to get the vpsfs alias vnode in order * to get the correct path. * XXX Also this only works if the corresponding vpsfs mount * is the vps instances' root. */ if (vps_func->vpsfs_tag != NULL && vps_func->vpsfs_nodeget != NULL && vps->_rootvnode->v_tag == vps_func->vpsfs_tag) { lvp = *vpp; mp = vps->_rootvnode->v_mount; /* Exclusive lock is required by vpsfs_nodeget(). */ error = vn_lock(lvp, LK_EXCLUSIVE); if (error != 0) { /* Give up */ DBGS("%s: vn_lock(%p, LK_EXCLUSIVE): %d\n", __func__, lvp, error); *vpp = lvp; vput(lvp); return (error); } if ((error = vps_func->vpsfs_nodeget(mp, lvp, &uvp))) { /* Give up */ DBGS("%s: lvp=%p nothing found\n", __func__, lvp); *vpp = lvp; vput(lvp); return (error); } else { DBGS("%s: lvp=%p --> uvp=%p\n", __func__, lvp, uvp); *vpp = uvp; } vn_lock(lvp, LK_RELEASE); } return (0); } VPSFUNC static int vps_snapshot_vmobject(struct vps_snapst_ctx *ctx, struct vps *vps, struct vm_object *vmo) { struct vps_dump_vmobject *vdvmo; struct vm_object *dumped_backing_obj; struct vps_dumpobj *o1, *save_obj; struct vps_restore_obj *vbo; struct vnode *save_vp; struct vnode *vp; int found; char dump_pages; int error = 0; if (vmo == NULL) { /* Nothing to do. */ DBGS("%s: NULL object\n", __func__); return (0); } /* Look if this object is already dumped (shared memory). */ SLIST_FOREACH(vbo, &ctx->obj_list, list) if (vbo->type == VPS_DUMPOBJT_VMOBJECT && vbo->orig_ptr == vmo) { /* DBGS("%s: object=%p already dumped, skipping.\n", __func__, vmo); */ return (0); } save_obj = ctx->curobj; /* Try to have a good size of pre-allocated space. */ /* XXX */ vps_ctx_extend(ctx, NULL, PAGE_SIZE + sizeof(struct vps_page_ref) * vmo->resident_page_count * 2, M_WAITOK); again1: VM_OBJECT_RLOCK(vmo); dumped_backing_obj = vmo->backing_object; if (vmo->backing_object != NULL) { /* Look if backing object is already dumped. */ found = 0; SLIST_FOREACH(vbo, &ctx->obj_list, list) { if (vbo->type == VPS_DUMPOBJT_VMOBJECT && vbo->orig_ptr == vmo->backing_object) { found = 1; break; } } if (found == 0) { /* Dump backing object first. */ DBGS("%s: obj=%p has backing_object=%p, dumping " "this first.\n", __func__, vmo, vmo->backing_object); VM_OBJECT_RUNLOCK(vmo); if ((error = vps_snapshot_vmobject(ctx, vps, dumped_backing_obj))) return (error); VM_OBJECT_RLOCK(vmo); } } VM_OBJECT_RUNLOCK(vmo); again2: /* if (error == ENOMEM) vps_ctx_extend(ctx, NULL, PAGE_SIZE, M_WAITOK); */ error = 0; VM_OBJECT_WLOCK(vmo); if (vmo->backing_object != dumped_backing_obj) { VM_OBJECT_WUNLOCK(vmo); DBGS("%s: jumping to again1 because backing object " "changed\n", __func__); vdo_discard(ctx, save_obj->next); goto again1; } if ((o1 = vdo_create(ctx, VPS_DUMPOBJT_VMOBJECT, M_NOWAIT)) == NULL) { VM_OBJECT_WUNLOCK(vmo); error = ENOMEM; vps_ctx_extend(ctx, NULL, PAGE_SIZE, M_WAITOK); goto again2; } /* DBGS("%s: obj=%p: size=%d ref_count=%d shadow_count=%d flags=%04x " "type=%02x handle=%p backing_object=%p\n", __func__, o, (int)o->size, o->ref_count,o->shadow_count, o->flags, o->type, o->handle, o->backing_object); */ if ((vdvmo = vdo_space(ctx, sizeof(*vdvmo), M_NOWAIT)) == NULL) { VM_OBJECT_WUNLOCK(vmo); vps_ctx_extend(ctx, NULL, PAGE_SIZE, M_WAITOK); vdo_discard(ctx, o1); error = ENOMEM; goto again2; } vdvmo->orig_ptr = vmo; vdvmo->cred = vmo->cred; vdvmo->backing_object = vmo->backing_object; vdvmo->flags = vmo->flags; vdvmo->type = vmo->type; vdvmo->have_vnode = 0; vdvmo->is_sharedpageobj = 0; vdvmo->size = vmo->size; vdvmo->charge = vmo->charge; vdvmo->backing_object_offset = vmo->backing_object_offset; switch (vmo->type) { case OBJT_DEAD: DBGS("%s: obj=%p OBJT_DEAD\n", __func__, vmo); dump_pages = 0; break; case OBJT_VNODE: vp = vmo->handle; vref(vp); save_vp = vp; VM_OBJECT_WUNLOCK(vmo); #if 1 error = vps_snapshot_vpsfs_getuppervn(ctx, vps, &vp); if (error != 0) { vdo_discard(ctx, o1); vrele(vp); goto out; } #endif - error = vps_snapshot_vnodepath(ctx, vps, vp, + error = vps_snapshot_vnode(ctx, vps, vp, M_WAITOK); if (error != 0) { vdo_discard(ctx, o1); vrele(vp); goto out; } VM_OBJECT_WLOCK(vmo); vrele(vp); /* XXX Why ? if (error != 0 && vmo->handle != save_vp) { */ if (vmo->handle != save_vp) { /* Object changed while it was unlocked. */ VM_OBJECT_WUNLOCK(vmo); vdo_discard(ctx, o1); goto out; } dump_pages = (vmo->flags & OBJ_MIGHTBEDIRTY) ? 1 : 0; vdvmo->have_vnode = 1; break; case OBJT_PHYS: DBGS("%s: obj=%p OBJT_PHYS %s\n", __func__, vmo, (vmo == shared_page_obj) ? "shared_page_obj" : ""); if (vmo == shared_page_obj) { dump_pages = 0; vdvmo->is_sharedpageobj = 1; } else { dump_pages = 1; } break; case OBJT_DEFAULT: case OBJT_SWAP: dump_pages = 1; break; /* case OBJT_DEVICE: break; */ default: panic("%s: unsupported object type obj=%p " "type=%d\n", __func__, vmo, vmo->type); break; } if (vmo->cred != NULL) { error = vps_snapshot_ucred(ctx, vps, vmo->cred, M_NOWAIT); if (error == ENOMEM) { VM_OBJECT_WUNLOCK(vmo); vps_ctx_extend(ctx, NULL, PAGE_SIZE, M_WAITOK); vdo_discard(ctx, o1); goto again2; } else if (error != 0) { VM_OBJECT_WUNLOCK(vmo); vdo_discard(ctx, o1); goto out; } } /* Dump pages. */ if (dump_pages) { error = vps_snapshot_vmpages(ctx, vps, vmo); if (error != 0) { VM_OBJECT_WUNLOCK(vmo); vdo_discard(ctx, o1); goto out; } } vm_object_reference_locked(vmo); VM_OBJECT_WUNLOCK(vmo); /* Insert in any case. */ if (1) { /* Insert into list of dumped backing objects. */ vbo = malloc(sizeof(*vbo), M_TEMP, M_WAITOK); vbo->type = VPS_DUMPOBJT_VMOBJECT; vbo->orig_ptr = vmo; vbo->new_ptr = NULL; SLIST_INSERT_HEAD(&ctx->obj_list, vbo, list); } vdo_close(ctx); out: return (error); } VPSFUNC static int vps_snapshot_vmspace(struct vps_snapst_ctx *ctx, struct vps *vps, struct vmspace *vmspace) { struct vps_restore_obj *vbo; struct vps_dump_vmspace *vdvms; struct vps_dump_vmmapentry *vdvme; struct vm_map_entry *e; int i; int error = 0; SLIST_FOREACH(vbo, &ctx->obj_list, list) if (vbo->type == VPS_DUMPOBJT_VMSPACE && vbo->orig_ptr == vmspace) break; if (vbo != NULL) { DBGS("%s: vmspace %p already dumped\n", __func__, vmspace); return (0); } DBGS("%s: vmspace=%p map=%p\n", __func__, vmspace, &vmspace->vm_map); vdo_create(ctx, VPS_DUMPOBJT_VMSPACE, M_WAITOK); vdvms = vdo_space(ctx, sizeof(*vdvms), M_WAITOK); vdvms->vm_orig_ptr = vmspace; vdvms->vm_tsize = vmspace->vm_tsize; vdvms->vm_dsize = vmspace->vm_dsize; vdvms->vm_ssize = vmspace->vm_ssize; vdvms->vm_map.minoffset = vmspace->vm_map.min_offset; vdvms->vm_map.maxoffset = vmspace->vm_map.max_offset; vm_map_lock(&vmspace->vm_map); for (i = 0, e = vmspace->vm_map.header.next; e != &vmspace->vm_map.header; i++, e = e->next) { DBGS("%s: entry=%p: start=%016zx end=%016zx prot=%02x " "max_prot=%02x " "object=%p eflags=%08x (%s)\n", __func__, e, (size_t)e->start, (size_t)e->end, e->protection, e->max_protection, e->object.vm_object, e->eflags, e->eflags & MAP_ENTRY_IS_SUB_MAP ? "submap" : "vm object"); vdo_create(ctx, VPS_DUMPOBJT_VMMAPENTRY, M_WAITOK); vdvme = vdo_space(ctx, sizeof(*vdvme), M_WAITOK); vdvme->map_object = e->object.vm_object; vdvme->offset = e->offset; vdvme->start = e->start; vdvme->end = e->end; vdvme->avail_ssize = e->avail_ssize; vdvme->eflags = e->eflags; vdvme->protection = e->protection; vdvme->max_protection = e->max_protection; vdvme->inheritance = e->inheritance; if (e->cred != NULL) vps_snapshot_ucred(ctx, vps, e->cred, M_WAITOK); vdvme->cred = e->cred; if (e->eflags & MAP_ENTRY_IS_SUB_MAP) { ERRMSG(ctx, "%s: WARNING: skipping submap\n", __func__); } else if ((error = vps_snapshot_vmobject(ctx, vps, e->object.vm_object))) goto out; vdo_close(ctx); } /* Insert into list of dumped objects. */ vbo = malloc(sizeof(*vbo), M_TEMP, M_WAITOK); vbo->type = VPS_DUMPOBJT_VMSPACE; vbo->orig_ptr = vmspace; vbo->new_ptr = NULL; SLIST_INSERT_HEAD(&ctx->obj_list, vbo, list); out: vm_map_unlock(&vmspace->vm_map); vdo_close(ctx); return (error); } VPSFUNC __attribute__((unused)) static int vps_snapshot_umtx(struct vps_snapst_ctx *ctx, struct vps *vps, struct thread *td) { /*struct umtx_q *uq;*/ int error = 0; vdo_create(ctx, VPS_DUMPOBJT_UMTX, M_WAITOK); vps_umtx_snapshot(td); /* DBGS("%s: td->td_umtxq=%p\n", __func__, td->td_umtxq); if (td->td_umtxq == NULL) { error = 0; goto out; } mtx_lock_spin(&umtx_lock); mtx_unlock_spin(&umtx_lock); */ /*out:*/ vdo_close(ctx); return (error); } VPSFUNC static int vps_snapshot_thread_savefpu(struct vps_snapst_ctx *ctx, struct vps *vps, struct thread *td) { return (vps_md_snapshot_thread_savefpu(ctx, vps, td)); } VPSFUNC static int vps_snapshot_thread(struct vps_snapst_ctx *ctx, struct vps *vps, struct thread *td) { struct vps_dumpobj *o1; struct vps_dump_thread *vdtd; int error = 0; int i; /* XXX Has to happen in suspend, because filesystem sync is finished. */ /* Drain pending ktrace records. */ ktruserret(td); /* pre-alloc some space to avoid re-doing ... */ vps_ctx_extend(ctx, NULL, td->td_kstack_pages << PAGE_SHIFT, M_WAITOK); again: o1 = vdo_create(ctx, VPS_DUMPOBJT_THREAD, M_WAITOK); thread_lock(td); /* Make sure the thread is stopped in ast(). <-- XXX */ if ( ! (TD_IS_SUSPENDED(td)) ) { ERRMSG(ctx, "%s: td=%p not suspended !\n", __func__, td); error = EBUSY; thread_unlock(td); vdo_discard(ctx, o1); goto out; } if ((vdtd = vdo_space(ctx, sizeof(*vdtd), M_NOWAIT)) == NULL) { thread_unlock(td); vdo_discard(ctx, o1); goto again; } vdtd->td_tid = td->td_tid; vdtd->td_xsig = td->td_xsig; vdtd->td_dbgflags = td->td_dbgflags; vdtd->td_sigstk.ss_sp = PTRTO64(td->td_sigstk.ss_sp); vdtd->td_sigstk.ss_size = td->td_sigstk.ss_size; vdtd->td_sigstk.ss_flags = td->td_sigstk.ss_flags; for (i = 0; i < _SIG_WORDS; i++) { vdtd->td_sigmask[i] = td->td_sigmask.__bits[i]; vdtd->td_oldsigmask[i] = td->td_oldsigmask.__bits[i]; } vdtd->td_rqindex = td->td_rqindex; vdtd->td_base_pri = td->td_base_pri; vdtd->td_priority = td->td_priority; vdtd->td_pri_class = td->td_pri_class; vdtd->td_user_pri = td->td_user_pri; vdtd->td_base_user_pri = td->td_base_user_pri; vdtd->td_errno = td->td_errno; vdtd->td_retval[0] = td->td_retval[0]; vdtd->td_retval[1] = td->td_retval[1]; vdtd->td_kstack_pages = td->td_kstack_pages; /* The kstack includes the PCB. */ if (vdo_append(ctx, (void *)td->td_kstack, td->td_kstack_pages << PAGE_SHIFT, M_NOWAIT)) { thread_unlock(td); vdo_discard(ctx, o1); goto again; } if (vps_md_snapshot_thread(vdtd, td) != 0) { thread_unlock(td); vdo_discard(ctx, o1); goto again; } if (vps_snapshot_thread_savefpu(ctx, vps, td) != 0) { thread_unlock(td); vdo_discard(ctx, o1); goto again; } thread_unlock(td); vps_md_print_thread(td); #ifdef DDB db_trace_thread(td, 16); #endif /* not yet if ((error = vps_snapshot_umtx(ctx, vps, td))) goto out; */ out: vdo_close(ctx); return (error); } VPSFUNC static int vps_snapshot_proc_one(struct vps_snapst_ctx *ctx, struct vps *vps, struct proc *p) { struct thread *td; struct vps_dump_proc *vdp; int error = 0; int i; DBGS("%s: p=%p pid=%d ctx->cpos=%p delta=%p\n", __func__, p, p->p_pid, ctx->cpos, (void *)((caddr_t)ctx->cpos - (caddr_t)ctx->data)); vdo_create(ctx, VPS_DUMPOBJT_PROC, M_WAITOK); PROC_LOCK(p); KASSERT(p->p_state == PRS_NORMAL, ("%s: p=%p p_state=%d\n", __func__, p, p->p_state)); PROC_UNLOCK(p); //PROC_LOCK(p); /* Dump structures. */ vdp = vdo_space(ctx, sizeof(*vdp), M_WAITOK); memset(vdp, 0, sizeof(*vdp)); /* sigacts */ KASSERT(_SIG_MAXSIG <= (sizeof(vdp->p_sigacts.ps_sigact) / sizeof(vdp->p_sigacts.ps_sigact[0])), ("%s: vdp->p_sigacts.ps_sigact too small\n", __func__)); KASSERT(_SIG_WORDS <= (sizeof(vdp->p_sigacts.ps_sigonstack) / sizeof(vdp->p_sigacts.ps_sigonstack[0])), ("%s: vdp->p_sigacts.ps_sigonstack etc. too small\n", __func__)); vdp->p_sigacts.ps_maxsig = _SIG_MAXSIG; vdp->p_sigacts.ps_sigwords = _SIG_WORDS; vdp->p_sigacts.ps_flag = p->p_sigacts->ps_flag; for (i = 0; i < _SIG_MAXSIG; i++) { vdp->p_sigacts.ps_sigact[i] = PTRTO64(p->p_sigacts->ps_sigact[i]); vdp->p_sigacts.ps_catchmask[i][0] = p->p_sigacts->ps_catchmask[i].__bits[0]; vdp->p_sigacts.ps_catchmask[i][1] = p->p_sigacts->ps_catchmask[i].__bits[1]; vdp->p_sigacts.ps_catchmask[i][2] = p->p_sigacts->ps_catchmask[i].__bits[2]; vdp->p_sigacts.ps_catchmask[i][3] = p->p_sigacts->ps_catchmask[i].__bits[3]; } for (i = 0; i < _SIG_WORDS; i++) { vdp->p_sigacts.ps_sigonstack[i] = p->p_sigacts->ps_sigonstack.__bits[i]; vdp->p_sigacts.ps_sigintr[i] = p->p_sigacts->ps_sigintr.__bits[i]; vdp->p_sigacts.ps_sigreset[i] = p->p_sigacts->ps_sigreset.__bits[i]; vdp->p_sigacts.ps_signodefer[i] = p->p_sigacts->ps_signodefer.__bits[i]; vdp->p_sigacts.ps_siginfo[i] = p->p_sigacts->ps_siginfo.__bits[i]; vdp->p_sigacts.ps_sigignore[i] = p->p_sigacts->ps_sigignore.__bits[i]; vdp->p_sigacts.ps_sigcatch[i] = p->p_sigacts->ps_sigcatch.__bits[i]; vdp->p_sigacts.ps_freebsd4[i] = p->p_sigacts->ps_freebsd4.__bits[i]; vdp->p_sigacts.ps_osigset[i] = p->p_sigacts->ps_osigset.__bits[i]; vdp->p_sigacts.ps_usertramp[i] = p->p_sigacts->ps_usertramp.__bits[i]; } /* plimit */ /* XXX this is COW ... */ KASSERT(RLIM_NLIMITS < (sizeof(vdp->p_limit.pl_rlimit) / sizeof(vdp->p_limit.pl_rlimit[0])), ("%s: vdp->p_limit.pl_rlimit too small\n", __func__)); vdp->p_limit.pl_nlimits = RLIM_NLIMITS; for (i = 0; i < RLIM_NLIMITS; i++) { vdp->p_limit.pl_rlimit[i].rlim_cur = p->p_limit->pl_rlimit[i].rlim_cur; vdp->p_limit.pl_rlimit[i].rlim_max = p->p_limit->pl_rlimit[i].rlim_max; } /* proc */ vdp->p_pptr_id = (p->p_pptr) ? p->p_pptr->p_pid : 0; vdp->p_peers_id = (p->p_peers) ? p->p_peers->p_pid : 0; vdp->p_leader_id = (p->p_leader) ? p->p_leader->p_pid : 0; vdp->p_pgrp_id = (p->p_pgrp) ? p->p_pgrp->pg_id : 0; vdp->p_xthread_id = (p->p_xthread) ? p->p_xthread->td_tid : 0; vdp->p_pid = p->p_pid; vdp->p_swtick = p->p_swtick; vdp->p_cpulimit = p->p_cpulimit; vdp->p_flag = p->p_flag; vdp->p_state = p->p_state; vdp->p_stops = p->p_stops; vdp->p_oppid = p->p_oppid; vdp->p_xstat = p->p_xstat; vdp->p_stype = p->p_stype; vdp->p_step = p->p_step; vdp->p_sigparent = p->p_sigparent; vdp->p_ucred = p->p_ucred; vdp->p_tracecred = p->p_tracecred; vdp->p_traceflag = p->p_traceflag; vdp->p_vmspace = p->p_vmspace; vdp->p_fd = p->p_fd; strlcpy(vdp->p_comm, p->p_comm, min(sizeof(vdp->p_comm), sizeof(p->p_comm))); /* credentials */ vps_snapshot_ucred(ctx, vps, p->p_ucred, M_WAITOK); /* sysentvec */ if ((error = vps_snapshot_sysentvec(ctx, vps, p))) goto out; /* ktrace */ if (p->p_tracevp != NULL) { - if ((error = vps_snapshot_vnodepath(ctx, vps, p->p_tracevp, + if ((error = vps_snapshot_vnode(ctx, vps, p->p_tracevp, M_WAITOK))) goto out; vdp->p_have_tracevp = 1; } /* Executable vnode. */ if (p->p_textvp != NULL) { - if ((error = vps_snapshot_vnodepath(ctx, vps, p->p_textvp, + if ((error = vps_snapshot_vnode(ctx, vps, p->p_textvp, M_WAITOK))) goto out; vdp->p_have_textvp = 1; } /* Dump vmspace. */ if ((error = vps_snapshot_vmspace(ctx, vps, p->p_vmspace))) goto out; /* Dump threads. */ TAILQ_FOREACH(td, &p->p_threads, td_plist) if ((error = vps_snapshot_thread(ctx, vps, td))) goto out; /* Dump file set. */ if ((error = vps_snapshot_fdset(ctx, vps, p))) goto out; /* Dump process argument list. */ if ((error = vps_snapshot_pargs(ctx, vps, p))) goto out; if (vps_func->sem_snapshot_proc && (error = vps_func->sem_snapshot_proc(ctx, vps, p))) goto out; if (vps_func->shm_snapshot_proc && (error = vps_func->shm_snapshot_proc(ctx, vps, p))) goto out; if (vps_func->msg_snapshot_proc && (error = vps_func->msg_snapshot_proc(ctx, vps, p))) goto out; out: //PROC_UNLOCK(p); vdo_close(ctx); return (error); } /* * * * * * * * * * * * * * * * * * */ VPSFUNC int vps_snapshot_finish(struct vps_dev_ctx *dev_ctx, struct vps *vps) { struct vps_snapst_ctx *ctx; ctx = dev_ctx->snapst; DBGS("%s: dev_ctx=%p\n", __func__, dev_ctx); if (ctx == NULL) return (0); if (vps == NULL) vps = ctx->vps; if (vps != NULL && vps->vps_status != VPS_ST_SNAPSHOOTING ) return (EINVAL); ctx->cmd = 0; ctx->vps = NULL; if (ctx->vps_vmobject) { (void)vm_object_reference(ctx->vps_vmobject); /* Frees one reference. */ (void)vm_map_remove(ctx->user_map, ctx->user_map_start, ctx->user_map_start + ((ctx->nsyspages + ctx->nuserpages) << PAGE_SHIFT)); VM_OBJECT_WLOCK(ctx->vps_vmobject); vps_pager_ops.pgo_dealloc(ctx->vps_vmobject); VM_OBJECT_WUNLOCK(ctx->vps_vmobject); ctx->vps_vmobject = NULL; } while ( ! SLIST_EMPTY(&ctx->obj_list)) { struct vps_restore_obj *vbo; vbo = SLIST_FIRST(&ctx->obj_list); SLIST_REMOVE_HEAD(&ctx->obj_list, list); switch (vbo->type) { case VPS_DUMPOBJT_VMOBJECT: vm_object_deallocate(vbo->orig_ptr); break; case VPS_DUMPOBJT_UCRED: crfree(vbo->orig_ptr); break; default: break; } free(vbo, M_TEMP); } /* * Clean up the memory mess. */ if (ctx->userpagelist) free(ctx->userpagelist, M_VPS_SNAPST); if (ctx->page_ref) free(ctx->page_ref, M_VPS_SNAPST); if (ctx->data) vps_ctx_free(ctx, vps); if (vps != NULL) { vps->vps_status = VPS_ST_SUSPENDED; /* XXX unlock vps */ } free(dev_ctx->snapst, M_VPS_SNAPST); dev_ctx->snapst = NULL; dev_ctx->cmd = 0; DBGS("%s: finished snapshot\n", __func__); return (0); } static int vps_snapst_modevent(module_t mod, int type, void *data) { int error; error = 0; switch (type) { case MOD_LOAD: vps_snapst_mod_refcnt = 0; vps_func->vps_snapshot = vps_snapshot; vps_func->vps_snapshot_finish = vps_snapshot_finish; vps_func->vps_ctx_extend_hard = vps_ctx_extend_hard; vps_func->vps_snapshot_ucred = vps_snapshot_ucred; break; case MOD_UNLOAD: if (vps_snapst_mod_refcnt > 0) return (EBUSY); vps_func->vps_snapshot = NULL; vps_func->vps_snapshot_finish = NULL; vps_func->vps_ctx_extend_hard = NULL; vps_func->vps_snapshot_ucred = NULL; break; default: error = EOPNOTSUPP; break; } return (error); } static moduledata_t vps_snapst_mod = { "vps_snapst", vps_snapst_modevent, 0 }; DECLARE_MODULE(vps_snapst, vps_snapst_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); #endif /* VPS */ /* EOF */