Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/checkpoint-restore/criu.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Emelyanov <xemul@virtuozzo.com>2016-02-19 16:36:37 +0300
committerPavel Emelyanov <xemul@virtuozzo.com>2016-02-20 13:40:52 +0300
commit2e13f1f0291c8e1ed9a1493d17722ee9f20bee83 (patch)
tree73a10c76404aaae5cf5dcfdaa73310a47e0e30c5 /test/zdtm/static
parent02b22eba8bb5aa620fd53b82c8e53d881a00903e (diff)
test: Get rid of live directory
Move static and transition into zdtm top. We can't move all the micro tests themselves, as we need to distinguish static from non static (zdtm.py makes additional checks on static ones). Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
Diffstat (limited to 'test/zdtm/static')
-rw-r--r--test/zdtm/static/Makefile409
-rw-r--r--test/zdtm/static/aio00.c36
-rw-r--r--test/zdtm/static/aio00.desc1
-rw-r--r--test/zdtm/static/apparmor.c90
-rwxr-xr-xtest/zdtm/static/apparmor.checkskip4
-rw-r--r--test/zdtm/static/apparmor.desc1
-rw-r--r--test/zdtm/static/apparmor.profile8
-rw-r--r--test/zdtm/static/arm-neon00.c67
-rw-r--r--test/zdtm/static/arm-neon00.desc1
-rw-r--r--test/zdtm/static/bind-mount.c62
-rw-r--r--test/zdtm/static/bind-mount.desc1
-rw-r--r--test/zdtm/static/binfmt_misc.c194
-rw-r--r--test/zdtm/static/binfmt_misc.desc1
-rw-r--r--test/zdtm/static/bridge.c113
-rw-r--r--test/zdtm/static/bridge.desc1
-rw-r--r--test/zdtm/static/busyloop00.c18
-rw-r--r--test/zdtm/static/caps00.c178
-rw-r--r--test/zdtm/static/caps00.desc1
-rw-r--r--test/zdtm/static/cgroup00.c205
-rw-r--r--test/zdtm/static/cgroup00.desc1
-rwxr-xr-xtest/zdtm/static/cgroup00.hook20
-rw-r--r--test/zdtm/static/cgroup00.opts1
-rw-r--r--test/zdtm/static/cgroup01.c115
-rw-r--r--test/zdtm/static/cgroup01.desc1
-rwxr-xr-xtest/zdtm/static/cgroup01.hook21
-rw-r--r--test/zdtm/static/cgroup01.opts1
-rw-r--r--test/zdtm/static/cgroup02.c157
-rw-r--r--test/zdtm/static/cgroup02.desc1
-rwxr-xr-xtest/zdtm/static/cgroup02.hook32
-rw-r--r--test/zdtm/static/cgroup02.opts1
-rw-r--r--test/zdtm/static/cgroup03.c171
-rw-r--r--test/zdtm/static/cgroup03.desc1
-rwxr-xr-xtest/zdtm/static/cgroup03.hook14
-rw-r--r--test/zdtm/static/child_opened_proc.c63
-rw-r--r--test/zdtm/static/chroot-file.c166
-rw-r--r--test/zdtm/static/chroot-file.desc1
-rw-r--r--test/zdtm/static/chroot.c164
-rw-r--r--test/zdtm/static/chroot.desc1
-rw-r--r--test/zdtm/static/clean_mntns.c25
-rw-r--r--test/zdtm/static/clean_mntns.desc1
-rw-r--r--test/zdtm/static/cmdlinenv00.c123
-rw-r--r--test/zdtm/static/cmdlinenv00.desc1
-rwxr-xr-xtest/zdtm/static/conntracks58
-rw-r--r--test/zdtm/static/conntracks.desc1
-rw-r--r--test/zdtm/static/console.c60
-rw-r--r--test/zdtm/static/console.desc1
-rw-r--r--test/zdtm/static/cow00.c113
-rw-r--r--test/zdtm/static/cow00.desc1
-rw-r--r--test/zdtm/static/cow01.c509
-rw-r--r--test/zdtm/static/cow01.desc1
-rw-r--r--test/zdtm/static/criu-rtc.c123
-rw-r--r--test/zdtm/static/criu-rtc.proto3
-rw-r--r--test/zdtm/static/cwd00.c68
-rw-r--r--test/zdtm/static/cwd01.c101
-rw-r--r--test/zdtm/static/cwd02.c92
-rw-r--r--test/zdtm/static/deleted_dev.c73
-rw-r--r--test/zdtm/static/deleted_dev.desc1
-rw-r--r--test/zdtm/static/deleted_unix_sock.c194
-rw-r--r--test/zdtm/static/different_creds.c149
-rw-r--r--test/zdtm/static/different_creds.desc1
-rw-r--r--test/zdtm/static/dumpable01.c48
-rw-r--r--test/zdtm/static/dumpable02.c207
-rw-r--r--test/zdtm/static/dumpable02.desc1
-rw-r--r--test/zdtm/static/env00.c39
-rw-r--r--test/zdtm/static/eventfs00.c98
-rw-r--r--test/zdtm/static/fanotify00.c311
-rw-r--r--test/zdtm/static/fanotify00.desc1
-rw-r--r--test/zdtm/static/fd.c109
-rw-r--r--test/zdtm/static/fd.desc1
-rw-r--r--test/zdtm/static/fdt_shared.c207
-rw-r--r--test/zdtm/static/fifo-ghost.c81
-rw-r--r--test/zdtm/static/fifo-rowo-pair.c159
-rw-r--r--test/zdtm/static/fifo.c86
-rw-r--r--test/zdtm/static/fifo_ro.c94
-rw-r--r--test/zdtm/static/fifo_wronly.c119
-rw-r--r--test/zdtm/static/file_append.c61
-rw-r--r--test/zdtm/static/file_attr.c121
-rw-r--r--test/zdtm/static/file_fown.c183
-rw-r--r--test/zdtm/static/file_fown.desc1
-rw-r--r--test/zdtm/static/file_locks00.c185
-rw-r--r--test/zdtm/static/file_locks00.desc1
-rw-r--r--test/zdtm/static/file_locks00.opts1
-rw-r--r--test/zdtm/static/file_locks01.c188
-rw-r--r--test/zdtm/static/file_locks01.desc1
-rw-r--r--test/zdtm/static/file_locks01.opts1
-rw-r--r--test/zdtm/static/file_locks02.c99
-rw-r--r--test/zdtm/static/file_locks02.desc1
-rw-r--r--test/zdtm/static/file_locks02.opts1
-rw-r--r--test/zdtm/static/file_locks03.c101
-rw-r--r--test/zdtm/static/file_locks03.desc1
-rw-r--r--test/zdtm/static/file_locks03.opts1
-rw-r--r--test/zdtm/static/file_locks04.c119
-rw-r--r--test/zdtm/static/file_locks04.desc1
-rw-r--r--test/zdtm/static/file_locks04.opts1
-rw-r--r--test/zdtm/static/file_locks05.c52
-rw-r--r--test/zdtm/static/file_locks05.desc1
-rw-r--r--test/zdtm/static/file_locks05.opts1
-rw-r--r--test/zdtm/static/file_shared.c117
-rw-r--r--test/zdtm/static/fpu00.c85
-rw-r--r--test/zdtm/static/fpu00.desc1
-rw-r--r--test/zdtm/static/fpu01.c119
-rw-r--r--test/zdtm/static/fpu01.desc1
-rw-r--r--test/zdtm/static/futex-rl.c126
-rw-r--r--test/zdtm/static/futex.c88
-rw-r--r--test/zdtm/static/get_smaps_bits.c127
-rw-r--r--test/zdtm/static/groups.c64
-rw-r--r--test/zdtm/static/groups.desc1
-rw-r--r--test/zdtm/static/grow_map.c66
-rw-r--r--test/zdtm/static/grow_map02.c63
-rw-r--r--test/zdtm/static/grow_map03.c40
-rw-r--r--test/zdtm/static/inotify00.c257
-rw-r--r--test/zdtm/static/inotify00.desc1
-rw-r--r--test/zdtm/static/inotify00.opts1
l---------test/zdtm/static/inotify01.c1
-rw-r--r--test/zdtm/static/inotify01.desc1
-rw-r--r--test/zdtm/static/inotify02.c101
-rw-r--r--test/zdtm/static/inotify02.desc1
-rw-r--r--test/zdtm/static/inotify_irmap.c69
-rw-r--r--test/zdtm/static/inotify_irmap.desc1
-rw-r--r--test/zdtm/static/inotify_system.c391
-rw-r--r--test/zdtm/static/inotify_system.desc1
-rw-r--r--test/zdtm/static/inotify_system_nodel.c391
-rw-r--r--test/zdtm/static/inotify_system_nodel.desc1
-rw-r--r--test/zdtm/static/ipc_namespace.c365
-rw-r--r--test/zdtm/static/ipc_namespace.desc1
-rw-r--r--test/zdtm/static/jobctl00.c301
l---------test/zdtm/static/lib/criu-rtc.so1
-rw-r--r--test/zdtm/static/link10.c79
-rw-r--r--test/zdtm/static/loginuid.c99
-rw-r--r--test/zdtm/static/loginuid.desc1
-rw-r--r--test/zdtm/static/maps00.c268
-rw-r--r--test/zdtm/static/maps01.c171
-rw-r--r--test/zdtm/static/maps01.desc1
-rw-r--r--test/zdtm/static/maps02.c110
-rw-r--r--test/zdtm/static/maps03.c38
-rw-r--r--test/zdtm/static/maps03.desc1
-rw-r--r--test/zdtm/static/maps04.c57
-rw-r--r--test/zdtm/static/maps05.c91
-rw-r--r--test/zdtm/static/maps_file_prot.c53
-rw-r--r--test/zdtm/static/mem-touch.c62
-rw-r--r--test/zdtm/static/mem-touch.desc1
-rw-r--r--test/zdtm/static/mlock_setuid.c55
-rw-r--r--test/zdtm/static/mlock_setuid.desc1
-rw-r--r--test/zdtm/static/mmx00.c99
-rw-r--r--test/zdtm/static/mmx00.desc1
-rw-r--r--test/zdtm/static/mnt_ext_auto.c71
-rw-r--r--test/zdtm/static/mnt_ext_auto.desc1
-rw-r--r--test/zdtm/static/mnt_ext_auto.opts1
-rw-r--r--test/zdtm/static/mnt_ext_master.c78
-rw-r--r--test/zdtm/static/mnt_ext_master.desc1
-rw-r--r--test/zdtm/static/mnt_ext_master.opts1
-rw-r--r--test/zdtm/static/mnt_ro_bind.c84
-rw-r--r--test/zdtm/static/mnt_ro_bind.desc1
-rw-r--r--test/zdtm/static/mntns_deleted.c104
-rw-r--r--test/zdtm/static/mntns_deleted.desc1
l---------test/zdtm/static/mntns_link_ghost.c1
-rw-r--r--test/zdtm/static/mntns_link_ghost.desc1
-rw-r--r--test/zdtm/static/mntns_link_remap.c250
-rw-r--r--test/zdtm/static/mntns_link_remap.desc1
-rw-r--r--test/zdtm/static/mntns_link_remap.opts1
-rw-r--r--test/zdtm/static/mntns_open.c140
-rw-r--r--test/zdtm/static/mntns_open.desc1
-rw-r--r--test/zdtm/static/mntns_root_bind.c123
-rw-r--r--test/zdtm/static/mntns_root_bind.desc1
l---------test/zdtm/static/mntns_root_bind02.c1
l---------test/zdtm/static/mntns_root_bind02.desc1
-rw-r--r--test/zdtm/static/mntns_rw_ro_rw.c46
-rw-r--r--test/zdtm/static/mntns_rw_ro_rw.desc1
-rw-r--r--test/zdtm/static/mntns_shared_bind.c128
-rw-r--r--test/zdtm/static/mntns_shared_bind.desc1
l---------test/zdtm/static/mntns_shared_bind02.c1
-rw-r--r--test/zdtm/static/mntns_shared_bind02.desc1
-rw-r--r--test/zdtm/static/mount_paths.c57
-rw-r--r--test/zdtm/static/mount_paths.desc1
-rw-r--r--test/zdtm/static/mountpoints.c357
-rw-r--r--test/zdtm/static/mountpoints.desc1
-rw-r--r--test/zdtm/static/mprotect00.c116
-rw-r--r--test/zdtm/static/msgque.c138
-rw-r--r--test/zdtm/static/msgque.desc1
-rw-r--r--test/zdtm/static/mtime_mmap.c116
-rw-r--r--test/zdtm/static/netns-dev.c220
-rw-r--r--test/zdtm/static/netns-dev.desc1
-rw-r--r--test/zdtm/static/netns-nf.c48
-rw-r--r--test/zdtm/static/netns-nf.desc1
-rw-r--r--test/zdtm/static/netns.c55
-rw-r--r--test/zdtm/static/netns.desc1
-rw-r--r--test/zdtm/static/oom_score_adj.c94
-rw-r--r--test/zdtm/static/overmount_dev.c92
-rw-r--r--test/zdtm/static/overmount_dev.desc1
-rw-r--r--test/zdtm/static/overmount_fifo.c90
-rw-r--r--test/zdtm/static/overmount_fifo.desc1
-rw-r--r--test/zdtm/static/overmount_file.c73
-rw-r--r--test/zdtm/static/overmount_file.desc1
-rw-r--r--test/zdtm/static/overmount_sock.c207
-rw-r--r--test/zdtm/static/overmount_sock.desc1
-rw-r--r--test/zdtm/static/packet_sock.c301
-rw-r--r--test/zdtm/static/packet_sock.desc1
-rw-r--r--test/zdtm/static/packet_sock_mmap.c103
-rw-r--r--test/zdtm/static/packet_sock_mmap.desc1
-rw-r--r--test/zdtm/static/pdeath_sig.c109
-rw-r--r--test/zdtm/static/pid00.c94
-rw-r--r--test/zdtm/static/pid00.desc1
-rw-r--r--test/zdtm/static/pipe00.c121
-rw-r--r--test/zdtm/static/pipe01.c132
-rw-r--r--test/zdtm/static/pipe02.c61
-rw-r--r--test/zdtm/static/poll.c133
-rw-r--r--test/zdtm/static/poll.desc1
-rw-r--r--test/zdtm/static/posix_timers.c443
-rw-r--r--test/zdtm/static/proc-self.c79
-rw-r--r--test/zdtm/static/pstree.c87
-rw-r--r--test/zdtm/static/pthread00.c185
-rw-r--r--test/zdtm/static/pthread01.c209
-rw-r--r--test/zdtm/static/pthread02.c43
-rw-r--r--test/zdtm/static/pthread02.desc1
-rw-r--r--test/zdtm/static/ptrace_sig.c189
-rw-r--r--test/zdtm/static/ptrace_sig.desc1
-rw-r--r--test/zdtm/static/pty00.c135
-rw-r--r--test/zdtm/static/pty01.c93
-rw-r--r--test/zdtm/static/pty02.c103
-rw-r--r--test/zdtm/static/pty02.desc1
-rw-r--r--test/zdtm/static/pty03.c83
-rw-r--r--test/zdtm/static/pty03.desc1
-rw-r--r--test/zdtm/static/pty04.c64
-rw-r--r--test/zdtm/static/remap_dead_pid.c77
-rw-r--r--test/zdtm/static/remap_dead_pid.desc1
-rw-r--r--test/zdtm/static/remap_dead_pid_root.c72
-rw-r--r--test/zdtm/static/remap_dead_pid_root.desc1
-rw-r--r--test/zdtm/static/rlimits00.c66
-rw-r--r--test/zdtm/static/rmdir_open.c70
-rwxr-xr-xtest/zdtm/static/route_rules74
-rw-r--r--test/zdtm/static/rtc.c61
-rw-r--r--test/zdtm/static/rtc.desc1
-rw-r--r--test/zdtm/static/sched_policy00.c63
-rw-r--r--test/zdtm/static/sched_policy00.desc1
-rw-r--r--test/zdtm/static/sched_prio00.c79
-rw-r--r--test/zdtm/static/sched_prio00.desc1
-rw-r--r--test/zdtm/static/seccomp_filter.c187
-rw-r--r--test/zdtm/static/seccomp_filter.desc1
-rw-r--r--test/zdtm/static/seccomp_filter_inheritance.c176
-rw-r--r--test/zdtm/static/seccomp_filter_inheritance.desc1
-rw-r--r--test/zdtm/static/seccomp_filter_tsync.c202
-rw-r--r--test/zdtm/static/seccomp_filter_tsync.desc1
-rw-r--r--test/zdtm/static/seccomp_strict.c122
-rw-r--r--test/zdtm/static/seccomp_strict.desc1
-rw-r--r--test/zdtm/static/selfexe00.c60
-rw-r--r--test/zdtm/static/sem.c181
-rw-r--r--test/zdtm/static/sem.desc1
-rw-r--r--test/zdtm/static/session00.c237
-rw-r--r--test/zdtm/static/session00.desc1
-rw-r--r--test/zdtm/static/session01.c338
-rw-r--r--test/zdtm/static/session01.desc1
-rw-r--r--test/zdtm/static/session02.c328
-rw-r--r--test/zdtm/static/session02.desc1
-rw-r--r--test/zdtm/static/session03.c377
-rw-r--r--test/zdtm/static/session03.desc1
-rw-r--r--test/zdtm/static/shm.c194
-rw-r--r--test/zdtm/static/shm.desc1
-rw-r--r--test/zdtm/static/sigaltstack.c168
-rw-r--r--test/zdtm/static/signalfd00.c72
-rw-r--r--test/zdtm/static/sigpending.c274
l---------test/zdtm/static/sk-freebind-false.c1
-rw-r--r--test/zdtm/static/sk-freebind.c77
-rw-r--r--test/zdtm/static/sk-netlink.c160
-rw-r--r--test/zdtm/static/sk-netlink.desc1
-rw-r--r--test/zdtm/static/sk-unix-rel.c110
-rw-r--r--test/zdtm/static/sk-unix-unconn.c64
-rw-r--r--test/zdtm/static/sleeping00.c18
-rw-r--r--test/zdtm/static/sock_filter.c115
-rw-r--r--test/zdtm/static/sock_opts00.c91
-rw-r--r--test/zdtm/static/sock_opts00.desc1
-rw-r--r--test/zdtm/static/sock_opts01.c61
-rw-r--r--test/zdtm/static/sock_opts01.desc1
-rw-r--r--test/zdtm/static/socket-closed-tcp.c58
-rw-r--r--test/zdtm/static/socket-closed-tcp.desc1
-rw-r--r--test/zdtm/static/socket-ext.c101
-rw-r--r--test/zdtm/static/socket-ext.desc1
-rw-r--r--test/zdtm/static/socket-ext.opts1
l---------test/zdtm/static/socket-tcp-local.c1
-rw-r--r--test/zdtm/static/socket-tcp-local.desc1
l---------test/zdtm/static/socket-tcp-local.opts1
l---------test/zdtm/static/socket-tcp-nfconntrack.c1
-rw-r--r--test/zdtm/static/socket-tcp-nfconntrack.desc1
-rw-r--r--test/zdtm/static/socket-tcp.c213
-rw-r--r--test/zdtm/static/socket-tcp.desc1
-rw-r--r--test/zdtm/static/socket-tcp.opts1
l---------test/zdtm/static/socket-tcp6-local.c1
l---------test/zdtm/static/socket-tcp6-local.desc1
l---------test/zdtm/static/socket-tcp6-local.opts1
l---------test/zdtm/static/socket-tcp6.c1
-rw-r--r--test/zdtm/static/socket-tcp6.desc1
-rw-r--r--test/zdtm/static/socket-tcp6.opts1
l---------test/zdtm/static/socket-tcpbuf-local.c1
-rw-r--r--test/zdtm/static/socket-tcpbuf-local.desc1
-rw-r--r--test/zdtm/static/socket-tcpbuf-local.opts1
-rw-r--r--test/zdtm/static/socket-tcpbuf.c321
-rw-r--r--test/zdtm/static/socket-tcpbuf.desc1
-rw-r--r--test/zdtm/static/socket-tcpbuf.opts1
l---------test/zdtm/static/socket-tcpbuf6-local.c1
l---------test/zdtm/static/socket-tcpbuf6-local.desc1
l---------test/zdtm/static/socket-tcpbuf6-local.opts1
l---------test/zdtm/static/socket-tcpbuf6.c1
-rw-r--r--test/zdtm/static/socket-tcpbuf6.desc1
-rw-r--r--test/zdtm/static/socket-tcpbuf6.opts1
-rw-r--r--test/zdtm/static/socket6_udp.c124
-rw-r--r--test/zdtm/static/socket_aio.c138
-rw-r--r--test/zdtm/static/socket_aio.desc1
-rw-r--r--test/zdtm/static/socket_close_data.c43
-rw-r--r--test/zdtm/static/socket_close_data01.c115
-rw-r--r--test/zdtm/static/socket_dgram_data.c82
-rw-r--r--test/zdtm/static/socket_listen.c118
l---------test/zdtm/static/socket_listen6.c1
-rw-r--r--test/zdtm/static/socket_queues.c110
-rw-r--r--test/zdtm/static/socket_snd_addr.c101
-rw-r--r--test/zdtm/static/socket_snd_addr.desc1
-rw-r--r--test/zdtm/static/socket_udp.c129
-rw-r--r--test/zdtm/static/socket_udplite.c129
-rw-r--r--test/zdtm/static/sockets00.c163
-rw-r--r--test/zdtm/static/sockets00.desc1
-rw-r--r--test/zdtm/static/sockets01.c150
-rw-r--r--test/zdtm/static/sockets02.c67
-rw-r--r--test/zdtm/static/sockets_dgram.c212
-rw-r--r--test/zdtm/static/sockets_spair.c58
-rw-r--r--test/zdtm/static/sse00.c88
-rw-r--r--test/zdtm/static/sse00.desc1
-rw-r--r--test/zdtm/static/sse20.c88
-rw-r--r--test/zdtm/static/sse20.desc1
-rw-r--r--test/zdtm/static/stopped.c86
l---------test/zdtm/static/stopped01.c1
l---------test/zdtm/static/stopped02.c1
l---------test/zdtm/static/stopped12.c1
-rw-r--r--test/zdtm/static/tempfs.c111
-rw-r--r--test/zdtm/static/tempfs.desc1
-rw-r--r--test/zdtm/static/tempfs_ro.c78
-rw-r--r--test/zdtm/static/tempfs_ro.desc1
-rw-r--r--test/zdtm/static/tempfs_subns.c136
-rw-r--r--test/zdtm/static/tempfs_subns.desc1
-rw-r--r--test/zdtm/static/timerfd.c166
-rw-r--r--test/zdtm/static/timerfd.desc1
-rw-r--r--test/zdtm/static/timers.c93
-rw-r--r--test/zdtm/static/tty00.c100
-rw-r--r--test/zdtm/static/tty00.desc1
-rw-r--r--test/zdtm/static/tty02.c53
-rw-r--r--test/zdtm/static/tty03.c114
-rw-r--r--test/zdtm/static/tun.c236
-rw-r--r--test/zdtm/static/tun.desc1
-rw-r--r--test/zdtm/static/umask00.c30
-rw-r--r--test/zdtm/static/unbound_sock.c42
-rw-r--r--test/zdtm/static/unhashed_proc.c81
-rw-r--r--test/zdtm/static/unhashed_proc.desc1
-rw-r--r--test/zdtm/static/unlink_fifo.c50
-rw-r--r--test/zdtm/static/unlink_fifo_wronly.c60
-rw-r--r--test/zdtm/static/unlink_fstat00.c136
-rwxr-xr-xtest/zdtm/static/unlink_fstat00.hook11
l---------test/zdtm/static/unlink_fstat01+.c1
-rw-r--r--test/zdtm/static/unlink_fstat01.c91
-rw-r--r--test/zdtm/static/unlink_fstat02.c112
-rw-r--r--test/zdtm/static/unlink_fstat03.c108
-rw-r--r--test/zdtm/static/unlink_fstat03.desc1
-rw-r--r--test/zdtm/static/unlink_fstat03.opts1
-rw-r--r--test/zdtm/static/unlink_largefile.c59
-rw-r--r--test/zdtm/static/unlink_largefile.desc1
-rw-r--r--test/zdtm/static/unlink_mmap00.c78
-rw-r--r--test/zdtm/static/unlink_mmap00.desc1
-rw-r--r--test/zdtm/static/unlink_mmap01.c102
-rw-r--r--test/zdtm/static/unlink_mmap01.desc1
-rw-r--r--test/zdtm/static/unlink_mmap02.c77
-rw-r--r--test/zdtm/static/unlink_mmap02.desc1
-rw-r--r--test/zdtm/static/unlink_regular00.c109
-rw-r--r--test/zdtm/static/unlink_regular00.desc1
-rw-r--r--test/zdtm/static/uptime_grow.c51
-rw-r--r--test/zdtm/static/uptime_grow.desc1
-rw-r--r--test/zdtm/static/utsname.c46
-rw-r--r--test/zdtm/static/utsname.desc1
-rw-r--r--test/zdtm/static/vdso00.c34
-rw-r--r--test/zdtm/static/vdso01.c420
-rw-r--r--test/zdtm/static/vdso01.desc1
-rw-r--r--test/zdtm/static/vfork00.c80
-rw-r--r--test/zdtm/static/vfork00.desc1
-rw-r--r--test/zdtm/static/vsx.c400
-rw-r--r--test/zdtm/static/vsx.desc1
-rw-r--r--test/zdtm/static/vt.c60
-rw-r--r--test/zdtm/static/vt.desc1
-rw-r--r--test/zdtm/static/wait00.c61
-rw-r--r--test/zdtm/static/write_read00.c61
-rw-r--r--test/zdtm/static/write_read01.c69
-rw-r--r--test/zdtm/static/write_read02.c80
-rw-r--r--test/zdtm/static/write_read10.c129
-rw-r--r--test/zdtm/static/xids00.c128
-rw-r--r--test/zdtm/static/zombie00.c110
389 files changed, 27072 insertions, 0 deletions
diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile
new file mode 100644
index 000000000..74712b5b4
--- /dev/null
+++ b/test/zdtm/static/Makefile
@@ -0,0 +1,409 @@
+include ../Makefile.inc
+
+LIBDIR = ../lib
+LIB = $(LIBDIR)/libzdtmtst.a
+override CPPFLAGS += -I$(LIBDIR)
+CFLAGS = -g -O2 -Wall -Werror -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0
+CFLAGS += $(USERCFLAGS)
+
+TST_NOFILE = \
+ busyloop00 \
+ sleeping00 \
+ pid00 \
+ caps00 \
+ wait00 \
+ zombie00 \
+ fpu00 \
+ fpu01 \
+ arm-neon00 \
+ futex \
+ futex-rl \
+ mmx00 \
+ sse00 \
+ sse20 \
+ mprotect00 \
+ timers \
+ timerfd \
+ unbound_sock \
+ sched_prio00 \
+ sched_policy00 \
+ socket_listen \
+ socket_listen6 \
+ socket_udp \
+ socket6_udp \
+ sk-freebind \
+ sk-freebind-false \
+ socket_udplite \
+ socket_aio \
+ socket_close_data \
+ socket_snd_addr \
+ socket_dgram_data \
+ packet_sock \
+ packet_sock_mmap \
+ sock_filter \
+ msgque \
+ inotify_system \
+ inotify_system_nodel \
+ shm \
+ ptrace_sig \
+ pipe00 \
+ pipe01 \
+ pipe02 \
+ pthread00 \
+ pthread01 \
+ pthread02 \
+ vdso00 \
+ vdso01 \
+ utsname \
+ pstree \
+ sockets01 \
+ sockets02 \
+ sockets_spair \
+ socket_queues \
+ socket_queues02 \
+ socket-tcp \
+ socket-tcp6 \
+ socket-tcp-local \
+ socket-tcp-nfconntrack \
+ socket-tcp6-local \
+ socket-tcpbuf \
+ socket-tcpbuf-local \
+ socket-tcpbuf6-local \
+ socket-tcpbuf6 \
+ socket-closed-tcp \
+ sock_opts00 \
+ sock_opts01 \
+ sk-unix-unconn \
+ ipc_namespace \
+ selfexe00 \
+ sem \
+ maps01 \
+ maps02 \
+ maps03 \
+ maps04 \
+ maps05 \
+ mlock_setuid \
+ xids00 \
+ groups \
+ pdeath_sig \
+ file_fown \
+ proc-self \
+ eventfs00 \
+ signalfd00 \
+ inotify_irmap \
+ fanotify00 \
+ uptime_grow \
+ session00 \
+ rlimits00 \
+ pty00 \
+ pty01 \
+ pty02 \
+ pty03 \
+ pty04 \
+ tty00 \
+ tty02 \
+ tty03 \
+ poll \
+ mountpoints \
+ netns \
+ netns-dev \
+ session01 \
+ session02 \
+ session03 \
+ socket-ext \
+ unhashed_proc \
+ cow00 \
+ child_opened_proc \
+ posix_timers \
+ sigpending \
+ sigaltstack \
+ sk-netlink \
+ mem-touch \
+ grow_map \
+ grow_map02 \
+ grow_map03 \
+ tun \
+ stopped \
+ stopped01 \
+ stopped02 \
+ stopped12 \
+ rtc \
+ clean_mntns \
+ mntns_rw_ro_rw \
+ dumpable01 \
+ dumpable02 \
+ remap_dead_pid \
+ remap_dead_pid_root \
+ aio00 \
+ fd \
+ apparmor \
+ seccomp_strict \
+ seccomp_filter \
+ seccomp_filter_tsync \
+ seccomp_filter_inheritance \
+ different_creds \
+ vsx \
+ bridge \
+ vfork00 \
+ oom_score_adj \
+ loginuid \
+# jobctl00 \
+
+TST_FILE = \
+ write_read00 \
+ write_read01 \
+ write_read02 \
+ write_read10 \
+ maps00 \
+ link10 \
+ file_attr \
+ deleted_unix_sock \
+ sk-unix-rel \
+ deleted_dev \
+ unlink_fstat00 \
+ unlink_fstat01 \
+ unlink_fstat01+ \
+ unlink_fstat02 \
+ unlink_fstat03 \
+ unlink_largefile \
+ mtime_mmap \
+ fifo \
+ fifo-ghost \
+ fifo_ro \
+ fifo_wronly \
+ console \
+ vt \
+ unlink_fifo \
+ unlink_fifo_wronly \
+ unlink_mmap00 \
+ unlink_mmap01 \
+ unlink_mmap02 \
+ file_shared \
+ file_append \
+ cow01 \
+ fdt_shared \
+ sockets00 \
+ sockets_dgram \
+ file_locks00 \
+ file_locks01 \
+ file_locks02 \
+ file_locks03 \
+ file_locks04 \
+ file_locks05 \
+ netns-nf \
+ maps_file_prot \
+ socket_close_data01 \
+
+TST_DIR = \
+ cwd00 \
+ cwd01 \
+ cwd02 \
+ overmount_dev \
+ overmount_file \
+ overmount_fifo \
+ overmount_sock \
+ tempfs \
+ tempfs_ro \
+ tempfs_subns \
+ mnt_ro_bind \
+ mount_paths \
+ bind-mount \
+ inotify00 \
+ inotify01 \
+ inotify02 \
+ cgroup00 \
+ rmdir_open \
+ cgroup01 \
+ cgroup02 \
+ cgroup03 \
+ mntns_open \
+ mntns_link_remap \
+ mntns_link_ghost \
+ mntns_shared_bind \
+ mntns_shared_bind02 \
+ mntns_root_bind \
+ mntns_root_bind02 \
+ mnt_ext_auto \
+ mnt_ext_master \
+ mntns_deleted \
+ binfmt_misc \
+ unlink_regular00 \
+
+TST_DIR_FILE = \
+ chroot \
+ chroot-file
+
+TST = \
+ $(TST_NOFILE) \
+ $(TST_FILE) \
+ $(TST_DIR) \
+ $(TST_DIR_FILE) \
+ env00 \
+ fifo-rowo-pair \
+ umask00 \
+ cmdlinenv00 \
+
+TST_STATE = \
+ conntracks \
+ route_rules \
+
+
+SRC = $(TST:%=%.c)
+OBJ = $(SRC:%.c=%.o)
+DEP = $(SRC:%.c=%.d)
+PID = $(TST:%=%.pid)
+OUT = $(TST:%=%.out)
+STATE = $(TST_STATE:%=%.state)
+STATE_OUT = $(TST_STATE:%=%.out)
+
+DEPEND.c = $(COMPILE.c) -MM -MP
+%.d: %.c
+ $(DEPEND.c) $(OUTPUT_OPTION) $<
+
+all: $(TST) criu-rtc.so
+install: all
+.PHONY: all install
+
+inotify_system_nodel.c: inotify_system.c
+ ln -sf inotify_system.c inotify_system_nodel.c
+
+$(TST_NOFILE:%=%.pid): %.pid: %
+ $(<D)/$(<F) --pidfile=$@ --outfile=$<.out
+
+$(TST_FILE:%=%.pid): %.pid: %
+ $(<D)/$(<F) --pidfile=$@ --outfile=$<.out --filename=$<.test
+
+$(TST_DIR:%=%.pid): %.pid: %
+ $(<D)/$(<F) --pidfile=$@ --outfile=$<.out --dirname=$<.test
+
+$(TST_DIR_FILE:%=%.pid): %.pid: %
+ $(<D)/$(<F) --pidfile=$@ --outfile=$<.out --dirname=$<.dir.test --filename=$<.test
+
+cmdlinenv00.pid: cmdlinenv00
+ $(<D)/$(<F) --pidfile=$@ --outfile=$<.out --arg1=arg1 --arg2=arg2 --arg3=arg3
+
+env00.pid: env00
+ $(<D)/$(<F) --pidfile=$@ --outfile=$<.out --envname=ENV_00_TEST
+umask00.pid: umask00
+ $(<D)/$(<F) --pidfile=$@ --outfile=$<.out --mask=0345
+
+fifo-rowo-pair.pid: fifo-rowo-pair
+ $(<D)/$(<F) --pidfile=$@ --outfile=$<.out --name_master=$<.master.test --name_slave=$<.slave.test
+
+%.out: %.pid %
+ -kill -TERM `cat $<`
+
+$(TST_STATE:%=%.state): %.state: %
+ $(<D)/$(<F) --statefile=$@ --outfile=$<.out start
+
+$(TST_STATE:%=%.out): %.out: %
+ -$(<D)/$(<F) --statefile=$<.state --outfile=$@ stop
+
+start: $(PID) $(STATE)
+
+%.is_running: %.pid
+ kill -0 `cat $<`
+
+check_start: $(PID:%.pid=%.is_running)
+
+stop: $(STATE_OUT)
+ -kill -TERM `cat *.pid`
+
+WAIT_TIME=240
+%.stop: %.pid %
+ kill -TERM `cat $<`; \
+ i=0; \
+ while [ $$i -lt $(WAIT_TIME) ] ; do \
+ kill -0 `cat $< 2>/dev/null` 2>/dev/null || break; \
+ sleep 1; \
+ echo -n .; \
+ i=`expr $$i + 1`; \
+ done; \
+ echo; \
+ [ $$i -lt $(WAIT_TIME) ]
+
+wait_stop:
+ i=0; \
+ while [ $$i -lt $(WAIT_TIME) ] ; do \
+ kill -0 `cat *.pid 2>/dev/null` 2>/dev/null || break; \
+ sleep 1; \
+ i=`expr $$i + 1`; \
+ done
+
+$(TST): $(LIB)
+
+aio00: override LDLIBS += -laio
+different_creds: override LDLIBS += -lcap
+futex.o: override CFLAGS += -pthread
+futex: override LDFLAGS += -pthread
+futex-rl.o: override CFLAGS += -pthread
+futex-rl: override LDFLAGS += -pthread
+jobctl00: override LDLIBS += -lutil
+socket_listen: override LDLIBS += -lrt -pthread
+socket_aio: override LDLIBS += -lrt -pthread
+uptime_grow: override LDLIBS += -lrt -pthread
+unlink_largefile: override CFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
+inotify_system_nodel: override CFLAGS += -DNODEL
+pthread00: override LDLIBS += -pthread
+pthread01: override LDLIBS += -pthread
+pthread02: override LDLIBS += -pthread
+different_creds: override LDLIBS += -pthread
+sigpending: override LDLIBS += -pthread
+sigaltstack: override LDLIBS += -pthread
+seccomp_filter_tsync: override LDLIBS += -pthread
+shm: override CFLAGS += -DNEW_IPC_NS
+msgque: override CFLAGS += -DNEW_IPC_NS
+sem: override CFLAGS += -DNEW_IPC_NS
+posix_timers: override LDLIBS += -lrt -pthread
+socket-tcp6: override CFLAGS += -D ZDTM_IPV6
+socket-tcpbuf6: override CFLAGS += -D ZDTM_IPV6
+socket-tcpbuf-local: override CFLAGS += -D ZDTM_TCP_LOCAL
+socket-tcpbuf6-local: override CFLAGS += -D ZDTM_TCP_LOCAL -D ZDTM_IPV6
+socket-tcp6-local: override CFLAGS += -D ZDTM_TCP_LOCAL -D ZDTM_IPV6
+socket-tcp-local: override CFLAGS += -D ZDTM_TCP_LOCAL
+socket-tcp-nfconntrack: override CFLAGS += -D ZDTM_TCP_LOCAL -DZDTM_CONNTRACK
+socket_listen6: override CFLAGS += -D ZDTM_IPV6
+sigpending: override LDLIBS += -lrt
+vdso01: override LDLIBS += -lrt
+mntns_link_remap: override CFLAGS += -DZDTM_LINK_REMAP
+mntns_shared_bind02: override CFLAGS += -DSHARED_BIND02
+mntns_root_bind02: override CFLAGS += -DROOT_BIND02
+maps02: get_smaps_bits.o
+mlock_setuid: get_smaps_bits.o
+inotify01: override CFLAGS += -DINOTIFY01
+unlink_fstat01+: override CFLAGS += -DUNLINK_OVER
+sk-freebind-false: override CFLAGS += -DZDTM_FREEBIND_FALSE
+stopped01: override CFLAGS += -DZDTM_STOPPED_KILL
+stopped02: override CFLAGS += -DZDTM_STOPPED_TKILL
+stopped12: override CFLAGS += -DZDTM_STOPPED_KILL -DZDTM_STOPPED_TKILL
+
+$(LIB): force
+ $(Q) $(MAKE) -C $(LIBDIR)
+
+clean:
+ $(RM) -f $(OBJ) $(TST) *~ criu-rtc.so criu-rtc.pb-c.c criu-rtc.pb-c.h get_smaps_bits.o
+
+cleandep: clean
+ $(RM) -f $(DEP)
+
+cleanout:
+ $(RM) -f -r *.pid *.out* *.test* *.state
+
+%.cleanout: %
+ $(Q) $(RM) -f -r $<.pid $<.out* *$<.test* $<.*.test $<.state $<.init.pid
+
+realclean: cleandep cleanout
+
+rtc.c: criu-rtc.so
+
+criu-rtc.pb-c.c: criu-rtc.proto
+ protoc-c --proto_path=. --c_out=. criu-rtc.proto
+
+criu-rtc.so: criu-rtc.c criu-rtc.pb-c.c
+ $(CC) -g -Wall -shared -nostartfiles criu-rtc.c criu-rtc.pb-c.c -o criu-rtc.so -iquote ../../../criu/include -fPIC $(USERCFLAGS)
+
+.PHONY: force clean cleandep cleanout realclean start check_start stop wait_stop
+
+ifeq ($(filter-out no-deps-targets, $(MAKECMDGOALS)),)
+-include $(DEP)
+endif
diff --git a/test/zdtm/static/aio00.c b/test/zdtm/static/aio00.c
new file mode 100644
index 000000000..93ca7aabb
--- /dev/null
+++ b/test/zdtm/static/aio00.c
@@ -0,0 +1,36 @@
+#include <libaio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that plain io_setup works";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+int main(int argc, char **argv)
+{
+ int ret;
+ io_context_t ctx = 0;
+
+ test_init(argc, argv);
+
+ if (io_setup(1, &ctx) < 0) {
+ pr_perror("Can't setup io ctx");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = io_getevents(ctx, 0, 1, NULL, NULL);
+ if (ret != 0) {
+ if (ret < 0)
+ fail("IO ctx lost (%d)", ret);
+ else
+ fail("IO ctx screwed up (%d)", ret);
+ } else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/aio00.desc b/test/zdtm/static/aio00.desc
new file mode 100644
index 000000000..fcaf84a14
--- /dev/null
+++ b/test/zdtm/static/aio00.desc
@@ -0,0 +1 @@
+{'feature': 'aio_remap', 'flags': 'nouser'}
diff --git a/test/zdtm/static/apparmor.c b/test/zdtm/static/apparmor.c
new file mode 100644
index 000000000..7af0be8db
--- /dev/null
+++ b/test/zdtm/static/apparmor.c
@@ -0,0 +1,90 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <linux/limits.h>
+#include <signal.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that an apparmor profile is restored";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+#define PROFILE "criu_test"
+
+int setprofile()
+{
+ char profile[1024];
+ int fd, len;
+
+ len = snprintf(profile, sizeof(profile), "changeprofile " PROFILE);
+ if (len < 0 || len >= sizeof(profile)) {
+ fail("bad sprintf\n");
+ return -1;
+ }
+
+ fd = open("/proc/self/attr/current", O_WRONLY);
+ if (fd < 0) {
+ fail("couldn't open fd\n");
+ return -1;
+ }
+
+ /* apparmor wants this in exactly one write, so we use write() here
+ * vs. fprintf Just To Be Sure */
+ len = write(fd, profile, len);
+ close(fd);
+
+ if (len < 0) {
+ fail("couldn't write profile\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int checkprofile()
+{
+ FILE *f;
+ char path[PATH_MAX], profile[1024];
+ int len;
+
+ sprintf(path, "/proc/self/attr/current");
+
+ f = fopen(path, "r");
+ if (!f) {
+ fail("couldn't open lsm current\n");
+ return -1;
+ }
+
+ len = fscanf(f, "%[^ \n]s", profile);
+ fclose(f);
+ if (len != 1) {
+ fail("wrong number of items scanned %d\n", len);
+ return -1;
+ }
+
+ if (strcmp(profile, PROFILE) != 0) {
+ fail("bad profile .%s. expected .%s.\n", profile, PROFILE);
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ test_init(argc, argv);
+
+ setprofile();
+
+ test_daemon();
+ test_waitsig();
+
+ if (checkprofile(0) == 0)
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/apparmor.checkskip b/test/zdtm/static/apparmor.checkskip
new file mode 100755
index 000000000..99fa72721
--- /dev/null
+++ b/test/zdtm/static/apparmor.checkskip
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+test -d /sys/kernel/security/apparmor || exit 1
+apparmor_parser -r `dirname $0`/apparmor.profile
diff --git a/test/zdtm/static/apparmor.desc b/test/zdtm/static/apparmor.desc
new file mode 100644
index 000000000..d969725f6
--- /dev/null
+++ b/test/zdtm/static/apparmor.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/apparmor.profile b/test/zdtm/static/apparmor.profile
new file mode 100644
index 000000000..69b1b259b
--- /dev/null
+++ b/test/zdtm/static/apparmor.profile
@@ -0,0 +1,8 @@
+# vim:syntax=apparmor
+
+profile criu_test {
+ /** rwmlkix,
+ capability,
+ unix,
+ signal,
+}
diff --git a/test/zdtm/static/arm-neon00.c b/test/zdtm/static/arm-neon00.c
new file mode 100644
index 000000000..96da16c6b
--- /dev/null
+++ b/test/zdtm/static/arm-neon00.c
@@ -0,0 +1,67 @@
+#include <stdlib.h>
+#include <time.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Initialize VFP registers before a migration,\n"
+ "check the VFP state is the same after a restore.";
+const char *test_author = "Alexander Karatshov <alekskartashov@parallels.com>";
+
+
+#ifdef __arm__
+
+int main(int argc, char ** argv)
+{
+ srand(time(0));
+
+ int a = rand() % 100;
+ int b = rand() % 100;
+ int c = rand() % 100;
+ int y1 = a + b*c;
+ int y2;
+
+ test_init(argc, argv);
+
+ asm (
+ ".fpu neon \n"
+ "vmov.32 d0[0], %0 \n"
+ "vmov.32 d1[0], %1 \n"
+ "vmov.32 d2[0], %2 \n"
+ ".fpu softvfp \n"
+ : : "r"(a), "r"(b), "r"(c)
+ );
+
+ test_msg("Preparing to wait...\n");
+
+ test_daemon();
+ test_waitsig();
+
+ test_msg("Restored.\n");
+
+ asm (
+ ".fpu neon \n"
+ "vmul.I32 d3, d1, d2 \n"
+ "vadd.I32 d4, d0, d3 \n"
+ "vmov.32 %0, d4[0] \n"
+ ".fpu softvfp \n"
+ : "=r"(y2)
+ );
+
+ if (y1 != y2)
+ fail("VFP restoration failed: result = %d, expected = %d (a = %d, b = %d, c = %d)\n", y2, y1, a, b, c);
+ else
+ pass();
+
+ return 0;
+}
+
+#else
+
+int main(int argc, char *argv[])
+{
+ test_init(argc, argv);
+ skip("This test is supposed to run on an ARM machine!");
+ return 0;
+}
+
+#endif
diff --git a/test/zdtm/static/arm-neon00.desc b/test/zdtm/static/arm-neon00.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/arm-neon00.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/bind-mount.c b/test/zdtm/static/bind-mount.c
new file mode 100644
index 000000000..06c5cf173
--- /dev/null
+++ b/test/zdtm/static/bind-mount.c
@@ -0,0 +1,62 @@
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check bind-mounts";
+const char *test_author = "Pavel Emelianov <avagin@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char **argv)
+{
+ char test_dir[PATH_MAX], test_bind[PATH_MAX];
+ char test_file[PATH_MAX], test_bind_file[PATH_MAX];
+ int fd;
+
+ test_init(argc, argv);
+
+ mkdir(dirname, 0700);
+
+ snprintf(test_dir, sizeof(test_dir), "%s/test", dirname);
+ snprintf(test_bind, sizeof(test_bind), "%s/bind", dirname);
+ snprintf(test_file, sizeof(test_file), "%s/test/test.file", dirname);
+ snprintf(test_bind_file, sizeof(test_bind_file), "%s/bind/test.file", dirname);
+
+ mkdir(test_dir, 0700);
+ mkdir(test_bind, 0700);
+
+ if (mount(test_dir, test_bind, NULL, MS_BIND, NULL)) {
+ pr_perror("Unable to mount %s to %s", test_dir, test_bind);
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ fd = open(test_file, O_CREAT | O_WRONLY | O_EXCL, 0600);
+ if (fd < 0) {
+ pr_perror("Unable to open %s", test_file);
+ return 1;
+ }
+ close(fd);
+
+ if (access(test_bind_file, F_OK)) {
+ pr_perror("%s doesn't exist", test_bind_file);
+ return 1;
+ }
+
+ if (umount(test_bind)) {
+ pr_perror("Unable to umount %s", test_bind);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/bind-mount.desc b/test/zdtm/static/bind-mount.desc
new file mode 100644
index 000000000..7657ba45c
--- /dev/null
+++ b/test/zdtm/static/bind-mount.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid'}
diff --git a/test/zdtm/static/binfmt_misc.c b/test/zdtm/static/binfmt_misc.c
new file mode 100644
index 000000000..9d7394c75
--- /dev/null
+++ b/test/zdtm/static/binfmt_misc.c
@@ -0,0 +1,194 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <linux/limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that binfmt_misc entries remain registered";
+const char *test_author = "Kirill Tkhai <ktkhai@odin.com";
+
+#define MAX_REG_STR 256
+#define MAX_MAGIC 16
+#define MAX_MAGIC_OFFSET 128 /* Max magic+offset */
+#define MAX_EXTENSION 128
+
+char *dirname = "binfmt_misc.dir";
+TEST_OPTION(dirname, string, "binfmt_misc mount directory name", 1);
+
+const char *NAME[2] = { "magic_file", "extension_file" };
+
+/* :name:type:offset:magic:mask:interpreter:flags */
+
+void create_magic_pattern(char *buf, const char *name)
+{
+ int i, magic, mask, offset;
+
+ magic = rand() % (MAX_MAGIC + 1);
+ mask = (rand() % 2) ? magic : 0;
+ offset = MAX_MAGIC_OFFSET - magic;
+ offset = rand() % (offset + 1);
+
+ buf += sprintf(buf, ":%s:M:%d:", name, offset);
+
+ for (i = 0; i < magic; i++)
+ buf += sprintf(buf, "\\x%02x", rand() % 256);
+
+ buf += sprintf(buf, ":");
+
+ for (i = 0; i < mask; i++)
+ buf += sprintf(buf, "\\x%02x", rand() % 256);
+
+ sprintf(buf, ":/bin/interpreter:OCP");
+}
+
+void create_extension_pattern(char *buf, const char *name)
+{
+ int i, extension;
+
+ extension = rand() % (MAX_EXTENSION + 1);
+ buf += sprintf(buf, ":%s:E::", name);
+
+ for (i = 0; i < extension; i++) {
+ int c = rand();
+
+ if (c == '\0' || c == ':' || c == '\n' || c == '/')
+ c = '1';
+ buf += sprintf(buf, "%c", c);
+ }
+
+ sprintf(buf, "::/bin/bash:");
+}
+
+int dump_content(const char *path, char **dump)
+{
+ int fd, len;
+ char *p;
+
+ p = *dump = malloc(PAGE_SIZE);
+ if (!p) {
+ fail("malloc");
+ return -1;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fail("open");
+ return -1;
+ }
+
+ len = read(fd, p, PAGE_SIZE-1);
+ close(fd);
+ if (len <= 0) {
+ fail("read");
+ return -1;
+ }
+
+ p[len] = '\0';
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ char buf[MAX_REG_STR + 1];
+ char path[PATH_MAX];
+ char *dump[2];
+ int i, fd, len;
+
+ test_init(argc, argv);
+
+ if (mkdir(dirname, 0777)) {
+ fail("mkdir");
+ exit(1);
+ }
+
+ if (mount("none", dirname, "binfmt_misc", 0, NULL)) {
+ fail("mount failed");
+ exit(1);
+ }
+
+ /* Register binfmt_entries */
+ sprintf(path, "%s/" "register", dirname);
+ fd = open(path, O_WRONLY);
+ if (!fd) {
+ fail("open");
+ exit(1);
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (i % 2 == 0)
+ create_magic_pattern(buf, NAME[i]);
+ else
+ create_extension_pattern(buf, NAME[i]);
+
+ test_msg("string: %s\n", buf);
+ len = strlen(buf);
+
+ if (len != write(fd, buf, len)) {
+ fail("write %s", NAME[i]);
+ exit(1);
+ }
+ }
+
+ close(fd);
+
+ /* Disable one of the entries */
+ sprintf(path, "%s/%s", dirname, NAME[0]);
+ fd = open(path, O_WRONLY);
+ if (!fd || write(fd, "0", 1) != 1) {
+ fail("Can't disable %s\n", path);
+ exit(1);
+ }
+ close(fd);
+
+ /* Dump files content */
+ for (i = 0; i < 2; i ++) {
+ sprintf(path, "%s/%s", dirname, NAME[i]);
+ if (dump_content(path, &dump[i]))
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ /* Check */
+ for (i = 0; i < 2; i ++) {
+ char *tmp;
+
+ sprintf(path, "%s/%s", dirname, NAME[i]);
+ if (dump_content(path, &tmp))
+ exit(1);
+
+ if (strcmp(tmp, dump[i])) {
+ fail("Content differs:\n%s\nand\n%s\n", tmp, dump[i]);
+ exit(1);
+ }
+ free(dump[i]);
+ free(tmp);
+ }
+
+ pass();
+
+ /* Clean up */
+ for (i = 0; i < 2; i++) {
+ sprintf(path, "%s/%s", dirname, NAME[i]);
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ pr_perror("open %s", path);
+ continue;
+ }
+ if (write(fd, "-1", 2) != 2)
+ pr_perror("cleanup %s", path);
+ close(fd);
+ }
+
+ umount(dirname);
+ rmdir(dirname);
+
+ return 0;
+}
diff --git a/test/zdtm/static/binfmt_misc.desc b/test/zdtm/static/binfmt_misc.desc
new file mode 100644
index 000000000..5f0328f0b
--- /dev/null
+++ b/test/zdtm/static/binfmt_misc.desc
@@ -0,0 +1 @@
+{'flavor': 'ns', 'flags': 'excl suid'}
diff --git a/test/zdtm/static/bridge.c b/test/zdtm/static/bridge.c
new file mode 100644
index 000000000..983c262af
--- /dev/null
+++ b/test/zdtm/static/bridge.c
@@ -0,0 +1,113 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <linux/limits.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "check that empty bridges are c/r'd correctly";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+#define BRIDGE_NAME "zdtmbr0"
+
+int add_bridge(void)
+{
+ if (system("ip link add " BRIDGE_NAME " type bridge"))
+ return -1;
+
+ if (system("ip addr add 10.0.55.55/32 dev " BRIDGE_NAME))
+ return -1;
+
+ /* use a link local address so we can test scope_id change */
+ if (system("ip addr add fe80:4567::1/64 nodad dev " BRIDGE_NAME))
+ return -1;
+
+ if (system("ip link set " BRIDGE_NAME " up"))
+ return -1;
+
+ return 0;
+}
+
+int del_bridge(void)
+{
+ /* don't check for errors, let's try to make sure it's deleted */
+ system("ip link set " BRIDGE_NAME " down");
+
+ if (system("ip link del " BRIDGE_NAME))
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int ret = 1;
+ struct sockaddr_in6 addr;
+ int sk;
+
+ test_init(argc, argv);
+
+ if (add_bridge() < 0)
+ return 1;
+
+ sk = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sk < 0) {
+ fail("can't get socket");
+ goto out;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_port = htons(0);
+ addr.sin6_family = AF_INET6;
+ if (inet_pton(AF_INET6, "fe80:4567::1", &addr.sin6_addr) < 0) {
+ fail("can't convert inet6 addr");
+ goto out;
+ }
+ addr.sin6_scope_id = if_nametoindex(BRIDGE_NAME);
+
+ if (bind(sk, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+ fail("can't bind");
+ goto out;
+ }
+
+ /* Here, we grep for inet because some of the IPV6 DAD stuff can be
+ * racy, and all we really care about is that the bridge got restored
+ * with the right MAC, since we know DAD will succeed eventually.
+ *
+ * (I got this race with zdtm.py, but not with zdtm.sh; not quite sure
+ * what the environment difference is/was.)
+ */
+ if (system("ip addr list dev " BRIDGE_NAME " | grep inet | sort > bridge.dump.test")) {
+ pr_perror("can't save net config");
+ fail("Can't save net config");
+ goto out;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (system("ip addr list dev " BRIDGE_NAME " | grep inet | sort > bridge.rst.test")) {
+ fail("Can't get net config");
+ goto out;
+ }
+
+ if (system("diff bridge.rst.test bridge.dump.test")) {
+ fail("Net config differs after restore");
+ goto out;
+ }
+
+ pass();
+
+ ret = 0;
+
+out:
+ del_bridge();
+ return ret;
+}
diff --git a/test/zdtm/static/bridge.desc b/test/zdtm/static/bridge.desc
new file mode 100644
index 000000000..8af1df737
--- /dev/null
+++ b/test/zdtm/static/bridge.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'deps': [ '/bin/sh', '/usr/bin/sort', '/bin/grep', '/sbin/ip', '/usr/bin/diff'], 'flags': 'suid'}
diff --git a/test/zdtm/static/busyloop00.c b/test/zdtm/static/busyloop00.c
new file mode 100644
index 000000000..e9a065cf7
--- /dev/null
+++ b/test/zdtm/static/busyloop00.c
@@ -0,0 +1,18 @@
+#include "zdtmtst.h"
+
+const char *test_doc = "Run busy loop while migrating";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+int main(int argc, char ** argv)
+{
+ test_init(argc, argv);
+
+ test_daemon();
+
+ while (test_go())
+ ;
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/caps00.c b/test/zdtm/static/caps00.c
new file mode 100644
index 000000000..5911fa19b
--- /dev/null
+++ b/test/zdtm/static/caps00.c
@@ -0,0 +1,178 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/prctl.h>
+#include <linux/capability.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that aps are preserved";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+struct cap_hdr {
+ unsigned int version;
+ int pid;
+};
+
+struct cap_data {
+ unsigned int eff;
+ unsigned int prm;
+ unsigned int inh;
+};
+
+#define _LINUX_CAPABILITY_VERSION_3 0x20080522
+#define _LINUX_CAPABILITY_U32S_3 2
+#define CAP_CHOWN 0
+#define CAP_DAC_OVERRIDE 1
+
+int capget(struct cap_hdr *hdrp, struct cap_data *datap);
+int capset(struct cap_hdr *hdrp, const struct cap_data *datap);
+
+static int cap_last_cap = 63;
+#define NORM_CAPS(v, cap) v[1].cap &= (1LL << (cap_last_cap + 1 - 32)) - 1;
+
+int main(int argc, char **argv)
+{
+ task_waiter_t t;
+ int pid, result_pipe[2];
+ struct cap_data data[_LINUX_CAPABILITY_U32S_3];
+ struct cap_data data_2[_LINUX_CAPABILITY_U32S_3];
+ char res = 'x';
+ FILE *f;
+
+ test_init(argc, argv);
+ task_waiter_init(&t);
+
+ f = fopen("/proc/sys/kernel/cap_last_cap", "r");
+ if (f) {
+ if (fscanf(f, "%d", &cap_last_cap) != 1) {
+ pr_perror("Unable to read cal_last_cap");
+ return 1;
+ }
+ fclose(f);
+ } else
+ test_msg("/proc/sys/kernel/cap_last_cap is not available\n");
+
+ if (pipe(result_pipe)) {
+ pr_perror("Can't create pipe");
+ return 1;
+ }
+
+ pid = test_fork();
+ if (pid == 0) {
+ struct cap_hdr hdr;
+ if (prctl(PR_CAPBSET_DROP, CAP_SETPCAP, 0, 0, 0)) {
+ res = 'x';
+ task_waiter_complete_current(&t);
+ goto bad;
+ }
+
+ hdr.version = _LINUX_CAPABILITY_VERSION_3;
+ hdr.pid = 0;
+
+ if (capget(&hdr, data) < 0) {
+ pr_perror("capget");
+ return -1;
+ }
+
+ hdr.version = _LINUX_CAPABILITY_VERSION_3;
+ hdr.pid = 0;
+
+ data[0].eff &= ~((1 << CAP_CHOWN) | (1 << CAP_DAC_OVERRIDE));
+ data[0].prm &= ~(1 << CAP_DAC_OVERRIDE);
+
+ if (capset(&hdr, data) < 0) {
+ pr_perror("capset");
+ return -1;
+ }
+
+ task_waiter_complete_current(&t);
+ task_waiter_wait4(&t, getppid());
+
+ hdr.version = _LINUX_CAPABILITY_VERSION_3;
+ hdr.pid = 0;
+
+ if (capget(&hdr, data_2) < 0) {
+ pr_perror("second capget");
+ return -1;
+ }
+
+ NORM_CAPS(data, eff);
+ NORM_CAPS(data, prm);
+ NORM_CAPS(data, inh);
+ NORM_CAPS(data_2, eff);
+ NORM_CAPS(data_2, prm);
+ NORM_CAPS(data_2, inh);
+
+ if (data[0].eff != data_2[0].eff) {
+ res = '1';
+ goto bad;
+ }
+ if (data[1].eff != data_2[1].eff) {
+ res = '2';
+ goto bad;
+ }
+ if (data[0].prm != data_2[0].prm) {
+ res = '3';
+ goto bad;
+ }
+ if (data[1].prm != data_2[1].prm) {
+ res = '4';
+ goto bad;
+ }
+ if (data[0].inh != data_2[0].inh) {
+ res = '3';
+ goto bad;
+ }
+ if (data[1].inh != data_2[1].inh) {
+ res = '4';
+ goto bad;
+ }
+
+ if (prctl(PR_CAPBSET_READ, CAP_SETPCAP, 0, 0, 0) != 0) {
+ res='5';
+ goto bad;
+ }
+
+ res = '0';
+bad:
+ write(result_pipe[1], &res, 1);
+
+ if (res != '0') {
+ write(result_pipe[1], data, sizeof(data));
+ write(result_pipe[1], data_2, sizeof(data_2));
+ }
+
+ close(result_pipe[0]);
+ close(result_pipe[1]);
+ _exit(0);
+ }
+
+ task_waiter_wait4(&t, pid);
+
+ test_daemon();
+ test_waitsig();
+
+ task_waiter_complete_current(&t);
+
+ read(result_pipe[0], &res, 1);
+
+ if (res == '0')
+ pass();
+ else {
+ read(result_pipe[0], data, sizeof(data));
+ read(result_pipe[0], data_2, sizeof(data_2));
+ test_msg("{eff,prm,inh}[]={%08x,%08x,%08x}, {%08x,%08x,%08x}\n",
+ data[0].eff, data[0].prm, data[0].inh,
+ data[1].eff, data[1].prm, data[1].inh);
+ test_msg("{eff,prm,inh}[]={%08x,%08x,%08x}, {%08x,%08x,%08x}\n",
+ data_2[0].eff, data_2[0].prm, data_2[0].inh,
+ data_2[1].eff, data_2[1].prm, data_2[1].inh);
+ fail("Fail: %c", res);
+ }
+ close(result_pipe[0]);
+ close(result_pipe[1]);
+
+ return 0;
+}
diff --git a/test/zdtm/static/caps00.desc b/test/zdtm/static/caps00.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/caps00.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/cgroup00.c b/test/zdtm/static/cgroup00.c
new file mode 100644
index 000000000..ee14d1fd3
--- /dev/null
+++ b/test/zdtm/static/cgroup00.c
@@ -0,0 +1,205 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <stdlib.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that cgroups layout is preserved";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "cgroup directory name", 1);
+static const char *cgname = "zdtmtst";
+#define SUBNAME "subcg00"
+#define SUBNAME2 SUBNAME"/subsubcg"
+
+static int cg_move(char *name)
+{
+ int cgfd, l;
+ char paux[256];
+
+ sprintf(paux, "%s/%s", dirname, name);
+ mkdir(paux, 0600);
+
+ sprintf(paux, "%s/%s/tasks", dirname, name);
+
+ cgfd = open(paux, O_WRONLY);
+ if (cgfd < 0) {
+ pr_perror("Can't open tasks");
+ return -1;
+ }
+
+ l = write(cgfd, "0", 2);
+ close(cgfd);
+
+ if (l < 0) {
+ pr_perror("Can't move self to subcg");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int cg_check(char *name)
+{
+ int found = 0;
+ FILE *cgf;
+ char paux[256], aux[128];
+
+ cgf = fopen("/proc/self/cgroup", "r");
+ if (cgf == NULL)
+ return -1;
+
+ sprintf(aux, "name=%s:/%s\n", cgname, name);
+ while (fgets(paux, sizeof(paux), cgf)) {
+ char *s;
+
+ s = strchr(paux, ':') + 1;
+ test_msg("CMP [%s] vs [%s]\n", s, aux);
+ if (!strcmp(s, aux)) {
+ found = 1;
+ break;
+ }
+ }
+
+ fclose(cgf);
+
+ return found ? 0 : -1;
+}
+
+int main(int argc, char **argv)
+{
+ char aux[64];
+ int p1[2], p2[2], pr[2], status;
+
+ test_init(argc, argv);
+
+ /*
+ * Pipes to talk to two kids.
+ * First, they report that they are ready (int),
+ * then they report the restore status (int).
+ */
+
+ pipe(p1);
+ pipe(p2);
+
+ /* "Restore happened" pipe */
+ pipe(pr);
+
+ if (mkdir(dirname, 0700) < 0) {
+ pr_perror("Can't make dir");
+ goto out;
+ }
+
+ sprintf(aux, "none,name=%s", cgname);
+ if (mount("none", dirname, "cgroup", 0, aux)) {
+ pr_perror("Can't mount cgroups");
+ goto out_rd;
+ }
+
+ if (cg_move(SUBNAME))
+ goto out_rs;
+
+ if (fork() == 0) {
+ if (fork() == 0) {
+ /*
+ * 2nd level kid -- moves into its own
+ * cgroup and triggers slow-path cg_set
+ * restore in criu
+ */
+
+ close(p1[0]);
+ close(p1[1]);
+ close(p2[0]);
+ close(pr[1]);
+
+ status = cg_move(SUBNAME2);
+ write(p2[1], &status, sizeof(status));
+
+ if (status == 0) {
+ read(pr[0], &status, sizeof(status));
+
+ status = cg_check(SUBNAME2);
+ write(p2[1], &status, sizeof(status));
+ }
+
+ exit(0);
+ }
+
+ /*
+ * 1st level kid -- inherits cgroup from
+ * parent and triggers fast-path cg_set
+ * restore in criu
+ */
+
+ close(p1[0]);
+ close(p2[0]);
+ close(p2[1]);
+ close(pr[1]);
+
+ status = 0;
+ write(p1[1], &status, sizeof(status));
+
+ read(pr[0], &status, sizeof(status));
+
+ status = cg_check(SUBNAME);
+ write(p1[1], &status, sizeof(status));
+
+ exit(0);
+ }
+
+ close(p1[1]);
+ close(p2[1]);
+ close(pr[0]);
+
+ status = -1;
+ read(p1[0], &status, sizeof(status));
+ if (status != 0)
+ goto out_ks;
+
+ status = -1;
+ read(p2[0], &status, sizeof(status));
+ if (status != 0)
+ goto out_ks;
+
+ test_daemon();
+ test_waitsig();
+
+ close(pr[1]);
+
+ if (cg_check(SUBNAME)) {
+ fail("Top level task cg changed");
+ goto out_rs;
+ }
+
+ status = -1;
+ read(p1[0], &status, sizeof(status));
+ if (status != 0) {
+ fail("1st level task cg changed");
+ goto out_rs;
+ }
+
+ status = -1;
+ read(p2[0], &status, sizeof(status));
+ if (status != 0) {
+ fail("2nd level task cg changed");
+ goto out_rs;
+ }
+
+ pass();
+
+out_rs:
+ umount(dirname);
+out_rd:
+ rmdir(dirname);
+out:
+ return 0;
+
+out_ks:
+ pr_perror("Error moving into cgroups");
+ close(pr[0]);
+ goto out_rs;
+}
diff --git a/test/zdtm/static/cgroup00.desc b/test/zdtm/static/cgroup00.desc
new file mode 100644
index 000000000..3c6c4a7e2
--- /dev/null
+++ b/test/zdtm/static/cgroup00.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'flags': 'suid', 'opts': '--manage-cgroups'}
diff --git a/test/zdtm/static/cgroup00.hook b/test/zdtm/static/cgroup00.hook
new file mode 100755
index 000000000..a8af99268
--- /dev/null
+++ b/test/zdtm/static/cgroup00.hook
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+[ "$1" == "--clean" -o "$1" == "--pre-restore" ] || exit 0
+
+set -e
+
+tname=$(mktemp -d cgclean.XXXXXX)
+mount -t cgroup none $tname -o "none,name=zdtmtst"
+
+echo "Cleaning $tname"
+
+set +e
+rmdir "$tname/subcg00/subsubcg/"
+rmdir "$tname/subcg00/"
+set -e
+
+echo "Left there is:"
+ls "$tname"
+umount "$tname"
+rmdir "$tname"
diff --git a/test/zdtm/static/cgroup00.opts b/test/zdtm/static/cgroup00.opts
new file mode 100644
index 000000000..998cb32c7
--- /dev/null
+++ b/test/zdtm/static/cgroup00.opts
@@ -0,0 +1 @@
+--manage-cgroups
diff --git a/test/zdtm/static/cgroup01.c b/test/zdtm/static/cgroup01.c
new file mode 100644
index 000000000..384f86053
--- /dev/null
+++ b/test/zdtm/static/cgroup01.c
@@ -0,0 +1,115 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that empty cgroups are preserved";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "cgroup directory name", 1);
+static const char *cgname = "zdtmtst";
+static const char *subname = "subcg01";
+static const char *empty = "empty";
+
+int main(int argc, char **argv)
+{
+ int cgfd, l, ret = 1, i;
+ char aux[1024], paux[1024];
+ FILE *cgf;
+ struct stat st;
+
+ test_init(argc, argv);
+
+ if (mkdir(dirname, 0700) < 0) {
+ pr_perror("Can't make dir");
+ goto out;
+ }
+
+ sprintf(aux, "none,name=%s", cgname);
+ if (mount("none", dirname, "cgroup", 0, aux)) {
+ pr_perror("Can't mount cgroups");
+ goto out_rd;
+ }
+
+ sprintf(paux, "%s/%s", dirname, subname);
+ mkdir(paux, 0600);
+
+ l = sprintf(aux, "%d", getpid());
+ sprintf(paux, "%s/%s/tasks", dirname, subname);
+
+ cgfd = open(paux, O_WRONLY);
+ if (cgfd < 0) {
+ pr_perror("Can't open tasks");
+ goto out_rs;
+ }
+
+ l = write(cgfd, aux, l);
+ close(cgfd);
+
+ if (l < 0) {
+ pr_perror("Can't move self to subcg");
+ goto out_rs;
+ }
+
+ for (i = 0; i < 2; i++) {
+ sprintf(paux, "%s/%s/%s.%d", dirname, subname, empty, i);
+ if (mkdir(paux, 0600)) {
+ pr_perror("mkdir %s", paux);
+ goto out_rs;
+ }
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ cgf = fopen("/proc/self/mountinfo", "r");
+ if (cgf == NULL) {
+ fail("No mountinfo file");
+ goto out_rs;
+ }
+
+ while (fgets(paux, sizeof(paux), cgf)) {
+ char *s;
+
+ s = strstr(paux, cgname);
+ if (!s)
+ continue;
+
+ sscanf(paux, "%*d %*d %*d:%*d %*s %s", aux);
+ test_msg("found cgroup at %s\n", aux);
+
+ for (i = 0; i < 2; i++) {
+ sprintf(paux, "%s/%s/%s.%d", aux, subname, empty, i);
+ if (stat(paux, &st)) {
+ fail("couldn't stat %s\n", paux);
+ ret = -1;
+ goto out_close;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ fail("%s is not a directory\n", paux);
+ ret = -1;
+ goto out_close;
+ }
+ }
+
+ pass();
+ ret = 0;
+ goto out_close;
+ }
+
+ fail("empty cgroup not found!\n");
+
+out_close:
+ fclose(cgf);
+out_rs:
+ umount(dirname);
+out_rd:
+ rmdir(dirname);
+out:
+ return ret;
+}
diff --git a/test/zdtm/static/cgroup01.desc b/test/zdtm/static/cgroup01.desc
new file mode 100644
index 000000000..3c6c4a7e2
--- /dev/null
+++ b/test/zdtm/static/cgroup01.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'flags': 'suid', 'opts': '--manage-cgroups'}
diff --git a/test/zdtm/static/cgroup01.hook b/test/zdtm/static/cgroup01.hook
new file mode 100755
index 000000000..d2eacbbdf
--- /dev/null
+++ b/test/zdtm/static/cgroup01.hook
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+[ "$1" == "--clean" -o "$1" == "--pre-restore" ] || exit 0
+
+set -e
+
+tname=$(mktemp -d cgclean.XXXXXX)
+mount -t cgroup none $tname -o "none,name=zdtmtst"
+
+echo "Cleaning $tname"
+
+set +e
+rmdir "$tname/subcg01/empty.0/"
+rmdir "$tname/subcg01/empty.1/"
+rmdir "$tname/subcg01/"
+set -e
+
+echo "Left there is:"
+ls "$tname"
+umount "$tname"
+rmdir "$tname"
diff --git a/test/zdtm/static/cgroup01.opts b/test/zdtm/static/cgroup01.opts
new file mode 100644
index 000000000..998cb32c7
--- /dev/null
+++ b/test/zdtm/static/cgroup01.opts
@@ -0,0 +1 @@
+--manage-cgroups
diff --git a/test/zdtm/static/cgroup02.c b/test/zdtm/static/cgroup02.c
new file mode 100644
index 000000000..d892b2f86
--- /dev/null
+++ b/test/zdtm/static/cgroup02.c
@@ -0,0 +1,157 @@
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that empty cgroups are preserved";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "cgroup directory name", 1);
+static const char *cgname = "zdtmtst";
+static const char *subname = "oldroot";
+static const char *cgname2 = "zdtmtst.defaultroot";
+
+int mount_and_add(const char *controller, const char *path)
+{
+ char aux[1024], paux[1024], subdir[1024];
+ int cgfd, l;
+
+ if (mkdir(dirname, 0700) < 0 && errno != EEXIST) {
+ pr_perror("Can't make dir");
+ return -1;
+ }
+
+ sprintf(subdir, "%s/%s", dirname, controller);
+ if (mkdir(subdir, 0700) < 0) {
+ pr_perror("Can't make dir");
+ return -1;
+ }
+
+ sprintf(aux, "none,name=%s", controller);
+ if (mount("none", subdir, "cgroup", 0, aux)) {
+ pr_perror("Can't mount cgroups");
+ goto err_rd;
+ }
+
+ sprintf(paux, "%s/%s", subdir, path);
+ mkdir(paux, 0600);
+
+ l = sprintf(aux, "%d", getpid());
+ sprintf(paux, "%s/%s/tasks", subdir, path);
+
+ cgfd = open(paux, O_WRONLY);
+ if (cgfd < 0) {
+ pr_perror("Can't open tasks");
+ goto err_rs;
+ }
+
+ l = write(cgfd, aux, l);
+ close(cgfd);
+
+ if (l < 0) {
+ pr_perror("Can't move self to subcg");
+ goto err_rs;
+ }
+
+ return 0;
+err_rs:
+ umount(dirname);
+err_rd:
+ rmdir(dirname);
+ return -1;
+}
+
+bool test_exists(char *mountinfo_line, char *path)
+{
+ char aux[1024], paux[1024];
+ struct stat st;
+
+ sscanf(mountinfo_line, "%*d %*d %*d:%*d %*s %s", aux);
+ test_msg("found cgroup at %s\n", aux);
+
+ sprintf(paux, "%s/%s", aux, path);
+ if (stat(paux, &st)) {
+ return false;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ FILE *cgf;
+ bool found_zdtmtstroot = false, found_newroot = false;
+ char paux[1024];
+ int ret = -1;
+
+ test_init(argc, argv);
+
+ if (mount_and_add(cgname, subname))
+ goto out;
+ if (mount_and_add(cgname2, subname)) {
+ sprintf(paux, "%s/%s", dirname, cgname);
+ umount(paux);
+ rmdir(paux);
+ goto out;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ cgf = fopen("/proc/self/mountinfo", "r");
+ if (cgf == NULL) {
+ fail("No mountinfo file");
+ goto out_umount;
+ }
+
+ while (fgets(paux, sizeof(paux), cgf)) {
+ char *s;
+
+ s = strstr(paux, cgname);
+ if (s && test_exists(paux, "zdtmtstroot")) {
+ found_zdtmtstroot = true;
+ }
+
+ s = strstr(paux, cgname2);
+ if (s && test_exists(paux, "newroot")) {
+ found_newroot = true;
+ }
+ }
+
+ if (!found_zdtmtstroot) {
+ fail("oldroot not rewritten to zdtmtstroot!\n");
+ goto out_close;
+ }
+
+ if (!found_newroot) {
+ fail("oldroot not rewritten to newroot!\n");
+ goto out_close;
+ }
+
+ pass();
+ ret = 0;
+
+
+out_close:
+ fclose(cgf);
+out_umount:
+ sprintf(paux, "%s/%s", dirname, cgname);
+ umount(paux);
+ rmdir(paux);
+
+ sprintf(paux, "%s/%s", dirname, cgname2);
+ umount(paux);
+ rmdir(paux);
+out:
+ return ret;
+}
diff --git a/test/zdtm/static/cgroup02.desc b/test/zdtm/static/cgroup02.desc
new file mode 100644
index 000000000..4999d91b6
--- /dev/null
+++ b/test/zdtm/static/cgroup02.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'flags': 'suid', 'opts': '--manage-cgroups --cgroup-root /newroot --cgroup-root name=zdtmtst:/zdtmtstroot'}
diff --git a/test/zdtm/static/cgroup02.hook b/test/zdtm/static/cgroup02.hook
new file mode 100755
index 000000000..3214552a8
--- /dev/null
+++ b/test/zdtm/static/cgroup02.hook
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+[ "$1" == "--clean" -o "$1" == "--pre-restore" ] || exit 0
+
+set -e
+
+rmroots() {
+ echo "Cleaning $tname ($1)"
+
+ mount -t cgroup none $tname -o "$1"
+
+ for d in "$tname/oldroot" "$tname/newroot" "$tname/zdtmtstroot"; do
+ test -d "$d" || continue
+ # sort by line length
+ for i in `find $d -type d | awk '{print length, $0}' | sort -rn | cut -d " " -f2-`; do
+ echo $i
+ rmdir $i
+ done
+ done
+
+ echo "Left there is:"
+ ls "$tname"
+ umount "$tname"
+}
+
+tname=$(mktemp -d cgclean.XXXXXX)
+
+for ctl in $(cat /proc/self/cgroup | cut -d: -f2); do
+ rmroots "$ctl"
+done
+
+rmdir $tname
diff --git a/test/zdtm/static/cgroup02.opts b/test/zdtm/static/cgroup02.opts
new file mode 100644
index 000000000..159938e0a
--- /dev/null
+++ b/test/zdtm/static/cgroup02.opts
@@ -0,0 +1 @@
+--manage-cgroups --cgroup-root /newroot --cgroup-root name=zdtmtst:/zdtmtstroot
diff --git a/test/zdtm/static/cgroup03.c b/test/zdtm/static/cgroup03.c
new file mode 100644
index 000000000..c6c4938ba
--- /dev/null
+++ b/test/zdtm/static/cgroup03.c
@@ -0,0 +1,171 @@
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <limits.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that global cgroup settings (+perms) are restored";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "cgroup directory name", 1);
+static const char *cgname = "zdtmtst";
+
+int mount_and_add(const char *controller, const char *path)
+{
+ char aux[1024], paux[1024], subdir[1024];
+ int cgfd, l;
+
+ if (mkdir(dirname, 0700) < 0 && errno != EEXIST) {
+ pr_perror("Can't make dir");
+ return -1;
+ }
+
+ sprintf(subdir, "%s/%s", dirname, controller);
+ if (mkdir(subdir, 0700) < 0) {
+ pr_perror("Can't make dir");
+ return -1;
+ }
+
+ sprintf(aux, "none,name=%s", controller);
+ if (mount("none", subdir, "cgroup", 0, aux)) {
+ pr_perror("Can't mount cgroups");
+ goto err_rd;
+ }
+
+ sprintf(paux, "%s/%s", subdir, path);
+ mkdir(paux, 0600);
+
+ l = sprintf(aux, "%d", getpid());
+ sprintf(paux, "%s/%s/tasks", subdir, path);
+
+ cgfd = open(paux, O_WRONLY);
+ if (cgfd < 0) {
+ pr_perror("Can't open tasks");
+ goto err_rs;
+ }
+
+ l = write(cgfd, aux, l);
+ close(cgfd);
+
+ if (l < 0) {
+ pr_perror("Can't move self to subcg");
+ goto err_rs;
+ }
+
+ return 0;
+err_rs:
+ umount(dirname);
+err_rd:
+ rmdir(dirname);
+ return -1;
+}
+
+int chownmod(char *path, int flags)
+{
+ int fd, ret = -1;
+
+ fd = open(path, flags);
+ if (fd < 0) {
+ pr_perror("can't open %s", path);
+ return -1;
+ }
+
+ if (fchown(fd, 1000, 1000) < 0) {
+ pr_perror("can't chown %s", path);
+ goto out;
+ }
+
+ if (fchmod(fd, 0777) < 0) {
+ pr_perror("can't chmod %s", path);
+ goto out;
+ }
+
+ ret = 0;
+out:
+ close(fd);
+ return ret;
+}
+
+int checkperms(char *path)
+{
+ struct stat sb;
+
+ if (stat(path, &sb) < 0) {
+ pr_perror("can't stat %s", path);
+ return -1;
+ }
+
+ if ((sb.st_mode & 0777) != 0777) {
+ fail("mode for %s doesn't match (%o)\n", path, sb.st_mode);
+ return -1;
+ }
+
+ if (sb.st_uid != 1000) {
+ fail("uid for %s doesn't match (%d)\n", path, sb.st_uid);
+ return -1;
+ }
+
+ if (sb.st_gid != 1000) {
+ fail("gid for %s doesn't match (%d)\n", path, sb.st_gid);
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int ret = -1;
+ char path[PATH_MAX];
+
+ test_init(argc, argv);
+
+ if (mount_and_add(cgname, "test") < 0)
+ return -1;
+
+ sprintf(path, "%s/%s/test", dirname, cgname);
+ if (chownmod(path, O_DIRECTORY) < 0)
+ goto out_umount;
+
+ sprintf(path, "%s/%s/test/notify_on_release", dirname, cgname);
+ if (chownmod(path, O_RDWR) < 0)
+ goto out_umount;
+
+
+ sprintf(path, "%s/%s/test/cgroup.procs", dirname, cgname);
+ if (chownmod(path, O_RDWR) < 0)
+ goto out_umount;
+
+ test_daemon();
+ test_waitsig();
+
+ sprintf(path, "%s/%s/test", dirname, cgname);
+ if (checkperms(path) < 0)
+ goto out_umount;
+
+ sprintf(path, "%s/%s/test/notify_on_release", dirname, cgname);
+ if (checkperms(path) < 0)
+ goto out_umount;
+
+ sprintf(path, "%s/%s/test/cgroup.procs", dirname, cgname);
+ if (checkperms(path) < 0)
+ goto out_umount;
+
+ pass();
+ ret = 0;
+
+out_umount:
+ sprintf(path, "%s/%s/test", dirname, cgname);
+ rmdir(path);
+ sprintf(path, "%s/%s", dirname, cgname);
+ umount(path);
+ rmdir(path);
+ rmdir(dirname);
+ return ret;
+}
diff --git a/test/zdtm/static/cgroup03.desc b/test/zdtm/static/cgroup03.desc
new file mode 100644
index 000000000..3c6c4a7e2
--- /dev/null
+++ b/test/zdtm/static/cgroup03.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'flags': 'suid', 'opts': '--manage-cgroups'}
diff --git a/test/zdtm/static/cgroup03.hook b/test/zdtm/static/cgroup03.hook
new file mode 100755
index 000000000..6f1102255
--- /dev/null
+++ b/test/zdtm/static/cgroup03.hook
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+[ "$1" == "--clean" -o "$1" == "--pre-restore" ] || exit 0
+
+tname=$(mktemp -d cgclean.XXXXXX)
+mount -t cgroup none $tname -o "none,name=zdtmtst"
+
+echo "Cleaning $tname"
+set +e
+rmdir "$tname/test"
+set -e
+
+umount "$tname"
+rmdir "$tname"
diff --git a/test/zdtm/static/child_opened_proc.c b/test/zdtm/static/child_opened_proc.c
new file mode 100644
index 000000000..2a1fa8c79
--- /dev/null
+++ b/test/zdtm/static/child_opened_proc.c
@@ -0,0 +1,63 @@
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that tree prior to files opening";
+const char *test_author = "Stanislav Kinsbursky <skinsbursky@paralles.com";
+
+int main(int argc, char ** argv)
+{
+ int pid, err = 0;
+ int proc_fd;
+ char name[64];
+
+ test_init(argc, argv);
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("Can't fork");
+ exit(1);
+ }
+
+ if (!pid) {
+ test_waitsig();
+ return 0;
+ }
+
+ sprintf(name, "/proc/%d/stat", pid);
+ proc_fd = open(name, O_RDONLY);
+ if (proc_fd == -1) {
+ pr_perror("can't open %s", name);
+ err++;
+ goto out;
+ }
+ test_daemon();
+ test_waitsig();
+
+ if (close(proc_fd) == -1) {
+ pr_perror("Failed to close %s", name);
+ err++;
+ }
+out:
+ if (kill(pid, SIGTERM) == -1) {
+ pr_perror("Failed to terminate child");
+ err++;
+ } else {
+ if (waitpid(pid, NULL, 0) != pid) {
+ pr_perror("Failed to collect killed child");
+ err++;
+ }
+ }
+
+ if (!err)
+ pass();
+
+ return err;
+}
diff --git a/test/zdtm/static/chroot-file.c b/test/zdtm/static/chroot-file.c
new file mode 100644
index 000000000..94f584213
--- /dev/null
+++ b/test/zdtm/static/chroot-file.c
@@ -0,0 +1,166 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that out-of-root file survives";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+#define MSG "out-file-contents"
+
+static int make_file(char *name)
+{
+ int fd;
+
+ fd = open(name, O_RDWR | O_CREAT, 0666);
+ if (fd < 0)
+ return -1;
+
+ if (write(fd, MSG, sizeof(MSG)) != sizeof(MSG))
+ return -1;
+
+ return fd;
+}
+
+static int check_file(int fd)
+{
+ char r[sizeof(MSG)];
+
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, r, sizeof(r)) != sizeof(MSG))
+ return -1;
+
+ if (memcmp(r, MSG, sizeof(MSG)))
+ return -1;
+
+ return 0;
+}
+
+#define SUCCESS 0
+#define ERR_PIPES (char)0x7f
+/* bitmap of errors */
+#define ERR_IN_FILE 1
+#define ERR_ROOT 2
+#define ERR_DIR 4
+#define ERR_CHDIR 8
+#define ERR_ROOT2 4
+
+int main(int argc, char **argv)
+{
+ int pid, pipe_prep[2], pipe_goon[2], pipe_res[2];
+ char res;
+ int fd, fd2;
+
+ test_init(argc, argv);
+
+ pipe(pipe_prep);
+ pipe(pipe_goon);
+ pipe(pipe_res);
+ pid = test_fork();
+ if (pid != 0) {
+ close(pipe_prep[1]);
+ close(pipe_goon[0]);
+ close(pipe_res[1]);
+
+ res = ERR_PIPES;
+ read(pipe_prep[0], &res, 1);
+ read(pipe_prep[0], &res, 1); /* wait when a descriptor will be closed */
+ if (res != SUCCESS) {
+ if (res == ERR_PIPES)
+ pr_perror("broken pipes");
+ else {
+ if (res & ERR_IN_FILE)
+ pr_perror("inside-root file fail");
+ if (res & ERR_ROOT)
+ pr_perror("chroot fail");
+ if (res & ERR_DIR)
+ pr_perror("mkdir fail");
+ if (res & ERR_CHDIR)
+ pr_perror("chrid fail");
+ }
+ return 0;
+ }
+
+ test_daemon();
+ test_waitsig();
+ close(pipe_goon[1]);
+
+ res = ERR_PIPES;
+ read(pipe_res[0], &res, 1);
+
+ if (res == SUCCESS)
+ pass();
+ else if (res == ERR_PIPES)
+ fail("broken pipes");
+ else {
+ if (res & ERR_IN_FILE)
+ fail("opened file broken");
+ if (res & ERR_ROOT)
+ fail("open in chroot succeeded");
+ if (res & ERR_ROOT2)
+ fail("open in chroot might work");
+ }
+
+ wait(NULL);
+ return 0;
+ }
+
+ close(pipe_prep[0]);
+ close(pipe_goon[1]);
+ close(pipe_res[0]);
+
+ fd = make_file(filename);
+ if (fd < 0) {
+ res = ERR_IN_FILE;
+ goto err;
+ }
+
+ if (mkdir(dirname, 0700)) {
+ res = ERR_DIR;
+ goto err;
+ }
+
+ if (chroot(dirname)) {
+ res = ERR_ROOT;
+ goto err;
+ }
+
+ if (chdir("/")) {
+ res = ERR_CHDIR;
+ goto err;
+ }
+
+ res = SUCCESS;
+ write(pipe_prep[1], &res, 1);
+ close(pipe_prep[1]);
+ read(pipe_goon[0], &res, 1);
+
+ res = SUCCESS;
+
+ if (check_file(fd))
+ res |= ERR_IN_FILE;
+
+ fd2 = open(filename, O_RDWR);
+ if (fd2 >= 0) {
+ res |= ERR_ROOT;
+ close(fd2);
+ } else if (errno != ENOENT)
+ res |= ERR_ROOT2;
+
+ write(pipe_res[1], &res, 1);
+ exit(0);
+
+err:
+ write(pipe_prep[1], &res, 1);
+ exit(0);
+}
diff --git a/test/zdtm/static/chroot-file.desc b/test/zdtm/static/chroot-file.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/chroot-file.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/chroot.c b/test/zdtm/static/chroot.c
new file mode 100644
index 000000000..39b1ace0c
--- /dev/null
+++ b/test/zdtm/static/chroot.c
@@ -0,0 +1,164 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that root didn't change";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+static char *filepath;
+
+#define MSG "chroot-file-contents"
+
+static int make_file(char *name)
+{
+ int fd;
+
+ fd = open(name, O_RDWR | O_CREAT, 0666);
+ if (fd < 0)
+ return -1;
+
+ if (write(fd, MSG, sizeof(MSG)) != sizeof(MSG))
+ return -1;
+
+ return fd;
+}
+
+static int check_file(int fd)
+{
+ char r[sizeof(MSG)];
+
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, r, sizeof(r)) != sizeof(MSG))
+ return -1;
+
+ if (memcmp(r, MSG, sizeof(MSG)))
+ return -1;
+
+ return 0;
+}
+
+#define SUCCESS 0
+#define ERR_PIPES (char)0x7f
+/* bitmap of errors */
+#define ERR_IN_FILE 1
+#define ERR_ROOT 2
+#define ERR_DIR 4
+#define ERR_OPEN 2
+#define ERR_FILE2 4
+
+int main(int argc, char **argv)
+{
+ int pid, pipe_prep[2], pipe_goon[2], pipe_res[2];
+ char res;
+ int fd, fd2;
+
+ test_init(argc, argv);
+
+ filepath = malloc(strlen(filename) + 1);
+ sprintf(filepath, "/%s", filename);
+
+ pipe(pipe_prep);
+ pipe(pipe_goon);
+ pipe(pipe_res);
+ pid = test_fork();
+ if (pid != 0) {
+ close(pipe_prep[1]);
+ close(pipe_goon[0]);
+ close(pipe_res[1]);
+
+ res = ERR_PIPES;
+ read(pipe_prep[0], &res, 1);
+ read(pipe_prep[0], &res, 1); /* wait when pipe_prep[] will be closed */
+ if (res != SUCCESS) {
+ if (res == ERR_PIPES)
+ pr_perror("broken pipes");
+ else {
+ if (res & ERR_IN_FILE)
+ pr_perror("inside-root file fail");
+ if (res & ERR_ROOT)
+ pr_perror("chroot fail");
+ if (res & ERR_DIR)
+ pr_perror("mkdir fail");
+ }
+ return 0;
+ }
+
+ test_daemon();
+ test_waitsig();
+ close(pipe_goon[1]);
+
+ res = ERR_PIPES;
+ read(pipe_res[0], &res, 1);
+
+ if (res == SUCCESS)
+ pass();
+ else if (res == ERR_PIPES)
+ fail("broken pipes");
+ else {
+ if (res & ERR_IN_FILE)
+ fail("opened file broken");
+ if (res & ERR_OPEN)
+ fail("open in chroot fail");
+ if (res & ERR_FILE2)
+ fail("wrong file opened");
+ }
+
+ wait(NULL);
+ return 0;
+ }
+
+ close(pipe_prep[0]);
+ close(pipe_goon[1]);
+ close(pipe_res[0]);
+
+ if (mkdir(dirname, 0700)) {
+ res = ERR_DIR;
+ goto err_nodir;
+ }
+
+ if (chroot(dirname)) {
+ res = ERR_ROOT;
+ goto err_noroot;
+ }
+
+ fd = make_file(filepath);
+ if (fd < 0) {
+ res = ERR_IN_FILE;
+ goto err_nofile2;
+ }
+
+ res = SUCCESS;
+ write(pipe_prep[1], &res, 1);
+ close(pipe_prep[1]);
+ read(pipe_goon[0], &res, 1);
+
+ res = SUCCESS;
+
+ if (check_file(fd))
+ res |= ERR_IN_FILE;
+
+ fd2 = open(filepath, O_RDWR);
+ if (fd2 < 0)
+ res |= ERR_OPEN;
+ else if (check_file(fd2))
+ res |= ERR_FILE2;
+
+ write(pipe_res[1], &res, 1);
+ exit(0);
+
+err_nofile2:
+err_noroot:
+err_nodir:
+ write(pipe_prep[1], &res, 1);
+ exit(0);
+}
diff --git a/test/zdtm/static/chroot.desc b/test/zdtm/static/chroot.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/chroot.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/clean_mntns.c b/test/zdtm/static/clean_mntns.c
new file mode 100644
index 000000000..762894611
--- /dev/null
+++ b/test/zdtm/static/clean_mntns.c
@@ -0,0 +1,25 @@
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mount.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that clean mntns works";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+int main(int argc, char **argv)
+{
+ test_init(argc, argv);
+
+ if (umount("/proc") < 0)
+ pr_perror("Can't umount proc");
+
+ if (umount("/dev/pts") < 0)
+ pr_perror("Can't umount devpts");
+
+ test_daemon();
+ test_waitsig();
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/clean_mntns.desc b/test/zdtm/static/clean_mntns.desc
new file mode 100644
index 000000000..dfe829b84
--- /dev/null
+++ b/test/zdtm/static/clean_mntns.desc
@@ -0,0 +1 @@
+{'flavor': 'ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/cmdlinenv00.c b/test/zdtm/static/cmdlinenv00.c
new file mode 100644
index 000000000..9d3fe6c16
--- /dev/null
+++ b/test/zdtm/static/cmdlinenv00.c
@@ -0,0 +1,123 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test that env/cmdline/auxv restored well\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org";
+
+static char *arg1, *arg2, *arg3;
+
+TEST_OPTION(arg1, string, "arg1", 1);
+TEST_OPTION(arg2, string, "arg2", 1);
+TEST_OPTION(arg3, string, "arg3", 1);
+
+static void read_from_proc(const char *path, char *buf, size_t size)
+{
+ size_t r = 0, ret;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fail("Can't open cmdline\n");
+ exit(1);
+ }
+
+ while (r < size) {
+ ret = read(fd, buf + r, size - r);
+ if (ret < 0) {
+ pr_perror("Read failed");
+ exit(1);
+ } else if (ret == 0) {
+ break;
+ }
+
+ r += ret;
+ }
+ close(fd);
+}
+
+static int cmp_auxv(const void *auxv_orig, const void *auxv, size_t size)
+{
+ const unsigned long long *new = auxv;
+ const unsigned int *old = auxv_orig;
+
+ if (!memcmp(auxv_orig, auxv, size))
+ return 0;
+
+ /*
+ * File /proc/$pid/auxv does not has compat layer, this "array of long"
+ * has different byte-representation between 32-bit and 64-bit host.
+ * We can migrate tasks only in one direction, thus check is simple.
+ */
+ while (size > 0) {
+ if (*new != *old)
+ return -1;
+ new++;
+ old++;
+ size -= sizeof(*new);
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char cmdline_orig[4096];
+ char cmdline[4096];
+ char env_orig[4096];
+ char env[4096];
+ char auxv_orig[1024];
+ char auxv[1024];
+
+ memset(cmdline_orig, 0, sizeof(cmdline_orig));
+ memset(cmdline, 0, sizeof(cmdline));
+ memset(env_orig, 0, sizeof(env_orig));
+ memset(env, 0, sizeof(env));
+ memset(auxv_orig, 0, sizeof(auxv_orig));
+ memset(auxv, 0, sizeof(auxv));
+
+ test_init(argc, argv);
+
+ read_from_proc("/proc/self/cmdline", cmdline_orig, sizeof(cmdline_orig));
+ read_from_proc("/proc/self/environ", env_orig, sizeof(env_orig));
+ read_from_proc("/proc/self/auxv", auxv_orig, sizeof(auxv_orig));
+
+ test_msg("old cmdline: %s\n", cmdline_orig);
+ test_msg("old environ: %s\n", env_orig);
+
+ test_daemon();
+ test_waitsig();
+
+ read_from_proc("/proc/self/cmdline", cmdline, sizeof(cmdline));
+ read_from_proc("/proc/self/environ", env, sizeof(env));
+ read_from_proc("/proc/self/auxv", auxv, sizeof(auxv));
+
+ test_msg("new cmdline: %s\n", cmdline);
+ test_msg("new environ: %s\n", env);
+
+ if (strncmp(cmdline_orig, cmdline, sizeof(cmdline_orig))) {
+ fail("cmdline corrupted on restore");
+ exit(1);
+ }
+
+ if (strncmp(env_orig, env, sizeof(env_orig))) {
+ fail("envirion corrupted on restore");
+ exit(1);
+ }
+
+ if (cmp_auxv(auxv_orig, auxv, sizeof(auxv_orig))) {
+ fail("auxv corrupted on restore");
+ exit(1);
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/cmdlinenv00.desc b/test/zdtm/static/cmdlinenv00.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/cmdlinenv00.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/conntracks b/test/zdtm/static/conntracks
new file mode 100755
index 000000000..bb2950ca9
--- /dev/null
+++ b/test/zdtm/static/conntracks
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+
+export PATH=$PATH:${0%/*}/../../lib
+
+die()
+{
+ echo "$0:${BASH_LINENO[0]}: $*" >&2
+ exit 1
+}
+
+fail()
+{
+ echo "FAIL: $0:${BASH_LINENO[0]}: $*" > "$outfile"
+ exit 1
+}
+
+do_or_fail()
+{
+ local failmsg="$1" output
+ shift
+ output="$(eval $@ 2>&1)" ||
+ fail "$failmsg: $output"
+}
+
+do_start()
+{
+ [ -f "$statefile" ] && die "state file $statefile aleady exists"
+
+ do_or_fail "can't install a state match" \
+ iptables -A INPUT \
+ -m state --state RELATED,ESTABLISHED -j ACCEPT
+
+ do_or_fail "can't list the loaded iptables" \
+ iptables -L \> "$statefile"
+}
+
+do_stop()
+{
+ do_or_fail "can't compare the iptables" \
+ iptables -L \| diff -u "$statefile" -
+
+ rm -f "$statefile"
+
+ echo "PASS" > $outfile
+}
+
+
+tmpargs="$(parseargs.sh --name=$0 \
+ --flags-req=statefile,outfile \
+ --flags-opt="start,stop" -- "$@")" ||
+ die "can't parse command line"
+eval "$tmpargs"
+
+[ -f "$outfile" ] && die "out file $outfile aleady exists"
+
+# expect "start" or "stop"
+do_$1
diff --git a/test/zdtm/static/conntracks.desc b/test/zdtm/static/conntracks.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/conntracks.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/console.c b/test/zdtm/static/console.c
new file mode 100644
index 000000000..a86b14639
--- /dev/null
+++ b/test/zdtm/static/console.c
@@ -0,0 +1,60 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check c/r for console device";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char ** argv)
+{
+ struct stat st1, st2;
+ int fd;
+
+ test_init(argc, argv);
+
+ if (mknod(filename, S_IFCHR | S_IRUSR | S_IWUSR, makedev(5,1))) {
+ pr_perror("Can't create console %s", filename);
+ return 1;
+ }
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("Open console %s failed", filename);
+ return 1;
+ }
+
+ if (fstat(fd, &st1)) {
+ pr_perror("Can't stat %s console", filename);
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (fstat(fd, &st2)) {
+ pr_perror("Can't stat %s console", filename);
+ return 1;
+ }
+
+ if (st1.st_rdev != st2.st_rdev) {
+ fail("Console rdev mismatch %x != %x on %s",
+ (int)st1.st_rdev, (int)st2.st_rdev,
+ filename);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/console.desc b/test/zdtm/static/console.desc
new file mode 100644
index 000000000..d969725f6
--- /dev/null
+++ b/test/zdtm/static/console.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/cow00.c b/test/zdtm/static/cow00.c
new file mode 100644
index 000000000..92446a16c
--- /dev/null
+++ b/test/zdtm/static/cow00.c
@@ -0,0 +1,113 @@
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <linux/limits.h>
+#include <sys/user.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that cow memory are restored";
+const char *test_author = "Andrey Vagin <avagin@parallels.com";
+
+static int is_cow(void *addr, pid_t p1, pid_t p2)
+{
+ char buf[PATH_MAX];
+ unsigned long pfn = (unsigned long) addr / PAGE_SIZE;
+ uint64_t map1, map2;
+ int fd1, fd2, ret, i;
+
+ snprintf(buf, sizeof(buf), "/proc/%d/pagemap", p1);
+ fd1 = open(buf, O_RDONLY);
+ if (fd1 < 0) {
+ pr_perror("Unable to open file %s", buf);
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "/proc/%d/pagemap", p2);
+ fd2 = open(buf, O_RDONLY);
+ if (fd1 < 0) {
+ pr_perror("Unable to open file %s", buf);
+ return -1;
+ }
+
+ /*
+ * A page can be swapped or unswapped,
+ * so we should do several iterations.
+ */
+ for (i = 0; i < 10; i++) {
+ lseek(fd1, pfn * sizeof(map1), SEEK_SET);
+ lseek(fd2, pfn * sizeof(map2), SEEK_SET);
+
+ ret = read(fd1, &map1, sizeof(map1));
+ if (ret != sizeof(map1)) {
+ pr_perror("Unable to read data");
+ return -1;
+ }
+ ret = read(fd2, &map2, sizeof(map2));
+ if (ret != sizeof(map2)) {
+ pr_perror("Unable to read data");
+ return -1;
+ }
+
+ if (map1 == map2)
+ break;
+ }
+
+ close(fd1);
+ close(fd2);
+
+ return map1 == map2;
+}
+
+int main(int argc, char ** argv)
+{
+ void *addr;
+ pid_t pid;
+ int ret = 1;
+
+ test_init(argc, argv);
+
+ addr = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (addr == MAP_FAILED) {
+ pr_perror("Can't allocate memory");
+ return 1;
+ }
+
+ memset(addr, 1, PAGE_SIZE);
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("Unable to fork a new process");
+ return 1;
+ } else if (pid == 0) {
+ test_waitsig();
+ return 0;
+ }
+
+ if (is_cow(addr, pid, getpid()) == 1)
+ test_msg("OK\n");
+ else {
+ pr_perror("A page is not shared");
+ goto out;
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ if (is_cow(addr, pid, getpid()) == 1)
+ pass();
+ else
+ fail("A page is not shared");
+
+ ret = 0;
+out:
+ kill(pid, SIGTERM);
+ wait(NULL);
+
+ return ret;
+}
diff --git a/test/zdtm/static/cow00.desc b/test/zdtm/static/cow00.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/cow00.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/cow01.c b/test/zdtm/static/cow01.c
new file mode 100644
index 000000000..895dd9005
--- /dev/null
+++ b/test/zdtm/static/cow01.c
@@ -0,0 +1,509 @@
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <linux/limits.h>
+#include <sys/user.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that cow memory are restored";
+const char *test_author = "Andrey Vagin <avagin@parallels.com";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+struct test_case {
+ union {
+ struct {
+ uint8_t b_f_write:1; /* before fork */
+ uint8_t b_f_read:1;
+ uint8_t a_f_write_child:1; /* after fork */
+ uint8_t a_f_write_parent:1;
+ uint8_t a_f_read_child:1;
+ uint8_t a_f_read_parent:1;
+#define TEST_CASES (2 << 6)
+ };
+ uint8_t num;
+ };
+
+ uint32_t crc_parent;
+ uint32_t crc_child;
+
+};
+
+struct test_cases {
+ struct test_case tc[TEST_CASES];
+ void *addr;
+ int (*init)(struct test_cases *tcs);
+ char *tname;
+};
+
+static int init_cow(struct test_cases *);
+static int init_cow_gd(struct test_cases *tcs);
+static int init_sep(struct test_cases *);
+static int init_file(struct test_cases *);
+
+static pid_t child_pid;
+
+/*
+ * A return code of -1 means an error running the test (e.g. error opening a
+ * file, etc.). A return code of 1 means failure, it means criu was not able
+ * to checkpoint and/or restore the process properly.
+ */
+#define EXECUTE_ACTION(func, fd) ({ \
+ int __ret = 0; \
+ __ret |= func(&sep_tcs, fd); \
+ __ret |= func(&cow_tcs, fd); \
+ __ret |= func(&cow_gd_tcs, fd); \
+ __ret |= func(&file_tcs, fd); \
+ __ret; \
+})
+
+struct test_cases cow_tcs = {.init = init_cow, .tname = "cow_tcs"},
+ sep_tcs = {.init = init_sep, .tname = "sep_tcs"},
+ file_tcs = {.init = init_file, .tname = "file_tcs"},
+ cow_gd_tcs = {.init = init_cow_gd, .tname = "cow_gd_tcs"};
+
+uint32_t zero_crc = ~1;
+
+static int is_cow(void *addr, pid_t pid_child, pid_t pid_parent,
+ uint64_t *map_child_ret, uint64_t *map_parent_ret, int fd)
+{
+ char buf[PATH_MAX];
+ unsigned long pfn = (unsigned long) addr / PAGE_SIZE;
+ uint64_t map_child, map_parent;
+ int fd_child, fd_parent, ret, i;
+ off_t lseek_ret;
+
+ snprintf(buf, sizeof(buf), "/proc/%d/pagemap", pid_child);
+ fd_child = open(buf, O_RDONLY);
+ if (fd_child < 0) {
+ pr_perror("Unable to open child pagemap file %s", buf);
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "/proc/%d/pagemap", pid_parent);
+ fd_parent = open(buf, O_RDONLY);
+ if (fd_parent < 0) {
+ pr_perror("Unable to open parent pagemap file %s", buf);
+ return -1;
+ }
+
+ /*
+ * A page can be swapped or unswapped,
+ * so we should do several iterations.
+ */
+ for (i = 0; i < 10; i++) {
+ void **p = addr;
+
+ lseek_ret = lseek(fd_child, pfn * sizeof(map_child), SEEK_SET);
+ if (lseek_ret == (off_t) -1) {
+ pr_perror("Unable to seek child pagemap to virtual addr %#08lx",
+ pfn * PAGE_SIZE);
+ return -1;
+ }
+
+ lseek_ret = lseek(fd_parent, pfn * sizeof(map_parent), SEEK_SET);
+ if (lseek_ret == (off_t) -1) {
+ pr_perror("Unable to seek parent pagemap to virtual addr %#08lx",
+ pfn * PAGE_SIZE);
+ return -1;
+ }
+
+ ret = read(fd_child, &map_child, sizeof(map_child));
+ if (ret != sizeof(map_child)) {
+ pr_perror("Unable to read child pagemap at virtual addr %#08lx",
+ pfn * PAGE_SIZE);
+ return -1;
+ }
+
+ ret = read(fd_parent, &map_parent, sizeof(map_parent));
+ if (ret != sizeof(map_parent)) {
+ pr_perror("Unable to read parent pagemap at virtual addr %#08lx",
+ pfn * PAGE_SIZE);
+ return -1;
+ }
+
+ if (map_child == map_parent && i > 5)
+ break;
+
+ p = (void **)(addr + i * PAGE_SIZE);
+ test_msg("Read *%p = %p\n", p, p[0]);
+ if (write(fd, &p, sizeof(p)) != sizeof(p)) {
+ pr_perror("write");
+ return -1;
+ }
+ if (read(fd, &p, sizeof(p)) != sizeof(p)) {
+ pr_perror("read");
+ return -1;
+ }
+ test_msg("Child %p\n", p);
+ }
+
+ close(fd_child);
+ close(fd_parent);
+
+ if (map_child_ret)
+ *map_child_ret = map_child;
+ if (map_parent_ret)
+ *map_parent_ret = map_parent;
+
+ // Return 0 for success, 1 if the pages differ.
+ return map_child != map_parent;
+}
+
+static int child_prep(struct test_cases *test_cases, int fd)
+{
+ int i;
+ uint8_t *addr = test_cases->addr;
+
+ for (i = 0; i < TEST_CASES; i++) {
+ struct test_case *tc = test_cases->tc + i;
+ if (tc->a_f_write_child) {
+ tc->crc_child = ~1;
+ datagen2(addr + i * PAGE_SIZE, PAGE_SIZE, &tc->crc_child);
+ }
+ if (tc->a_f_read_child) {
+ uint32_t crc = ~1;
+
+ datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc);
+ }
+ }
+
+ return 0;
+}
+
+static int child_check(struct test_cases *test_cases, int fd)
+{
+ int i, ret = 0;
+ uint8_t *addr = test_cases->addr;
+
+ for (i = 0; i < TEST_CASES; i++) {
+ uint32_t crc = ~1;
+ struct test_case *tc = test_cases->tc + i;
+
+ datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc);
+ if (crc != tc->crc_child) {
+ errno = 0;
+ fail("%s[%#x]: %p child data mismatch (expected [%04x] got [%04x])",
+ test_cases->tname, i, addr + i * PAGE_SIZE, tc->crc_child, crc);
+ ret |= 1;
+ }
+ }
+
+ return ret;
+}
+
+static int parent_before_fork(struct test_cases *test_cases, int fd)
+{
+ uint8_t *addr;
+ int i;
+
+ if (test_cases->init(test_cases))
+ return -1;
+
+ addr = test_cases->addr;
+
+ for (i = 0; i < TEST_CASES; i++) {
+ struct test_case *tc = test_cases->tc + i;
+ tc->num = i;
+
+ if (tc->b_f_write) {
+ tc->crc_parent = ~1;
+ datagen2(addr + i * PAGE_SIZE, PAGE_SIZE, &tc->crc_parent);
+ if (test_cases != &sep_tcs)
+ tc->crc_child = tc->crc_parent;
+ }
+ if (tc->b_f_read) {
+ uint32_t crc = ~1;
+
+ datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc);
+ }
+ }
+
+ return 0;
+}
+
+static int parent_post_fork(struct test_cases *test_cases, int fd)
+{
+ uint8_t *addr = test_cases->addr;
+ int i;
+
+ for (i = 0; i < TEST_CASES; i++) {
+ struct test_case *tc = test_cases->tc + i;
+
+ if (tc->a_f_write_parent) {
+ tc->crc_parent = ~1;
+ datagen2(addr + i * PAGE_SIZE, PAGE_SIZE, &tc->crc_parent);
+ }
+
+ if (tc->a_f_read_parent) {
+ uint32_t crc = ~1;
+
+ datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc);
+ }
+ }
+
+ return 0;
+}
+
+static int parent_check(struct test_cases *test_cases, int fd)
+{
+ uint8_t *addr = test_cases->addr;
+ int i, ret = 0;
+
+ for (i = 0; i < TEST_CASES; i++) {
+ struct test_case *tc = test_cases->tc + i;
+ uint32_t crc = ~1;
+
+ datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc);
+ if (crc != tc->crc_parent) {
+ errno = 0;
+ fail("%s[%#x]: %p parent data mismatch (expected [%04x] got [%04x])",
+ test_cases->tname, i, addr + i * PAGE_SIZE, tc->crc_parent, crc);
+ ret |= 1;
+ }
+
+ if (test_cases == &sep_tcs)
+ continue;
+
+ if (!tc->a_f_write_child &&
+ !tc->a_f_write_parent &&
+ tc->b_f_write) {
+ uint64_t map_child, map_parent;
+ int is_cow_ret;
+
+ is_cow_ret = is_cow(addr + i * PAGE_SIZE, child_pid, getpid(),
+ &map_child, &map_parent, fd);
+ ret |= is_cow_ret;
+ if (is_cow_ret == 1) {
+ errno = 0;
+ fail("%s[%#x]: %p is not COW-ed (pagemap of "
+ "child=[%#08lx], parent=[%#08lx])",
+ test_cases->tname, i, addr + i * PAGE_SIZE,
+ map_child, map_parent);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int __init_cow(struct test_cases *tcs, int flags)
+{
+ int i;
+ void *addr;
+
+ addr = mmap(NULL, PAGE_SIZE * (TEST_CASES + 2),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (addr == MAP_FAILED) {
+ pr_perror("Can't allocate memory");
+ return -1;
+ }
+
+ /*
+ * Guard pages are used for preventing merging with other vma-s.
+ * In parent cow-ed and coinciding regions can be merged, but
+ * in child they cannot be, so COW will not be restored. FIXME
+ */
+ mmap(addr, PAGE_SIZE, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ addr += PAGE_SIZE;
+ tcs->addr = addr;
+ mmap(addr + PAGE_SIZE * TEST_CASES, PAGE_SIZE, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | flags, -1, 0);
+
+ test_msg("addr[%s]=%p\n", tcs->tname, tcs->addr);
+ for (i = 0; i < TEST_CASES; i++) {
+ struct test_case *tc = tcs->tc + i;
+ tc->crc_parent = zero_crc;
+ tc->crc_child = zero_crc;
+ }
+
+ return 0;
+}
+
+static int init_cow(struct test_cases *tcs)
+{
+ return __init_cow(tcs, 0);
+}
+
+static int init_cow_gd(struct test_cases *tcs)
+{
+ return __init_cow(tcs, MAP_GROWSDOWN);
+}
+
+static int init_sep(struct test_cases *tcs)
+{
+ int i;
+
+ tcs->addr = mmap(NULL, PAGE_SIZE * TEST_CASES,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (tcs->addr == MAP_FAILED) {
+ pr_perror("Can't allocate memory");
+ return -1;
+ }
+
+ test_msg("addr[%s]=%p\n", tcs->tname, tcs->addr);
+ for (i = 0; i < TEST_CASES; i++) {
+ struct test_case *tc = tcs->tc + i;
+ tc->crc_parent = zero_crc;
+ tc->crc_child = zero_crc;
+ }
+
+ return 0;
+}
+
+static int init_file(struct test_cases *tcs)
+{
+ int i, ret, fd;
+ uint8_t buf[PAGE_SIZE];
+ uint32_t crc;
+
+ fd = open(filename, O_TRUNC | O_CREAT | O_RDWR, 0600);
+ if (fd < 0) {
+ pr_perror("Unable to create a test file");
+ return -1;
+ }
+
+ for (i = 0; i < TEST_CASES; i++) {
+ struct test_case *tc = tcs->tc + i;
+ crc = ~1;
+ datagen2(buf, sizeof(buf), &crc);
+ ret = write(fd, buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
+ pr_perror("Unable to write data in test file %s", filename);
+ return -1;
+ }
+
+ tc->crc_parent = crc;
+ tc->crc_child = crc;
+ }
+
+ tcs->addr = mmap(NULL, PAGE_SIZE * TEST_CASES,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_FILE, fd, 0);
+ if (tcs->addr == MAP_FAILED) {
+ pr_perror("Can't allocate memory");
+ return -1;
+ }
+
+ test_msg("addr[%s]=%p\n", tcs->tname, tcs->addr);
+ close(fd);
+
+ return 0;
+}
+
+static int child(task_waiter_t *child_waiter, int fd)
+{
+ int ret = 0;
+
+ sep_tcs.addr = mmap(sep_tcs.addr, PAGE_SIZE * TEST_CASES,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ if (sep_tcs.addr == MAP_FAILED) {
+ pr_perror("Can't allocate memory");
+ return -1;
+ }
+
+ EXECUTE_ACTION(child_prep, fd);
+
+ task_waiter_complete_current(child_waiter);
+
+ while (1) {
+ void **p;
+ ret = read(fd, &p, sizeof(p));
+ if (ret == 0)
+ break;
+ if (ret != sizeof(p)) {
+ pr_perror("read");
+ return -1;
+ }
+ test_msg("Read *%p = %p\n", p, p[0]);
+ p = ((void **)p)[0];
+ if (write(fd, &p, sizeof(p)) != sizeof(p)) {
+ pr_perror("write");
+ return -1;
+ }
+ ret = 0;
+ }
+
+ ret = EXECUTE_ACTION(child_check, fd);
+
+ // Exit code of child process, so return 2 for a test error, 1 for a
+ // test failure (child_check got mismatched checksums) and 0 for
+ // success.
+ return (ret < 0) ? 2 : (ret != 0);
+}
+
+int main(int argc, char ** argv)
+{
+ uint8_t zero_page[PAGE_SIZE];
+ int status = -1, ret = 0;
+ task_waiter_t child_waiter;
+ int pfd[2], fd;
+
+ task_waiter_init(&child_waiter);
+
+ memset(zero_page, 0, sizeof(zero_page));
+
+ datasum(zero_page, sizeof(zero_page), &zero_crc);
+
+ test_init(argc, argv);
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pfd)) {
+ pr_perror("pipe");
+ return 1;
+ }
+
+ if (EXECUTE_ACTION(parent_before_fork, -1))
+ return 2;
+
+ child_pid = test_fork();
+ if (child_pid < 0) {
+ pr_perror("Can't fork");
+ return 2;
+ }
+
+ if (child_pid == 0) {
+ close(pfd[0]);
+ return child(&child_waiter, pfd[1]);
+ }
+ close(pfd[1]);
+ fd = pfd[0];
+
+ task_waiter_wait4(&child_waiter, child_pid);
+
+ EXECUTE_ACTION(parent_post_fork, -1);
+
+ test_daemon();
+
+ test_waitsig();
+
+ ret |= EXECUTE_ACTION(parent_check, fd);
+
+ close(fd);
+ wait(&status);
+
+ unlink(filename);
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 2)
+ ret |= WEXITSTATUS(status);
+ else
+ ret |= -1;
+
+ if (ret == 0)
+ pass();
+
+ // Exit code, so return 2 for a test error, 1 for a test failure and 0
+ // for success.
+ return (ret < 0) ? 2 : (ret != 0);
+}
diff --git a/test/zdtm/static/cow01.desc b/test/zdtm/static/cow01.desc
new file mode 100644
index 000000000..d969725f6
--- /dev/null
+++ b/test/zdtm/static/cow01.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/criu-rtc.c b/test/zdtm/static/criu-rtc.c
new file mode 100644
index 000000000..0d6ffeac0
--- /dev/null
+++ b/test/zdtm/static/criu-rtc.c
@@ -0,0 +1,123 @@
+#include <stdio.h>
+#include <linux/rtc.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "criu-plugin.h"
+#include "criu-log.h"
+
+#include "criu-rtc.pb-c.h"
+
+extern cr_plugin_dump_file_t cr_plugin_dump_file;
+extern cr_plugin_restore_file_t cr_plugin_restore_file;
+
+int cr_plugin_dump_file(int fd, int id)
+{
+ CriuRtc e = CRIU_RTC__INIT;
+ char img_path[PATH_MAX];
+ unsigned char buf[4096];
+ int img_fd, ret, len;
+ unsigned long irqp;
+ struct stat st, st_rtc;
+
+ if (fstat(fd, &st) == -1) {
+ pr_perror("fstat");
+ return -1;
+ }
+
+ ret = stat("/dev/rtc", &st_rtc);
+ if (ret == -1) {
+ pr_perror("fstat");
+ return -1;
+ }
+
+ if (major(st.st_rdev) != major(st_rtc.st_rdev) ||
+ minor(st.st_rdev) != 0)
+ return -ENOTSUP;
+
+ if (ioctl(fd, RTC_IRQP_READ, &irqp) == -1) {
+ pr_perror("RTC_IRQP_READ");
+ return -1;
+ }
+
+ e.irqp = irqp;
+
+ snprintf(img_path, sizeof(img_path), "rtc.%x", id);
+ img_fd = openat(criu_get_image_dir(), img_path, O_WRONLY | O_CREAT);
+ if (img_fd < 0) {
+ pr_perror("Can't open %s", img_path);
+ return -1;
+ }
+
+ len = criu_rtc__get_packed_size(&e);
+ if (len > sizeof(buf))
+ return -1;
+
+ criu_rtc__pack(&e, buf);
+
+ ret = write(img_fd, buf, len);
+ if (ret != len) {
+ pr_perror("Unable to write in %s", img_path);
+ close(img_fd);
+ return -1;
+ }
+
+ close(img_fd);
+ return 0;
+}
+
+int cr_plugin_restore_file(int id)
+{
+ unsigned char buf[4096];
+ char img_path[PATH_MAX];
+ int img_fd, len, fd;
+ CriuRtc *e;
+
+ snprintf(img_path, sizeof(img_path), "rtc.%x", id);
+ img_fd = openat(criu_get_image_dir(), img_path, O_RDONLY);
+ if (img_fd < 0) {
+ pr_perror("open(%s)", img_path);
+ return -ENOTSUP;
+ }
+
+ len = read(img_fd, &buf, sizeof(buf));
+ if (len <= 0) {
+ pr_perror("Unable to read from %s", img_path);
+ close(img_fd);
+ return -1;
+ }
+ close(img_fd);
+
+ e = criu_rtc__unpack(NULL, len, buf);
+ if (e == NULL) {
+ pr_err("Unable to parse the RTC message %#x", id);
+ return -1;
+ }
+
+ fd = open("/dev/rtc", O_RDWR);
+ if (fd < 0) {
+ pr_perror("open");
+ return -1;
+ }
+
+ if (ioctl(fd, RTC_IRQP_SET, e->irqp) == -1) {
+ pr_perror("RTC_IRQP_SET");
+ close(fd);
+ return -1;
+ }
+
+ criu_rtc__free_unpacked(e, NULL);
+
+ if (ioctl(fd, RTC_PIE_ON, 0) == -1) {
+ pr_perror("RTC_PIE_ON");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/test/zdtm/static/criu-rtc.proto b/test/zdtm/static/criu-rtc.proto
new file mode 100644
index 000000000..5d5cee956
--- /dev/null
+++ b/test/zdtm/static/criu-rtc.proto
@@ -0,0 +1,3 @@
+message criu_rtc {
+ required uint64 IRQP = 1;
+}
diff --git a/test/zdtm/static/cwd00.c b/test/zdtm/static/cwd00.c
new file mode 100644
index 000000000..b0736f5e2
--- /dev/null
+++ b/test/zdtm/static/cwd00.c
@@ -0,0 +1,68 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that cwd didn't change";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char **argv)
+{
+ char cwd1[256], cwd2[256];
+ int fd;
+
+ test_init(argc, argv);
+
+ fd = open(".", O_DIRECTORY | O_RDONLY);
+ if (fd == -1) {
+ pr_perror("Unable to open the current dir");
+ exit(1);
+ }
+
+ if (mkdir(dirname, 0700)) {
+ pr_perror("can't make directory %s", dirname);
+ exit(1);
+ }
+
+ if (chdir(dirname)) {
+ pr_perror("can't change directory to %s", dirname);
+ goto cleanup;
+ }
+
+ if (!getcwd(cwd1, sizeof(cwd1))) {
+ pr_perror("can't get cwd");
+ goto cleanup;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (!getcwd(cwd2, sizeof(cwd2))) {
+ fail("can't get cwd: %m\n");
+ goto cleanup;
+ }
+
+ if (strcmp(cwd1, cwd2))
+ fail("%s != %s\n", cwd1, cwd2);
+ else
+ pass();
+cleanup:
+ /* return to the initial dir before writing out results */
+ if (fchdir(fd)) {
+ pr_perror("can't restore cwd");
+ exit(1);
+ }
+ if (rmdir(dirname)) {
+ pr_perror("can't remove directory %s", dirname);
+ exit(1);
+ }
+ return 0;
+}
diff --git a/test/zdtm/static/cwd01.c b/test/zdtm/static/cwd01.c
new file mode 100644
index 000000000..4e10a6b8b
--- /dev/null
+++ b/test/zdtm/static/cwd01.c
@@ -0,0 +1,101 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that removed cwd works";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char **argv)
+{
+ char cwd1[PATH_MAX], cwd2[PATH_MAX];
+ int pid, p[2], aux, aux2, fd;
+
+ test_init(argc, argv);
+
+ pipe(p);
+ pid = fork();
+ if (pid == 0) {
+ close(p[1]);
+ read(p[0], &aux, sizeof(aux));
+ aux = rmdir(dirname);
+ exit(aux ? 1 : 0);
+ }
+
+ fd = open(".", O_DIRECTORY | O_RDONLY);
+ if (fd == -1) {
+ pr_perror("Unable to open the current dir");
+ exit(1);
+ }
+
+ if (mkdir(dirname, 0700)) {
+ pr_perror("can't make directory %s", dirname);
+ exit(1);
+ }
+
+ if (chdir(dirname)) {
+ pr_perror("can't change directory to %s", dirname);
+ goto cleanup;
+ }
+
+ close(p[1]);
+ close(p[0]);
+ waitpid(pid, &aux, 0);
+ if (!WIFEXITED(aux) || WEXITSTATUS(aux) != 0) {
+ pr_perror("can't remove dir");
+ goto cleanup;
+ }
+
+ aux = readlink("/proc/self/cwd", cwd1, sizeof(cwd1));
+ if (aux < 0) {
+ pr_perror("can't get cwd");
+ goto cleanup;
+ }
+ if (aux == sizeof(cwd1)) {
+ pr_perror("A buffer is too small");
+ goto cleanup;
+ }
+
+ cwd1[aux] = '\0';
+
+ test_daemon();
+ test_waitsig();
+
+ aux2 = readlink("/proc/self/cwd", cwd2, sizeof(cwd2));
+ if (aux2 < 0) {
+ fail("can't get cwd: %m\n");
+ goto cleanup;
+ }
+ if (aux2 == sizeof(cwd2)) {
+ pr_perror("A buffer is too small");
+ goto cleanup;
+ }
+
+ cwd2[aux2] = '\0';
+
+ /* FIXME -- criu adds a suffix to removed cwd */
+ if (strncmp(cwd1, cwd2, aux))
+ fail("%s != %s\n", cwd1, cwd2);
+ else
+ pass();
+cleanup:
+ /* return to the initial dir before writing out results */
+ if (fchdir(fd)) {
+ pr_perror("can't restore cwd");
+ exit(1);
+ }
+
+ rmdir(dirname);
+ return 0;
+}
diff --git a/test/zdtm/static/cwd02.c b/test/zdtm/static/cwd02.c
new file mode 100644
index 000000000..123744567
--- /dev/null
+++ b/test/zdtm/static/cwd02.c
@@ -0,0 +1,92 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that removed and opened cwd are kept";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char **argv)
+{
+ int cwd, fd, pid, p[2], aux;
+ struct stat std, stf;
+
+ test_init(argc, argv);
+
+ pipe(p);
+ pid = fork();
+ if (pid == 0) {
+ close(p[1]);
+ read(p[0], &aux, sizeof(aux));
+ aux = rmdir(dirname);
+ exit(aux ? 1 : 0);
+ }
+
+ cwd = open(".", O_DIRECTORY | O_RDONLY);
+ if (cwd == -1) {
+ pr_perror("Unable to open the current dir");
+ exit(1);
+ }
+
+ if (mkdir(dirname, 0700)) {
+ pr_perror("can't make directory %s", dirname);
+ exit(1);
+ }
+
+ if ((fd = open(dirname, O_DIRECTORY)) < 0) {
+ pr_perror("can't open dir %s", dirname);
+ goto cleanup;
+ }
+
+ if (chdir(dirname)) {
+ pr_perror("can't change directory to %s", dirname);
+ goto cleanup;
+ }
+
+ close(p[1]);
+ close(p[0]);
+ waitpid(pid, &aux, 0);
+ if (!WIFEXITED(aux) || WEXITSTATUS(aux) != 0) {
+ pr_perror("can't remove dir");
+ goto cleanup;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (fstat(fd, &stf) < 0) {
+ fail("dir fd closed\n");
+ goto cleanup;
+ }
+
+ if (stat("/proc/self/cwd", &std) < 0) {
+ fail("cwd is not OK\n");
+ goto cleanup;
+ }
+
+ if (stf.st_ino != std.st_ino ||
+ stf.st_dev != stf.st_dev) {
+ fail("cwd and opened fd are not the same\n");
+ goto cleanup;
+ }
+
+ pass();
+
+cleanup:
+ /* return to the initial dir before writing out results */
+ if (fchdir(cwd)) {
+ pr_perror("can't restore cwd");
+ exit(1);
+ }
+
+ rmdir(dirname);
+ return 0;
+}
diff --git a/test/zdtm/static/deleted_dev.c b/test/zdtm/static/deleted_dev.c
new file mode 100644
index 000000000..e855aed69
--- /dev/null
+++ b/test/zdtm/static/deleted_dev.c
@@ -0,0 +1,73 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that we can migrate with a device special file "
+ "open and unlinked before migration";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char **argv)
+{
+ int fd;
+ struct stat st;
+ /* /dev/null params - sure to exist in a VPS */
+ mode_t mode = S_IFCHR | 0700;
+ dev_t dev = makedev(1, 3);
+
+ test_init(argc, argv);
+
+ if (mknod(filename, mode, dev)) {
+ pr_perror("can't make device file \"%s\"", filename);
+ exit(1);
+ }
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ goto out;
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink %s", filename);
+ goto out;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (fstat(fd, &st) < 0) {
+ fail("can't stat %s: %m", filename);
+ goto out;
+ }
+
+ if (st.st_mode != mode || st.st_rdev != dev) {
+ fail("%s is no longer the device file we had", filename);
+ test_msg("mode %x want %x, dev %x want %x\n",
+ st.st_mode, mode, st.st_rdev, dev);
+ goto out;
+ }
+
+ if (close(fd) < 0) {
+ fail("can't close %s: %m", filename);
+ goto out;
+ }
+
+ if (unlink(filename) != -1 || errno != ENOENT) {
+ fail("file %s should have been deleted before migration: unlink: %m\n");
+ goto out;
+ }
+
+ pass();
+out:
+ close(fd);
+ unlink(filename);
+ return 0;
+}
diff --git a/test/zdtm/static/deleted_dev.desc b/test/zdtm/static/deleted_dev.desc
new file mode 100644
index 000000000..d969725f6
--- /dev/null
+++ b/test/zdtm/static/deleted_dev.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/deleted_unix_sock.c b/test/zdtm/static/deleted_unix_sock.c
new file mode 100644
index 000000000..3562e2635
--- /dev/null
+++ b/test/zdtm/static/deleted_unix_sock.c
@@ -0,0 +1,194 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Create a unix socket, and destroy it before "
+ "migration; check that the child can write to it "
+ "and the parent can read from it after migration";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+static int fill_sock_name(struct sockaddr_un *name, const char *filename)
+{
+ char *cwd;
+
+ cwd = get_current_dir_name();
+ if (strlen(filename) + strlen(cwd) + 1 >= sizeof(name->sun_path))
+ return -1;
+
+ name->sun_family = AF_LOCAL;
+ sprintf(name->sun_path, "%s/%s", cwd, filename);
+ return 0;
+}
+
+static int setup_srv_sock(void)
+{
+ struct sockaddr_un name;
+ int sock;
+
+ if (fill_sock_name(&name, filename) < 0) {
+ pr_perror("filename \"%s\" is too long", filename);
+ return -1;
+ }
+
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0) {
+ pr_perror("can't create socket");
+ return -1;
+ }
+
+ if (bind(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0) {
+ pr_perror("can't bind to socket \"%s\"", filename);
+ goto err;
+ }
+
+ if (listen(sock, 1) < 0) {
+ pr_perror("can't listen on a socket \"%s\"", filename);
+ goto err;
+ }
+
+ return sock;
+err:
+ close(sock);
+ return -1;
+}
+
+static int setup_clnt_sock(void)
+{
+ struct sockaddr_un name;
+ int sock;
+
+ if (fill_sock_name(&name, filename) < 0)
+ return -1;
+
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -1;
+
+ if (connect(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0)
+ goto err;
+
+ return sock;
+err:
+ close(sock);
+ return -1;
+}
+
+int main(int argc, char ** argv)
+{
+ int sock, acc_sock, ret;
+ pid_t pid;
+ uint32_t crc;
+ uint8_t buf[1000];
+
+ test_init(argc, argv);
+
+ sock = setup_srv_sock();
+ if (sock < 0)
+ exit(1);
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("can't fork");
+ exit(1);
+ }
+
+ if (pid == 0) { /* child writes to the unlinked socket and returns */
+ close(sock);
+
+ sock = setup_clnt_sock();
+ if (sock < 0)
+ _exit(1);
+
+ test_waitsig();
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+ if (write(sock, buf, sizeof(buf)) != sizeof(buf)) {
+ pr_perror("can't write to socket");
+ exit(errno);
+ }
+
+ close(sock);
+ exit(0);
+ }
+
+ acc_sock = accept(sock, NULL, NULL);
+ if (acc_sock < 0) {
+ pr_perror("can't accept() the connection on \"%s\"", filename);
+ goto out_kill;
+ }
+
+ close(sock);
+ sock = acc_sock;
+
+ if (unlink(filename)) {
+ pr_perror("can't unlink %s", filename);
+ goto out_kill;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (kill(pid, SIGTERM)) {
+ fail("terminating the child failed: %m\n");
+ goto out;
+ }
+
+ if (wait(&ret) != pid) {
+ fail("wait() returned wrong pid %d: %m\n", pid);
+ goto out;
+ }
+
+ if (WIFEXITED(ret)) {
+ ret = WEXITSTATUS(ret);
+ if (ret) {
+ fail("child exited with nonzero code %d (%s)\n", ret, strerror(ret));
+ goto out;
+ }
+ }
+ if (WIFSIGNALED(ret)) {
+ fail("child exited on unexpected signal %d\n", WTERMSIG(ret));
+ goto out;
+ }
+
+ if (read(sock, buf, sizeof(buf)) != sizeof(buf)) {
+ fail("can't read %s: %m\n", filename);
+ goto out;
+ }
+
+ crc = ~0;
+ if (datachk(buf, sizeof(buf), &crc)) {
+ fail("CRC mismatch\n");
+ goto out;
+ }
+
+
+ if (close(sock)) {
+ fail("close failed: %m\n");
+ goto out;
+ }
+
+ if (unlink(filename) != -1 || errno != ENOENT) {
+ fail("file %s should have been deleted before migration: unlink: %m\n");
+ goto out;
+ }
+
+ pass();
+
+out_kill:
+ kill(pid, SIGTERM);
+out:
+ close(sock);
+ return 0;
+}
diff --git a/test/zdtm/static/different_creds.c b/test/zdtm/static/different_creds.c
new file mode 100644
index 000000000..af7aa3c6f
--- /dev/null
+++ b/test/zdtm/static/different_creds.c
@@ -0,0 +1,149 @@
+#define _GNU_SOURCE
+#include <alloca.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <sched.h>
+#include <sys/capability.h>
+#include <linux/limits.h>
+#include <pthread.h>
+#include <syscall.h>
+#include <sys/socket.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that threads with different creds aren't checkpointed";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+void *drop_caps_and_wait(void *arg)
+{
+ int fd = *((int *) arg), i;
+ void *retcode = (void *)0xdeadbeaf;
+ cap_t caps;
+ char c;
+
+ typedef struct cap_set {
+ cap_flag_value_t val;
+ cap_flag_value_t new;
+ cap_flag_t flag;
+ cap_value_t bit;
+ } cap_set_t;
+
+ cap_set_t src[] = {
+ {
+ .val = CAP_CLEAR,
+ .flag = CAP_EFFECTIVE,
+ .bit = CAP_CHOWN,
+ },
+ {
+ .val = CAP_SET,
+ .flag = CAP_EFFECTIVE,
+ .bit = CAP_DAC_OVERRIDE,
+ },
+ {
+ .val = CAP_CLEAR,
+ .flag = CAP_INHERITABLE,
+ .bit = CAP_SETPCAP,
+ },
+ {
+ .val = CAP_SET,
+ .flag = CAP_INHERITABLE,
+ .bit = CAP_NET_BIND_SERVICE,
+ },
+ };
+
+ caps = cap_get_proc();
+ if (!caps) {
+ pr_perror("cap_get_proc");
+ return NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(src); i++) {
+ if (cap_set_flag(caps, src[i].flag, 1, &src[i].bit, src[i].val) < 0) {
+ pr_perror("Can't setup CAP %s", cap_to_name(src[i].bit));
+ goto die;
+ }
+ }
+
+ if (cap_set_proc(caps) < 0) {
+ pr_perror("cap_set_proc");
+ goto die;
+ }
+
+ if (write(fd, "a", 1) != 1) {
+ pr_perror("Unable to send a status");
+ goto die;
+ }
+
+ if (read(fd, &c, 1) != 1) {
+ pr_perror("Unable to read a status");
+ goto die;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(src); i++) {
+ if (cap_get_flag(caps, src[i].bit, src[i].flag, &src[i].new) < 0) {
+ pr_perror("Can't get CAP %s", cap_to_name(src[i].bit));
+ goto die;
+ }
+
+ if (src[i].val != src[i].new) {
+ pr_err("Val mismatch on CAP %s\n", cap_to_name(src[i].bit));
+ goto die;
+ }
+ }
+
+ retcode = NULL;
+die:
+ cap_free(caps);
+ return retcode;
+}
+
+int main(int argc, char ** argv)
+{
+ int pipefd[2];
+ pthread_t thr;
+ char c;
+ void *retcode;
+
+ test_init(argc, argv);
+
+ if (socketpair(AF_FILE, SOCK_SEQPACKET, 0, pipefd)) {
+ pr_perror("pipe");
+ return -1;
+ }
+
+ if (pthread_create(&thr, NULL, drop_caps_and_wait, &pipefd[0])) {
+ pr_perror("Unable to create thread");
+ return -1;
+ }
+
+ /*
+ * Wait for child to signal us that it has droped caps.
+ */
+ if (read(pipefd[1], &c, 1) != 1) {
+ pr_perror("read");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (write(pipefd[1], &c, 1) != 1) {
+ pr_perror("write");
+ return 1;
+ }
+
+ if (pthread_join(thr, &retcode)) {
+ pr_perror("Unable to jount a thread");
+ return 1;
+ }
+
+ if (retcode != NULL) {
+ fail("retcode returned %p", retcode);
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/different_creds.desc b/test/zdtm/static/different_creds.desc
new file mode 100644
index 000000000..fa2c82d08
--- /dev/null
+++ b/test/zdtm/static/different_creds.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'flags': 'suid'}
diff --git a/test/zdtm/static/dumpable01.c b/test/zdtm/static/dumpable01.c
new file mode 100644
index 000000000..e5dfc9ab7
--- /dev/null
+++ b/test/zdtm/static/dumpable01.c
@@ -0,0 +1,48 @@
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check dumpable flag handling (dumpable case)";
+const char *test_author = "Filipe Brandenburger <filbranden@google.com>";
+
+int main(int argc, char **argv)
+{
+ int save_dumpable;
+ int dumpable;
+
+ test_init(argc, argv);
+
+ save_dumpable = prctl(PR_GET_DUMPABLE);
+ if (save_dumpable < 0) {
+ pr_perror("error getting prctl(PR_GET_DUMPABLE) before dump");
+ return 1;
+ }
+#ifdef DEBUG
+ test_msg("DEBUG: before dump: dumpable=%d\n", save_dumpable);
+#endif
+
+ /* Wait for criu dump and restore. */
+ test_daemon();
+ test_waitsig();
+
+ dumpable = prctl(PR_GET_DUMPABLE);
+ if (dumpable < 0) {
+ pr_perror("error getting prctl(PR_GET_DUMPABLE) after restore");
+ return 1;
+ }
+#ifdef DEBUG
+ test_msg("DEBUG: after dump: dumpable=%d\n", dumpable);
+#endif
+
+ if (dumpable != save_dumpable) {
+ errno = 0;
+ fail("dumpable flag was not preserved over migration");
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/dumpable02.c b/test/zdtm/static/dumpable02.c
new file mode 100644
index 000000000..3463deda7
--- /dev/null
+++ b/test/zdtm/static/dumpable02.c
@@ -0,0 +1,207 @@
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check dumpable flag handling (non-dumpable case)";
+const char *test_author = "Filipe Brandenburger <filbranden@google.com>";
+
+int dumpable_server() {
+ char buf[256];
+ int ret;
+
+ for (;;) {
+ ret = read(0, buf, sizeof(buf));
+ if (ret == 0)
+ break;
+ ret = snprintf(buf, sizeof(buf), "DUMPABLE:%d\n", prctl(PR_GET_DUMPABLE));
+ write(1, buf, ret);
+ }
+ return 0;
+}
+
+int get_dumpable_from_pipes(int pipe_input, int pipe_output) {
+ char buf[256];
+ int len;
+ long value;
+ char *endptr = NULL;
+
+ /* input and output are from the child's point of view. */
+
+ write(pipe_input, "GET\n", 4);
+ len = read(pipe_output, buf, sizeof(buf));
+ if (len < 0) {
+ pr_perror("error in parent reading from pipe");
+ return -1;
+ }
+
+ if (memcmp(buf, "DUMPABLE:", 9) != 0) {
+ pr_perror("child returned [%s]", buf);
+ return -1;
+ }
+
+ value = strtol(&buf[9], &endptr, 10);
+ if (!endptr || *endptr != '\n' || endptr != buf + len - 1) {
+ pr_perror("child returned [%s]", buf);
+ return -1;
+ }
+
+ return (int)value;
+}
+
+
+int main(int argc, char **argv)
+{
+ int pipe_input[2];
+ int pipe_output[2];
+ int save_dumpable;
+ int dumpable;
+ int ret;
+ pid_t pid;
+ pid_t waited;
+ int status;
+
+ /*
+ * Check if we are being re-executed to spawn the dumpable server. This
+ * re-execution is what essentially causes the dumpable flag to be
+ * cleared since we have execute but not read permissions to the
+ * binary.
+ */
+ if (getenv("DUMPABLE_SERVER"))
+ return dumpable_server();
+
+ /*
+ * Otherwise, do normal startup and spawn a dumpable server. While we
+ * are still running as root, chmod() the binary to give it execute but
+ * not read permissions, that way when we execv() it as a non-root user
+ * the kernel will drop our dumpable flag and reset it to the value in
+ * /proc/sys/fs/suid_dumpable.
+ */
+ ret = chmod(argv[0], 0111);
+ if (ret < 0) {
+ pr_perror("error chmodding %s", argv[0]);
+ return 1;
+ }
+
+ test_init(argc, argv);
+
+ ret = pipe(pipe_input);
+ if (ret < 0) {
+ pr_perror("error creating input pipe");
+ return 1;
+ }
+
+ ret = pipe(pipe_output);
+ if (ret < 0) {
+ pr_perror("error creating output pipe");
+ return 1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("error forking the dumpable server");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /*
+ * Child process will execv() the dumpable server. Start by
+ * reopening stdin and stdout to use the pipes, then set the
+ * environment variable and execv() the same binary.
+ */
+ close(0);
+ close(1);
+
+ ret = dup2(pipe_input[0], 0);
+ if (ret < 0) {
+ pr_perror("could not dup2 pipe into child's stdin");
+ return 1;
+ }
+
+ ret = dup2(pipe_output[1], 1);
+ if (ret < 0) {
+ pr_perror("could not dup2 pipe into child's stdout");
+ return 1;
+ }
+
+ close(pipe_output[0]);
+ close(pipe_output[1]);
+ close(pipe_input[0]);
+ close(pipe_input[1]);
+
+ ret = setenv("DUMPABLE_SERVER", "yes", 1);
+ if (ret < 0) {
+ pr_perror("could not set the DUMPABLE_SERVER env variable");
+ return 1;
+ }
+
+ ret = execl(argv[0], "dumpable_server", NULL);
+ pr_perror("could not execv %s as a dumpable_server", argv[0]);
+ return 1;
+ }
+
+ /*
+ * Parent process, write to the pipe_input socket to ask the server
+ * child to tell us what its dumpable flag value is on its side.
+ */
+ close(pipe_input[0]);
+ close(pipe_output[1]);
+
+ save_dumpable = get_dumpable_from_pipes(pipe_input[1], pipe_output[0]);
+ if (save_dumpable < 0) return 1;
+#ifdef DEBUG
+ test_msg("DEBUG: before dump: dumpable=%d\n", save_dumpable);
+#endif
+
+ /* Wait for dump and restore. */
+ test_daemon();
+ test_waitsig();
+
+ dumpable = get_dumpable_from_pipes(pipe_input[1], pipe_output[0]);
+ if (dumpable < 0) return 1;
+#ifdef DEBUG
+ test_msg("DEBUG: after restore: dumpable=%d\n", dumpable);
+#endif
+
+ if (dumpable != save_dumpable) {
+ errno = 0;
+ fail("dumpable flag was not preserved over migration");
+ return 1;
+ }
+
+ /* Closing the pipes will terminate the child server. */
+ close(pipe_input[1]);
+ close(pipe_output[0]);
+
+ waited = wait(&status);
+ if (waited < 0) {
+ pr_perror("error calling wait on the child");
+ return 1;
+ }
+ errno = 0;
+ if (waited != pid) {
+ pr_perror("waited pid %d did not match child pid %d",
+ waited, pid);
+ return 1;
+ }
+ if (!WIFEXITED(status)) {
+ pr_perror("child dumpable server returned abnormally with status=%d",
+ status);
+ return 1;
+ }
+ if (WEXITSTATUS(status) != 0) {
+ pr_perror("child dumpable server returned rc=%d",
+ WEXITSTATUS(status));
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/dumpable02.desc b/test/zdtm/static/dumpable02.desc
new file mode 100644
index 000000000..f8c897914
--- /dev/null
+++ b/test/zdtm/static/dumpable02.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'nouser'}
diff --git a/test/zdtm/static/env00.c b/test/zdtm/static/env00.c
new file mode 100644
index 000000000..1feabfa9f
--- /dev/null
+++ b/test/zdtm/static/env00.c
@@ -0,0 +1,39 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that environment didn't change";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *envname;
+TEST_OPTION(envname, string, "environment variable name", 1);
+
+int main(int argc, char **argv)
+{
+ char *env;
+
+ test_init(argc, argv);
+
+ if (setenv(envname, test_author, 1)) {
+ pr_perror("Can't set env var \"%s\" to \"%s\"", envname, test_author);
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ env = getenv(envname);
+ if (!env) {
+ fail("can't get env var \"%s\": %m\n", envname);
+ goto out;
+ }
+
+ if (strcmp(env, test_author))
+ fail("%s != %s\n", env, test_author);
+ else
+ pass();
+out:
+ return 0;
+}
diff --git a/test/zdtm/static/eventfs00.c b/test/zdtm/static/eventfs00.c
new file mode 100644
index 000000000..69fc88186
--- /dev/null
+++ b/test/zdtm/static/eventfs00.c
@@ -0,0 +1,98 @@
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/eventfd.h>
+#include <sys/ioctl.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "zdtmtst.h"
+
+#ifndef F_SETSIG
+#define F_SETSIG 10 /* for sockets. */
+#define F_GETSIG 11 /* for sockets. */
+#endif
+
+const char *test_doc = "Check for eventfs";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+#define EVENTFD_INITIAL 30
+#define EVENTFD_FINAL 90
+
+int main(int argc, char *argv[])
+{
+ int efd, ret, epollfd;
+ int pipefd[2];
+ uint64_t v = EVENTFD_INITIAL;
+ struct epoll_event ev;
+
+ test_init(argc, argv);
+
+ epollfd = epoll_create(1);
+ if (epollfd < 0) {
+ fail("epoll_create");
+ exit(1);
+ }
+
+ efd = eventfd((unsigned int)v, EFD_NONBLOCK);
+ if (efd < 0) {
+ fail("eventfd");
+ exit(1);
+ }
+
+ memset(&ev, 0xff, sizeof(ev));
+ ev.events = EPOLLIN | EPOLLOUT;
+
+ if (pipe(pipefd)) {
+ fail("pipe");
+ exit(1);
+ }
+
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd[0], &ev)) {
+ fail("epoll_ctl");
+ exit(1);
+ }
+
+ test_msg("created eventfd with %lx\n", v);
+
+ ret = write(efd, &v, sizeof(v));
+ if (ret != sizeof(v)) {
+ fail("write");
+ exit(1);
+ }
+
+ ret = write(efd, &v, sizeof(v));
+ if (ret != sizeof(v)) {
+ fail("write");
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = read(efd, &v, sizeof(v));
+ if (ret != sizeof(v)) {
+ fail("write");
+ exit(1);
+ }
+
+ if (v != EVENTFD_FINAL) {
+ fail("EVENTFD_FINAL mismatch\n");
+ exit(1);
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/fanotify00.c b/test/zdtm/static/fanotify00.c
new file mode 100644
index 000000000..37c9cb425
--- /dev/null
+++ b/test/zdtm/static/fanotify00.c
@@ -0,0 +1,311 @@
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include <unistd.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <linux/fanotify.h>
+
+#include "zdtmtst.h"
+
+#ifdef __x86_64__
+# define __NR_fanotify_init 300
+# define __NR_fanotify_mark 301
+#elif defined(__PPC64__)
+# define __NR_fanotify_init 323
+# define __NR_fanotify_mark 324
+#elif __aarch64__
+# define __NR_fanotify_init 262
+# define __NR_fanotify_mark 263
+#else
+# define __NR_fanotify_init 338
+# define __NR_fanotify_mark 339
+#endif
+
+const char *test_doc = "Check for fanotify delivery";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+const char fanotify_path[] = "fanotify-del-after-cr";
+
+#define BUFF_SIZE (8192)
+
+struct fanotify_mark_inode {
+ unsigned long i_ino;
+ unsigned int s_dev;
+ unsigned int mflags;
+ unsigned int mask;
+ unsigned int ignored_mask;
+ unsigned int fhandle_bytes;
+ unsigned int fhandle_type;
+ unsigned char fhandle[512];
+};
+
+struct fanotify_mark_mount {
+ unsigned int mnt_id;
+ unsigned int mflags;
+ unsigned int mask;
+ unsigned int ignored_mask;
+};
+
+struct fanotify_glob {
+ unsigned int faflags;
+ unsigned int evflags;
+};
+
+struct fanotify_obj {
+ struct fanotify_glob glob;
+ struct fanotify_mark_inode inode;
+ struct fanotify_mark_mount mount;
+};
+
+static int fanotify_init(unsigned int flags, unsigned int event_f_flags)
+{
+ return syscall(__NR_fanotify_init, flags, event_f_flags);
+}
+
+static int fanotify_mark(int fanotify_fd, unsigned int flags, unsigned long mask,
+ int dfd, const char *pathname)
+{
+ return syscall(__NR_fanotify_mark, fanotify_fd, flags, mask, dfd, pathname);
+}
+
+#define fdinfo_field(str, field) !strncmp(str, field":", sizeof(field))
+
+static void show_fanotify_obj(struct fanotify_obj *obj)
+{
+ test_msg("fanotify obj at %p\n", obj);
+
+ test_msg(" glob\n");
+ test_msg(" faflags: %x evflags: %x\n",
+ obj->glob.faflags, obj->glob.evflags);
+
+ test_msg(" inode\n");
+ test_msg(" i_ino: %lx s_dev: %x mflags: %x "
+ "mask: %x ignored_mask: %x "
+ "fhandle_bytes: %x fhandle_type: %x "
+ "fhandle: %s",
+ obj->inode.i_ino, obj->inode.s_dev,
+ obj->inode.mflags, obj->inode.mask,
+ obj->inode.ignored_mask, obj->inode.fhandle_bytes,
+ obj->inode.fhandle_type, obj->inode.fhandle);
+
+ test_msg(" mount\n");
+ test_msg(" mnt_id: %x mflags: %x mask: %x ignored_mask: %x\n",
+ obj->mount.mnt_id, obj->mount.mflags,
+ obj->mount.mask, obj->mount.ignored_mask);
+}
+
+static void copy_fhandle(char *tok, struct fanotify_mark_inode *inode)
+{
+ int off = 0;
+
+ while (*tok && (*tok > '0' || *tok < 'f')) {
+ inode->fhandle[off++] = *tok++;
+ if (off >= sizeof(inode->fhandle) - 1)
+ break;
+ }
+ inode->fhandle[off] = '\0';
+}
+
+static int cmp_fanotify_obj(struct fanotify_obj *old, struct fanotify_obj *new)
+{
+ if ((old->glob.faflags != new->glob.faflags) ||
+ (old->glob.evflags != new->glob.evflags) ||
+ (old->inode.i_ino != new->inode.i_ino) ||
+ (old->inode.s_dev != new->inode.s_dev) ||
+ (old->inode.mflags != new->inode.mflags) ||
+ (old->inode.mask != new->inode.mask) ||
+ (old->inode.ignored_mask != new->inode.ignored_mask))
+ return -1;
+
+ if (memcmp(old->inode.fhandle, new->inode.fhandle,
+ sizeof(new->inode.fhandle)))
+ return -2;
+
+ /* mnt_id may change, exclude it */
+ if ((old->mount.mflags != new->mount.mflags) ||
+ (old->mount.mask != new->mount.mask) ||
+ (old->mount.ignored_mask != new->mount.ignored_mask))
+ return -3;
+
+ return 0;
+}
+
+int parse_fanotify_fdinfo(int fd, struct fanotify_obj *obj, unsigned int expected_to_meet)
+{
+ unsigned int met = 0;
+ char str[512];
+ FILE *f;
+ int ret;
+
+ sprintf(str, "/proc/self/fdinfo/%d", fd);
+ f = fopen(str, "r");
+ if (!f) {
+ pr_perror("Can't open fdinfo to parse");
+ return -1;
+ }
+
+ while (fgets(str, sizeof(str), f)) {
+ if (fdinfo_field(str, "fanotify flags")) {
+ ret = sscanf(str, "fanotify flags:%x event-flags:%x",
+ &obj->glob.faflags, &obj->glob.evflags);
+ if (ret != 2)
+ goto parse_err;
+ met++;
+ continue;
+ }
+ if (fdinfo_field(str, "fanotify mnt_id")) {
+ ret = sscanf(str,
+ "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x",
+ &obj->mount.mnt_id, &obj->mount.mflags,
+ &obj->mount.mask, &obj->mount.ignored_mask);
+ if (ret != 4)
+ goto parse_err;
+ met++;
+ continue;
+ }
+ if (fdinfo_field(str, "fanotify ino")) {
+ int hoff;
+ ret = sscanf(str,
+ "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x "
+ "fhandle-bytes:%x fhandle-type:%x f_handle: %n",
+ &obj->inode.i_ino, &obj->inode.s_dev,
+ &obj->inode.mflags, &obj->inode.mask, &obj->inode.ignored_mask,
+ &obj->inode.fhandle_bytes, &obj->inode.fhandle_type,
+ &hoff);
+ if (ret != 7)
+ goto parse_err;
+ copy_fhandle(&str[hoff], &obj->inode);
+ met++;
+ continue;
+ }
+ }
+
+ if (expected_to_meet != met) {
+ pr_perror("Expected to meet %d entries but got %d",
+ expected_to_meet, met);
+ return -1;
+ }
+
+ return 0;
+
+parse_err:
+ pr_perror("Can't parse '%s'", str);
+ return -1;
+}
+
+int main (int argc, char *argv[])
+{
+ struct fanotify_obj old = { }, new = { };
+ int fa_fd, fd, del_after;
+ char buf[BUFF_SIZE];
+ ssize_t length;
+ int ns = getenv("ZDTM_NEWNS") != NULL;
+
+ test_init(argc, argv);
+
+ if (ns) {
+ if (mkdir("/tmp", 666) && errno != EEXIST) {
+ pr_perror("Unable to create the /tmp directory");
+ return -1;
+ }
+ if (mount("zdtm", "/tmp", "tmpfs", 0, NULL)) {
+ pr_perror("Unable to mount tmpfs into %s", "/tmp");
+ }
+ }
+
+ fa_fd = fanotify_init(FAN_NONBLOCK | O_RDONLY | O_LARGEFILE |
+ FAN_CLASS_NOTIF | FAN_UNLIMITED_QUEUE,
+ 0);
+ if (fa_fd < 0) {
+ pr_perror("fanotify_init failed");
+ exit(1);
+ }
+
+ del_after = open(fanotify_path, O_CREAT | O_TRUNC);
+ if (del_after < 0) {
+ pr_perror("open failed");
+ exit(1);
+ }
+
+ if (fanotify_mark(fa_fd, FAN_MARK_ADD,
+ FAN_MODIFY | FAN_ACCESS | FAN_OPEN | FAN_CLOSE,
+ AT_FDCWD, fanotify_path)) {
+ pr_perror("fanotify_mark failed");
+ exit(1);
+ }
+
+ if (fanotify_mark(fa_fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+ FAN_ONDIR | FAN_OPEN | FAN_CLOSE,
+ AT_FDCWD, "/tmp")) {
+ pr_perror("fanotify_mark failed");
+ exit(1);
+ }
+
+ if (fanotify_mark(fa_fd, FAN_MARK_ADD | FAN_MARK_MOUNT |
+ FAN_MARK_IGNORED_MASK | FAN_MARK_IGNORED_SURV_MODIFY,
+ FAN_MODIFY | FAN_ACCESS,
+ AT_FDCWD, "/tmp")) {
+ pr_perror("fanotify_mark failed");
+ exit(1);
+ }
+
+ if (parse_fanotify_fdinfo(fa_fd, &old, 3)) {
+ pr_perror("parsing fanotify fdinfo failed");
+ exit(1);
+ }
+
+ show_fanotify_obj(&old);
+
+ test_daemon();
+ test_waitsig();
+
+ fd = open("/", O_RDONLY);
+ close(fd);
+
+ fd = open(fanotify_path, O_RDWR);
+ close(fd);
+
+ if (unlink(fanotify_path)) {
+ fail("can't unlink %s\n", fanotify_path);
+ exit(1);
+ }
+
+ if (parse_fanotify_fdinfo(fa_fd, &new, 3)) {
+ fail("parsing fanotify fdinfo failed\n");
+ exit(1);
+ }
+
+ show_fanotify_obj(&new);
+
+ if (cmp_fanotify_obj(&old, &new)) {
+ fail("fanotify mismatch on fdinfo level\n");
+ exit(1);
+ }
+
+ length = read(fa_fd, buf, sizeof(buf));
+ if (length <= 0) {
+ fail("No events in fanotify queue\n");
+ exit(1);
+ }
+
+ if (fanotify_mark(fa_fd, FAN_MARK_REMOVE | FAN_MARK_MOUNT,
+ FAN_ONDIR | FAN_OPEN | FAN_CLOSE,
+ AT_FDCWD, "/tmp")) {
+ pr_perror("fanotify_mark failed");
+ exit(1);
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/fanotify00.desc b/test/zdtm/static/fanotify00.desc
new file mode 100644
index 000000000..d969725f6
--- /dev/null
+++ b/test/zdtm/static/fanotify00.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/fd.c b/test/zdtm/static/fd.c
new file mode 100644
index 000000000..a2e89d93f
--- /dev/null
+++ b/test/zdtm/static/fd.c
@@ -0,0 +1,109 @@
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+#include "lock.h"
+
+const char *test_doc = "Check that criu closes up all its descriptors";
+const char *test_author = "Andrew Vagin <avagin@parallels.com>";
+
+int main(int argc, char **argv)
+{
+ struct dirent *de;
+ char pfd[PATH_MAX];
+ mutex_t *lock;
+ int status;
+ pid_t pid;
+ DIR *d;
+
+ test_init(argc, argv);
+
+ lock = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (lock == MAP_FAILED)
+ return 1;
+
+ mutex_init(lock);
+ mutex_lock(lock);
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork()");
+ return 1;
+ }
+
+ if (pid == 0) {
+
+ d = opendir("/proc/self/fd");
+ if (d == NULL)
+ return 1;
+
+ while ((de = readdir(d))) {
+ int fd;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ fd = atoi(de->d_name);
+ if (dirfd(d) == fd)
+ continue;
+ close(fd);
+ }
+
+ closedir(d);
+ mutex_unlock(lock);
+
+ test_waitsig();
+
+ return 0;
+ }
+
+ mutex_lock(lock);
+
+ test_daemon();
+ test_waitsig();
+
+ snprintf(pfd, sizeof(pfd), "/proc/%d/fd", pid);
+ d = opendir(pfd);
+ if (d == NULL)
+ return 2;
+
+ while ((de = readdir(d))) {
+ int ret;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ ret = readlinkat(dirfd(d), de->d_name, pfd, sizeof(pfd) - 1);
+ if (ret < 0) {
+ pr_perror("readlink");
+ ret = 0;
+ }
+ pfd[ret] = '\0';
+ fail("Unexpected fd: %s -> %s\n", de->d_name, pfd);
+ return 1;
+ }
+
+ closedir(d);
+ kill(pid, SIGTERM);
+
+ if (waitpid(pid, &status, 0) != pid) {
+ pr_perror("waitpid()");
+ return 1;
+ }
+
+ if (status != 0) {
+ fail("%d:%d:%d:%d", WIFEXITED(status), WEXITSTATUS(status),
+ WIFSIGNALED(status), WTERMSIG(status));
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/fd.desc b/test/zdtm/static/fd.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/fd.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/fdt_shared.c b/test/zdtm/static/fdt_shared.c
new file mode 100644
index 000000000..908f6bcd0
--- /dev/null
+++ b/test/zdtm/static/fdt_shared.c
@@ -0,0 +1,207 @@
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sched.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check a shared file descriptor table.";
+const char *test_author = "Andrew Vagin <avagin@openvz.org>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+#define STACK_SIZE 4096
+#define TEST_FD 128
+#define TEST_STRING "Hello World!"
+
+#define CHILDREN 4
+static int fork_pfd[2];
+
+static void forked()
+{
+ char c = 0;
+
+ if (write(fork_pfd[1], &c, 1) != 1) {
+ pr_perror("Unable to send a signal to the parent");
+ exit(5);
+ }
+}
+
+static void wait_children()
+{
+ int i;
+ char c;
+
+ for (i = 0; i < CHILDREN; i++) {
+ if (read(fork_pfd[0], &c, 1) != 1) {
+ pr_perror("Unable to read a signal from a child");
+ exit(5);
+ }
+ }
+}
+
+static pid_t clone_child(int (*fn)(void *), int flags)
+{
+ char stack[STACK_SIZE] __stack_aligned__;
+ pid_t pid;
+
+ pid = clone(fn, stack + STACK_SIZE,
+ flags | SIGCHLD, NULL);
+ if (pid == -1) {
+ pr_perror("Unable to clone a new process");
+ return -1;
+ }
+
+ return pid;
+}
+
+static int child2(void *_arg)
+{
+ char buf[10];
+
+ forked();
+ test_waitsig();
+
+ if (read(TEST_FD, buf, sizeof(TEST_STRING)) != sizeof(TEST_STRING)) {
+ pr_perror("Unable to read from %d", TEST_FD);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int child3(void *_arg)
+{
+ forked();
+ test_waitsig();
+
+ if (close(TEST_FD) != -1) {
+ fail("%d is exist\n", TEST_FD);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int child(void *_arg)
+{
+ char buf[10];
+ pid_t pid, pid2;
+ int status;
+
+ pid = clone_child(child2, CLONE_FILES);
+ if (pid < 0)
+ return 1;
+
+ pid2 = clone_child(child3, 0);
+ if (pid < 0)
+ return 1;
+
+ forked();
+ test_waitsig();
+
+ kill(pid2, SIGTERM);
+ kill(pid, SIGTERM);
+ waitpid(pid2, &status, 0);
+
+ if (status) {
+ fail("The child3 returned %d\n", status);
+ return 1;
+ }
+
+ waitpid(pid, &status, 0);
+
+ if (status) {
+ fail("The child2 returned %d\n", status);
+ return 1;
+ }
+
+ if (read(TEST_FD, buf, sizeof(TEST_STRING)) != sizeof(TEST_STRING)) {
+ pr_perror("Unable to read from %d", TEST_FD);
+ return 1;
+ }
+
+ if (close(TEST_FD) == -1) {
+ pr_perror("Unable to close(%d)", TEST_FD);
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char ** argv)
+{
+ int status;
+ pid_t pid, pid2;
+ int fd, i;
+
+ test_init(argc, argv);
+
+ if (pipe(fork_pfd)) {
+ pr_perror("pipe");
+ return 1;
+ }
+
+ pid = clone_child(child, CLONE_FILES);
+ if (pid < 0)
+ return 1;
+
+ pid2 = clone_child(child2, CLONE_FILES);
+ if (pid2 < 0)
+ return 1;
+
+ wait_children();
+
+ test_daemon();
+ test_waitsig();
+
+ fd = open(filename, O_RDWR | O_CREAT, 0666);
+ if (fd == -1) {
+ pr_perror("Can't open /dev/zero");
+ return -1;
+ }
+
+ for (i = 0; i < 3; i++)
+ if (write(fd, TEST_STRING, sizeof(TEST_STRING)) != sizeof(TEST_STRING)) {
+ pr_perror("Unable to write a test string");
+ return -1;
+ }
+
+ fd = dup2(fd, TEST_FD);
+ if (fd == -1) {
+ pr_perror("Can't dup fd to %d", fd, TEST_FD);
+ return -1;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+ kill(pid2, SIGTERM);
+ waitpid(pid2, &status, 0);
+ kill(pid, SIGTERM);
+
+ if (status) {
+ fail("The child returned %d\n", status);
+ return 1;
+ }
+
+ waitpid(pid, &status, 0);
+ if (status) {
+ fail("The child returned %d\n", status);
+ return 1;
+ }
+
+ if (close(TEST_FD) == 0) {
+ fail("%d was not closed\n", TEST_FD);
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/fifo-ghost.c b/test/zdtm/static/fifo-ghost.c
new file mode 100644
index 000000000..94a8c57f5
--- /dev/null
+++ b/test/zdtm/static/fifo-ghost.c
@@ -0,0 +1,81 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that a ghost fifo with data restored";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char **argv)
+{
+ int fd;
+ int fd_ro;
+ mode_t mode = S_IFIFO | 0700;
+ uint8_t buf[256];
+ uint32_t crc;
+ int ret;
+
+ test_init(argc, argv);
+
+ if (mknod(filename, mode, 0)) {
+ pr_perror("can't make fifo \"%s\"", filename);
+ exit(1);
+ }
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ return 1;
+ }
+
+ fd_ro = open(filename, O_RDONLY);
+ if (fd_ro < 0) {
+ pr_perror("can't open %s", filename);
+ return 1;
+ }
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+ ret = write(fd, buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
+ pr_perror("write() failed");
+ return 1;
+ }
+
+ if (unlink(filename) < 0) {
+ fail("can't unlink %s", filename);
+ return 1;
+ }
+
+ close(fd);
+
+ test_daemon();
+ test_waitsig();
+
+ ret = read(fd_ro, buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
+ pr_perror("read() failed");
+ return 1;
+ }
+
+ crc = ~0;
+ if (datachk(buf, sizeof(buf), &crc)) {
+ fail("data corrupted");
+ return 1;
+ }
+
+ if (close(fd_ro) < 0) {
+ fail("can't close %s", filename);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/fifo-rowo-pair.c b/test/zdtm/static/fifo-rowo-pair.c
new file mode 100644
index 000000000..b96714772
--- /dev/null
+++ b/test/zdtm/static/fifo-rowo-pair.c
@@ -0,0 +1,159 @@
+#define _GNU_SOURCE
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+
+#include <time.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test for fifo ro/wo with "
+ "fake fifo needed on criu side";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+char *name_master;
+TEST_OPTION(name_master, string, "master fifo name", 1);
+
+char *name_slave;
+TEST_OPTION(name_slave, string, "slave fifo name", 1);
+
+#define TEST_VALUE (00100)
+
+#define exit_shot(pid, code) \
+ do { kill(pid, SIGKILL); exit(code); } while (0)
+
+#define exit_shot_parent(code) \
+ exit_shot(getppid(), 1)
+
+int main(int argc, char **argv)
+{
+ task_waiter_t t;
+ pid_t pid;
+ int fd_master, fd_slave;
+ int v, status;
+
+ test_init(argc, argv);
+
+ if (mknod(name_master, S_IFIFO | 0700, 0)) {
+ pr_perror("can't make fifo \"%s\"", name_master);
+ exit(1);
+ }
+
+ if (mknod(name_slave, S_IFIFO | 0700, 0)) {
+ pr_perror("can't make fifo \"%s\"", name_slave);
+ exit(1);
+ }
+
+ fd_slave = open(name_slave, O_RDWR);
+ if (fd_slave < 0) {
+ pr_perror("can't open %s", name_slave);
+ exit(1);
+ }
+
+ task_waiter_init(&t);
+
+ pid = test_fork();
+ if (pid == 0) {
+ int new_slave;
+
+ fd_master = open(name_master, O_WRONLY);
+ if (fd_master < 0) {
+ pr_perror("can't open %s", name_master);
+ exit_shot_parent(1);
+ }
+
+ new_slave = dup2(fd_slave, 64);
+ if (new_slave < 0) {
+ pr_perror("can't dup %s", name_slave);
+ exit_shot_parent(1);
+ }
+
+ close(fd_slave);
+
+ task_waiter_complete_current(&t);
+
+ v = TEST_VALUE;
+ if (write(new_slave, &v, sizeof(v)) != sizeof(v)) {
+ pr_perror("write failed");
+ exit_shot_parent(1);
+ }
+
+ v = TEST_VALUE;
+ if (write(fd_master, &v, sizeof(v)) != sizeof(v)) {
+ pr_perror("write failed");
+ exit_shot_parent(1);
+ }
+
+ /* Don't exit until explicitly asked */
+ task_waiter_wait4(&t, getppid());
+
+ exit(0);
+ } else if (pid < 0) {
+ pr_perror("test_fork failed");
+ exit(1);
+ }
+
+ fd_master = open(name_master, O_RDONLY);
+ if (fd_master < 0) {
+ pr_perror("can't open %s", name_master);
+ exit_shot(pid, 1);
+ }
+
+ /* Wait until data appear in kernel fifo buffer */
+ task_waiter_wait4(&t, pid);
+
+ test_daemon();
+ test_waitsig();
+
+ if (read(fd_master, &v, sizeof(v)) != sizeof(v)) {
+ pr_perror("read failed");
+ exit_shot(pid, 1);
+ }
+
+ task_waiter_complete_current(&t);
+
+ if (v != TEST_VALUE) {
+ fail("read data mismatch\n");
+ exit_shot(pid, 1);
+ }
+
+ if (read(fd_slave, &v, sizeof(v)) != sizeof(v)) {
+ pr_perror("read failed");
+ exit_shot(pid, 1);
+ }
+ if (v != TEST_VALUE) {
+ fail("read data mismatch\n");
+ exit_shot(pid, 1);
+ }
+
+ waitpid(pid, &status, P_ALL);
+
+ if (unlink(name_master) < 0)
+ pr_perror("can't unlink %s", name_master);
+
+ if (unlink(name_slave) < 0)
+ pr_perror("can't unlink %s", name_slave);
+
+ if (!WIFEXITED(status)) {
+ pr_perror("child %d is still running", pid);
+ exit_shot(pid, 1);
+ }
+
+ errno = WEXITSTATUS(status);
+ if (errno) {
+ fail("Child exited with error %m");
+ exit(errno);
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/fifo.c b/test/zdtm/static/fifo.c
new file mode 100644
index 000000000..c8437d932
--- /dev/null
+++ b/test/zdtm/static/fifo.c
@@ -0,0 +1,86 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that we can migrate with a named pipe "
+ "open";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+#define BUF_SIZE (16 * 4096) /* A fifo buffer has 16 slots by default */
+
+int main(int argc, char **argv)
+{
+ int fd;
+ struct stat st;
+ mode_t mode = S_IFIFO | 0700;
+ uint8_t buf[BUF_SIZE];
+ uint32_t crc;
+ int ret;;
+
+ test_init(argc, argv);
+
+ if (mknod(filename, mode, 0)) {
+ pr_perror("can't make fifo \"%s\"", filename);
+ exit(1);
+ }
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ return 1;
+ }
+
+ crc = ~0;
+ datagen(buf, BUF_SIZE, &crc);
+ ret = write(fd, buf, BUF_SIZE);
+ if (ret != BUF_SIZE) {
+ pr_perror("write() failed");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = read(fd, buf, BUF_SIZE);
+ if (ret != BUF_SIZE) {
+ pr_perror("read() failed");
+ return 1;
+ }
+
+ crc = ~0;
+ if (datachk(buf, BUF_SIZE, &crc)) {
+ fail("data corrupted\n");
+ return 1;
+ }
+
+ if (close(fd) < 0) {
+ fail("can't close %s: %m", filename);
+ return 1;
+ }
+
+ if (stat(filename, &st) < 0) {
+ fail("can't stat %s: %m", filename);
+ return 1;
+ }
+
+ if (st.st_mode != mode) {
+ fail("%s is no longer the fifo we had", filename);
+ return 1;
+ }
+
+ if (unlink(filename) < 0) {
+ fail("can't unlink %s: %m", filename);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/fifo_ro.c b/test/zdtm/static/fifo_ro.c
new file mode 100644
index 000000000..63c4d29cf
--- /dev/null
+++ b/test/zdtm/static/fifo_ro.c
@@ -0,0 +1,94 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that a fifo read-only descriptor is restored with data";
+const char *test_author = "Andrew Vagin <avagin@openvz.org>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+#define BUF_SIZE (16 * 4096) /* A fifo buffer has 16 slots by default */
+
+int main(int argc, char **argv)
+{
+ int fd;
+ int fd_ro;
+ struct stat st;
+ mode_t mode = S_IFIFO | 0700;
+ uint8_t buf[BUF_SIZE];
+ uint32_t crc;
+ int ret;;
+
+ test_init(argc, argv);
+
+ if (mknod(filename, mode, 0)) {
+ pr_perror("can't make fifo \"%s\"", filename);
+ exit(1);
+ }
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ return 1;
+ }
+
+ fd_ro = open(filename, O_RDONLY);
+ if (fd_ro < 0) {
+ pr_perror("can't open %s", filename);
+ return 1;
+ }
+
+ crc = ~0;
+ datagen(buf, BUF_SIZE, &crc);
+ ret = write(fd, buf, BUF_SIZE);
+ if (ret != BUF_SIZE) {
+ pr_perror("write() failed");
+ return 1;
+ }
+
+ close(fd);
+
+ test_daemon();
+ test_waitsig();
+
+ ret = read(fd_ro, buf, BUF_SIZE);
+ if (ret != BUF_SIZE) {
+ pr_perror("read() failed");
+ return 1;
+ }
+
+ crc = ~0;
+ if (datachk(buf, BUF_SIZE, &crc)) {
+ fail("data corrupted\n");
+ return 1;
+ }
+
+ if (close(fd_ro) < 0) {
+ fail("can't close %s: %m", filename);
+ return 1;
+ }
+
+ if (stat(filename, &st) < 0) {
+ fail("can't stat %s: %m", filename);
+ return 1;
+ }
+
+ if (st.st_mode != mode) {
+ fail("%s is no longer the fifo we had", filename);
+ return 1;
+ }
+
+ if (unlink(filename) < 0) {
+ fail("can't unlink %s: %m", filename);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/fifo_wronly.c b/test/zdtm/static/fifo_wronly.c
new file mode 100644
index 000000000..2fbd69e6b
--- /dev/null
+++ b/test/zdtm/static/fifo_wronly.c
@@ -0,0 +1,119 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that we can migrate with a named pipe, "
+ "opened in WRONLY mode";
+#define BUF_SIZE 256
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char **argv)
+{
+ task_waiter_t t;
+ int fd, fd1;
+ struct stat st;
+ mode_t mode = S_IFIFO | 0600;
+ int pid;
+ int chret;
+
+ test_init(argc, argv);
+
+ task_waiter_init(&t);
+
+ if (mknod(filename, mode, 0)) {
+ pr_perror("can't make fifo \"%s\"", filename);
+ exit(1);
+ }
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("Can't fork");
+ exit(1);
+ }
+
+ if (pid == 0) {
+ char rbuf[BUF_SIZE];
+ int res;
+ fd1 = open(filename, O_RDONLY);
+ if (fd1 < 0) {
+ pr_perror("open(%s, O_RDONLY) Failed", filename);
+ chret = errno;
+ return chret;
+ }
+ task_waiter_complete(&t, 1);
+ res = read(fd1, rbuf, 7);
+ if (res < 0) {
+ pr_perror("read error %s", filename);
+ chret = errno;
+ return chret;
+ }
+ else if (res == 0) {
+ pr_perror("read(%d, rbuf, 7) return 0", fd1);
+ return 1;
+ }
+ if (close(fd1) < 0) {
+ fail("can't close %d, %s: %m", fd1, filename);
+ chret = errno;
+ return chret;
+ }
+
+ } else {
+
+ fd = open(filename, O_WRONLY);
+ if (fd < 0) {
+ pr_perror("open(%s, O_WRONLY) Failed", filename);
+ kill(pid, SIGKILL);
+ wait(NULL);
+ return 1;
+ }
+ task_waiter_wait4(&t, 1);
+
+ test_daemon();
+ test_waitsig();
+
+ if (write(fd, "string", 7) == -1) {
+ pr_perror("write(%d, 'string', 7) Failed", fd);
+ return 1;
+ }
+
+ wait(&chret);
+ chret = WEXITSTATUS(chret);
+ if (chret) {
+ fail("child exited with non-zero code %d (%s)\n",
+ chret, strerror(chret));
+ return 1;
+ }
+
+ if (close(fd) < 0) {
+ fail("can't close %d, %s: %m", fd, filename);
+ return 1;
+ }
+
+ if (stat(filename, &st) < 0) {
+ fail("can't stat %s: %m", filename);
+ return 1;
+ }
+
+ if (st.st_mode != mode) {
+ fail("%s is no longer the fifo we had", filename);
+ return 1;
+ }
+
+ if (unlink(filename) < 0) {
+ fail("can't unlink %s: %m", filename);
+ return 1;
+ }
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/file_append.c b/test/zdtm/static/file_append.c
new file mode 100644
index 000000000..14f18776c
--- /dev/null
+++ b/test/zdtm/static/file_append.c
@@ -0,0 +1,61 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check O_APPEND preserved";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char **argv)
+{
+ int fd, fd2, ret;
+ char tmp[3];
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_RDWR | O_CREAT | O_APPEND, 0644);
+ if (fd == -1)
+ return 1;
+
+ fd2 = open(filename, O_RDWR, 0644);
+ if (fd2 == -1)
+ return 1;
+
+ test_daemon();
+ test_waitsig();
+
+ if (write(fd2, "x", 1) != 1) {
+ pr_perror("Can't write x");
+ return 1;
+ }
+
+ if (write(fd, "y", 1) != 1) {
+ pr_perror("Can't write y");
+ return 1;
+ }
+
+ lseek(fd2, 0, SEEK_SET);
+ ret = read(fd2, tmp, 3);
+ if (ret != 2) {
+ fail("Smth's wrong with file size");
+ return 1;
+ }
+ tmp[2] = '\0';
+ if (strcmp(tmp, "xy")) {
+ fail("Smth's wron with file contents (%s)", tmp);
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/file_attr.c b/test/zdtm/static/file_attr.c
new file mode 100644
index 000000000..80ad78914
--- /dev/null
+++ b/test/zdtm/static/file_attr.c
@@ -0,0 +1,121 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <utime.h>
+#include <sys/stat.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that attributes and content of an open, "
+ "written to, and then unlinked file migrate "
+ "correctly";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+#define DEF_PERMS 06604 /* -rwS--Sr--, really esoteric one */
+unsigned int perms = DEF_PERMS;
+TEST_OPTION(perms, uint, "permissions to set on file "
+ "(default " __stringify(DEF_PERMS) ")", 0);
+#define DEF_MTIME 123456 /* another really esoteric one */
+unsigned int mtime = DEF_MTIME;
+TEST_OPTION(mtime, uint, "mtime to set on file "
+ "(default " __stringify(DEF_MTIME) ")", 0);
+
+
+int main(int argc, char ** argv)
+{
+ int fd;
+ struct utimbuf ut;
+ uint32_t crc;
+ struct stat st;
+ uint8_t buf[1000000];
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ pr_perror("can't write to %s", filename);
+ exit(1);
+ }
+
+ ut = (struct utimbuf) {
+ .actime = 0,
+ .modtime = mtime,
+ };
+ if (utime(filename, &ut)) {
+ pr_perror("can't set modtime %d on %s", mtime, filename);
+ exit(1);
+ }
+
+ if (fchmod(fd, perms)) {
+ pr_perror("can't set perms %o on %s", perms, filename);
+ exit(1);
+ }
+
+ if (unlink(filename)) {
+ pr_perror("can't unlink %s", filename);
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (lseek(fd, 0, SEEK_SET) < 0) {
+ fail("lseeking to the beginning of file failed: %m\n");
+ goto out;
+ }
+
+ if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ fail("can't read %s: %m\n", filename);
+ goto out;
+ }
+
+ crc = ~0;
+ if (datachk(buf, sizeof(buf), &crc)) {
+ fail("CRC mismatch\n");
+ goto out;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ fail("can't fstat %s: %m", filename);
+ goto out;
+ }
+
+ if ((st.st_mode & 07777) != perms) {
+ fail("permissions have changed");
+ goto out;
+ }
+
+ if (st.st_mtime != mtime) {
+ fail("modification time has changed");
+ goto out;
+ }
+
+ if (close(fd)) {
+ fail("close failed: %m\n");
+ goto out_noclose;
+ }
+
+ if (unlink(filename) != -1 || errno != ENOENT) {
+ fail("file %s should have been deleted before migration: unlink: %m\n");
+ goto out_noclose;
+ }
+
+ pass();
+
+out:
+ close(fd);
+out_noclose:
+ return 0;
+}
diff --git a/test/zdtm/static/file_fown.c b/test/zdtm/static/file_fown.c
new file mode 100644
index 000000000..c0732e72a
--- /dev/null
+++ b/test/zdtm/static/file_fown.c
@@ -0,0 +1,183 @@
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+
+#include "zdtmtst.h"
+
+#ifndef F_SETSIG
+#define F_SETSIG 10 /* for sockets. */
+#define F_GETSIG 11 /* for sockets. */
+#endif
+
+const char *test_doc = "Check for signal delivery on file owners";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+struct params {
+ int sigio;
+ int pipe_flags[2];
+ int pipe_pid[2];
+ int pipe_sig[2];
+} *shared;
+
+static void signal_handler_io(int status)
+{
+ shared->sigio++;
+}
+
+static void fill_pipe_params(struct params *p, int *pipes)
+{
+ p->pipe_flags[0] = fcntl(pipes[0], F_GETFL);
+ p->pipe_flags[1] = fcntl(pipes[1], F_GETFL);
+
+ test_msg("pipe_flags0 %08o\n", p->pipe_flags[0]);
+ test_msg("pipe_flags1 %08o\n", p->pipe_flags[1]);
+
+ p->pipe_pid[0] = fcntl(pipes[0], F_GETOWN);
+ p->pipe_pid[1] = fcntl(pipes[1], F_GETOWN);
+
+ p->pipe_sig[0] = fcntl(pipes[0], F_GETSIG);
+ p->pipe_sig[1] = fcntl(pipes[1], F_GETSIG);
+}
+
+static int cmp_pipe_params(struct params *p1, struct params *p2)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (p1->pipe_flags[i] != p2->pipe_flags[i]) {
+ fail("pipe flags failed [%d] expected %08o got %08o\n",
+ i, p1->pipe_flags[i], p2->pipe_flags[i]);
+ return -1;
+ }
+ if (p1->pipe_pid[i] != p2->pipe_pid[i]) {
+ fail("pipe pid failed [%d] expected %d got %d\n",
+ i, p1->pipe_pid[i], p2->pipe_pid[i]);
+ return -1;
+ }
+ if (p1->pipe_sig[i] != p2->pipe_sig[i]) {
+ fail("pipe sig failed [%d] expected %d got %d\n",
+ i, p1->pipe_sig[i], p2->pipe_sig[i]);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct sigaction saio = { };
+ struct params obtained = { };
+ uid_t ruid, euid, suid;
+ int status, pipes[2];
+ pid_t pid;
+
+ test_init(argc, argv);
+
+ shared = (void *)mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if ((void *)shared == MAP_FAILED) {
+ fail("mmap failed");
+ exit(1);
+ }
+
+ if (getresuid(&ruid, &euid, &suid)) {
+ fail("getresuid failed\n");
+ exit(1);
+ }
+
+ if (pipe(pipes)) {
+ pr_perror("Can't create pipe");
+ exit(1);
+ }
+
+ saio.sa_handler = (sig_t)signal_handler_io;
+ saio.sa_flags = SA_RESTART;
+ if (sigaction(SIGIO, &saio, 0)) {
+ fail("sigaction failed\n");
+ exit(1);
+ }
+
+ if (!getuid() && setresuid(-1, 1, -1)) {
+ fail("setresuid failed\n");
+ exit(1);
+ }
+
+ if (fcntl(pipes[0], F_SETOWN, getpid()) ||
+ fcntl(pipes[1], F_SETOWN, getpid()) ||
+ fcntl(pipes[0], F_SETSIG, SIGIO) ||
+ fcntl(pipes[1], F_SETSIG, SIGIO) ||
+ fcntl(pipes[0], F_SETFL, fcntl(pipes[0], F_GETFL) | O_ASYNC) ||
+ fcntl(pipes[1], F_SETFL, fcntl(pipes[1], F_GETFL) | O_ASYNC)) {
+ fail("fcntl failed\n");
+ exit(1);
+ }
+
+ fill_pipe_params(shared, pipes);
+
+ if (setresuid(-1, euid, -1)) {
+ fail("setresuid failed\n");
+ exit(1);
+ }
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("can't fork");
+ exit(1);
+ }
+
+ if (pid == 0) {
+ struct params p = { };
+
+ test_waitsig();
+
+ fcntl(pipes[1], F_SETOWN, getpid());
+ fill_pipe_params(&p, pipes);
+
+ if (write(pipes[1], &p, sizeof(p)) != sizeof(p)) {
+ fail("write failed\n");
+ exit(1);
+ }
+
+ exit(0);
+ }
+
+ test_daemon();
+ test_waitsig();
+ kill(pid, SIGTERM);
+
+ if (waitpid(pid, &status, P_ALL) == -1) {
+ fail("waitpid failed\n");
+ exit(1);
+ }
+
+ if (read(pipes[0], &obtained, sizeof(obtained)) != sizeof(obtained)) {
+ fail("read failed\n");
+ exit(1);
+ }
+
+ if (shared->sigio < 1) {
+ fail("shared->sigio = %d (> 0 expected)\n", shared->sigio);
+ exit(1);
+ }
+
+ shared->pipe_pid[1] = pid;
+
+ if (cmp_pipe_params(shared, &obtained)) {
+ fail("params comparison failed\n");
+ exit(1);
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/file_fown.desc b/test/zdtm/static/file_fown.desc
new file mode 100644
index 000000000..63df42aa6
--- /dev/null
+++ b/test/zdtm/static/file_fown.desc
@@ -0,0 +1 @@
+{'flavor': 'h'}
diff --git a/test/zdtm/static/file_locks00.c b/test/zdtm/static/file_locks00.c
new file mode 100644
index 000000000..f701e00c0
--- /dev/null
+++ b/test/zdtm/static/file_locks00.c
@@ -0,0 +1,185 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <linux/limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that posix flocks are restored";
+const char *test_author = "Qiang Huang <h.huangqiang@huawei.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+char file0[PATH_MAX];
+char file1[PATH_MAX];
+
+static int lock_reg(int fd, int cmd, int type, int whence,
+ off_t offset, off_t len)
+{
+ struct flock lock;
+
+ lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */
+ lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
+ lock.l_start = offset; /* byte offset, relative to l_whence */
+ lock.l_len = len; /* #bytes (0 means to EOF) */
+
+ return fcntl(fd, cmd, &lock);
+}
+
+#define set_read_lock(fd, whence, offset, len) \
+ lock_reg(fd, F_SETLK, F_RDLCK, whence, offset, len)
+#define set_write_lock(fd, whence, offset, len) \
+ lock_reg(fd, F_SETLK, F_WRLCK, whence, offset, len)
+
+static int check_read_lock(int fd, int whence, off_t offset, off_t len)
+{
+ struct flock lock;
+ int ret;
+
+ lock.l_type = F_RDLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */
+ lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
+ lock.l_start = offset; /* byte offset, relative to l_whence */
+ lock.l_len = len; /* #bytes (0 means to EOF) */
+ lock.l_pid = -1;
+
+ ret = fcntl(fd, F_GETLK, &lock);
+ if (ret == -1) {
+ pr_perror("F_GETLK failed.");
+ return -1;
+ }
+
+ if (lock.l_pid == -1) {
+ /* Share lock should succeed. */
+ return 0;
+ }
+
+ fail("Read lock check failed.");
+ return -1;
+}
+
+static int check_write_lock(int fd, int whence, off_t offset, off_t len)
+{
+ struct flock lock;
+
+ int ret;
+ pid_t ppid = getppid();
+
+ lock.l_type = F_WRLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */
+ lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
+ lock.l_start = offset; /* byte offset, relative to l_whence */
+ lock.l_len = len; /* #bytes (0 means to EOF) */
+ lock.l_pid = -1;
+
+ ret = fcntl(fd, F_GETLK, &lock);
+ if (ret == -1) {
+ pr_perror("F_GETLK failed.");
+ return -1;
+ }
+
+ if (lock.l_pid == -1) {
+ fail("Write lock check failed.");
+ return -1;
+ }
+
+ /*
+ * It only succeed when the file lock's owner is exactly
+ * the same as the file lock was dumped.
+ */
+ if (lock.l_pid == ppid)
+ return 0;
+
+ fail("Write lock check failed.");
+ return -1;
+}
+
+static int check_file_locks()
+{
+ int fd_0, fd_1;
+ int ret0, ret1;
+
+ fd_0 = open(file0, O_RDWR | O_CREAT, 0644);
+ if (fd_0 < 0) {
+ pr_perror("Unable to open file %s", file0);
+ return -1;
+ }
+ ret0 = check_read_lock(fd_0, SEEK_SET, 0, 0);
+
+ fd_1 = open(file1, O_RDWR | O_CREAT, 0644);
+ if (fd_1 < 0) {
+ close(fd_0);
+ unlink(file0);
+ pr_perror("Unable to open file %s", file1);
+ return -1;
+ }
+ ret1 = check_write_lock(fd_1, SEEK_SET, 0, 0);
+
+ close(fd_0);
+ close(fd_1);
+
+ return ret0 | ret1;
+}
+
+int main(int argc, char **argv)
+{
+ int fd_0, fd_1;
+ pid_t pid;
+
+ test_init(argc, argv);
+
+ snprintf(file0, sizeof(file0), "%s.0", filename);
+ snprintf(file1, sizeof(file0), "%s.1", filename);
+ fd_0 = open(file0, O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (fd_0 < 0) {
+ pr_perror("Unable to open file %s", file0);
+ return -1;
+ }
+
+ fd_1 = open(file1, O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (fd_1 < 0) {
+ close(fd_0);
+ unlink(file0);
+ pr_perror("Unable to open file %s", file1);
+ return -1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("Can't fork");
+ return -1;
+ }
+
+ if (pid == 0) { /* child will check father's file locks */
+ test_waitsig();
+
+ if (check_file_locks()) {
+ fail("Posix file lock check failed");
+ exit(1);
+ }
+
+ pass();
+ exit(0);
+ }
+
+ set_read_lock(fd_0, SEEK_SET, 0, 0);
+ set_write_lock(fd_1, SEEK_SET, 0, 0);
+
+ test_daemon();
+ test_waitsig();
+
+ kill(pid, SIGTERM);
+ waitpid(pid, NULL, 0);
+ close(fd_0);
+ close(fd_1);
+ unlink(file0);
+ unlink(file1);
+
+ return 0;
+}
diff --git a/test/zdtm/static/file_locks00.desc b/test/zdtm/static/file_locks00.desc
new file mode 100644
index 000000000..80cd04e28
--- /dev/null
+++ b/test/zdtm/static/file_locks00.desc
@@ -0,0 +1 @@
+{'flags': 'excl', 'opts': '--file-locks'}
diff --git a/test/zdtm/static/file_locks00.opts b/test/zdtm/static/file_locks00.opts
new file mode 100644
index 000000000..738083ade
--- /dev/null
+++ b/test/zdtm/static/file_locks00.opts
@@ -0,0 +1 @@
+--file-locks
diff --git a/test/zdtm/static/file_locks01.c b/test/zdtm/static/file_locks01.c
new file mode 100644
index 000000000..24f54acc7
--- /dev/null
+++ b/test/zdtm/static/file_locks01.c
@@ -0,0 +1,188 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+
+#include "zdtmtst.h"
+#include "fs.h"
+
+const char *test_doc = "Check that flock locks are restored";
+const char *test_author = "Qiang Huang <h.huangqiang@huawei.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+char file0[PATH_MAX];
+char file1[PATH_MAX];
+char file2[PATH_MAX];
+unsigned int inodes[3];
+static mnt_info_t *m;
+dev_t dev;
+
+static int open_all_files(int *fd_0, int *fd_1, int *fd_2)
+{
+ struct stat buf;
+
+ snprintf(file0, sizeof(file0), "%s.0", filename);
+ snprintf(file1, sizeof(file0), "%s.1", filename);
+ snprintf(file2, sizeof(file0), "%s.2", filename);
+ *fd_0 = open(file0, O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (*fd_0 < 0) {
+ pr_perror("Unable to open file %s", file0);
+ return -1;
+ }
+
+ fstat(*fd_0, &buf);
+ inodes[0] = buf.st_ino;
+
+ if (!strcmp(m->fsname, "btrfs"))
+ dev = m->s_dev;
+ else
+ dev = buf.st_dev;
+
+ *fd_1 = open(file1, O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (*fd_1 < 0) {
+ close(*fd_0);
+ unlink(file0);
+ pr_perror("Unable to open file %s", file1);
+ return -1;
+ }
+
+ fstat(*fd_1, &buf);
+ inodes[1] = buf.st_ino;
+
+ *fd_2 = open(file2, O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (*fd_2 < 0) {
+ close(*fd_0);
+ close(*fd_1);
+ unlink(file0);
+ unlink(file1);
+ pr_perror("Unable to open file %s", file1);
+ return -1;
+ }
+
+ fstat(*fd_2, &buf);
+ inodes[2] = buf.st_ino;
+
+ return 0;
+}
+
+static int check_file_locks()
+{
+ FILE *fp_locks = NULL;
+ char buf[100];
+
+ long long fl_id = 0;
+ char fl_flag[10], fl_type[15], fl_option[10];
+ pid_t fl_owner;
+ int maj, min;
+ unsigned long i_no;
+ long long start;
+ char end[32];
+
+ int num;
+ int count = 3;
+
+ fp_locks = fopen("/proc/locks", "r");
+ if (!fp_locks)
+ return -1;
+
+ test_msg("C: %d/%d/%d\n", inodes[0], inodes[1], inodes[2]);
+
+ while (fgets(buf, sizeof(buf), fp_locks)) {
+ test_msg("c: %s", buf);
+
+ if (strstr(buf, "->"))
+ continue;
+
+ num = sscanf(buf,
+ "%lld:%s %s %s %d %x:%x:%ld %lld %s",
+ &fl_id, fl_flag, fl_type, fl_option,
+ &fl_owner, &maj, &min, &i_no, &start, end);
+
+ if (num < 10) {
+ pr_perror("Invalid lock info.");
+ break;
+ }
+
+ if (i_no != inodes[0] && i_no != inodes[1] && i_no != inodes[2])
+ continue;
+
+ if (!strcmp(m->fsname, "btrfs")) {
+ if (MKKDEV(major(maj), minor(min)) != dev)
+ continue;
+ } else {
+ if (makedev(maj, min) != dev)
+ continue;
+ }
+
+ if (!strcmp(fl_flag, "FLOCK") && !strcmp(fl_type, "ADVISORY")) {
+ if (!strcmp(fl_option, "READ"))
+ count--;
+ else if (!strcmp(fl_option, "WRITE"))
+ count--;
+ }
+
+ if (!strcmp(fl_flag, "FLOCK") &&
+ !strcmp(fl_type, "MSNFS") &&
+ !strcmp(fl_option, "READ"))
+ count--;
+
+ memset(fl_flag, 0, sizeof(fl_flag));
+ memset(fl_type, 0, sizeof(fl_type));
+ memset(fl_option, 0, sizeof(fl_option));
+ }
+
+ fclose(fp_locks);
+
+ /*
+ * If we find all three matched file locks, count would be 0,
+ * return 0 for success.
+ */
+ return count;
+}
+
+int main(int argc, char **argv)
+{
+ int fd_0, fd_1, fd_2;
+
+ test_init(argc, argv);
+
+ m = get_cwd_mnt_info();
+ if (!m) {
+ pr_perror("Can't fetch mountinfo");
+ return -1;
+ }
+ if (!strcmp(m->fsname, "btrfs"))
+ m->s_dev = kdev_to_odev(m->s_dev);
+
+ if (open_all_files(&fd_0, &fd_1, &fd_2))
+ return -1;
+
+ flock(fd_0, LOCK_SH);
+ flock(fd_1, LOCK_EX);
+ flock(fd_2, LOCK_MAND | LOCK_READ);
+
+ test_daemon();
+ test_waitsig();
+
+ if (check_file_locks())
+ fail("Flock file locks check failed");
+ else
+ pass();
+
+ close(fd_0);
+ close(fd_1);
+ close(fd_2);
+ unlink(file0);
+ unlink(file1);
+ unlink(file2);
+
+ return 0;
+}
diff --git a/test/zdtm/static/file_locks01.desc b/test/zdtm/static/file_locks01.desc
new file mode 100644
index 000000000..80cd04e28
--- /dev/null
+++ b/test/zdtm/static/file_locks01.desc
@@ -0,0 +1 @@
+{'flags': 'excl', 'opts': '--file-locks'}
diff --git a/test/zdtm/static/file_locks01.opts b/test/zdtm/static/file_locks01.opts
new file mode 100644
index 000000000..738083ade
--- /dev/null
+++ b/test/zdtm/static/file_locks01.opts
@@ -0,0 +1 @@
+--file-locks
diff --git a/test/zdtm/static/file_locks02.c b/test/zdtm/static/file_locks02.c
new file mode 100644
index 000000000..2db3bd0a0
--- /dev/null
+++ b/test/zdtm/static/file_locks02.c
@@ -0,0 +1,99 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that 'shared' flocks work";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+static int check_file_locks(pid_t child)
+{
+ FILE *fp_locks = NULL;
+ char buf[100], fl_flag[16], fl_type[16], fl_option[16];
+ pid_t pid = getpid();
+ int found = 0, num, fl_owner;
+
+ fp_locks = fopen("/proc/locks", "r");
+ if (!fp_locks)
+ return -1;
+
+ test_msg("C: %d\n", pid);
+
+ while (fgets(buf, sizeof(buf), fp_locks)) {
+ test_msg("c: %s", buf);
+
+ if (strstr(buf, "->"))
+ continue;
+
+ num = sscanf(buf,
+ "%*d:%s %s %s %d %*02x:%*02x:%*d %*d %*s",
+ fl_flag, fl_type, fl_option, &fl_owner);
+
+ if (num < 4) {
+ pr_perror("Invalid lock info.");
+ break;
+ }
+
+ if (fl_owner != pid && fl_owner != child)
+ continue;
+
+ if (!strcmp(fl_flag, "FLOCK") &&
+ !strcmp(fl_type, "ADVISORY") &&
+ !strcmp(fl_option, "WRITE"))
+ found++;
+
+ memset(fl_flag, 0, sizeof(fl_flag));
+ memset(fl_type, 0, sizeof(fl_type));
+ memset(fl_option, 0, sizeof(fl_option));
+ }
+
+ fclose(fp_locks);
+
+ return found == 1;
+}
+
+int main(int argc, char **argv)
+{
+ int fd, pid;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_CREAT | O_RDWR, 0600);
+ if (fd < 0) {
+ pr_perror("No file");
+ return -1;
+ }
+
+ flock(fd, LOCK_EX);
+
+ pid = fork();
+ if (pid == 0) {
+ test_waitsig();
+ exit(0);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (check_file_locks(pid))
+ pass();
+ else
+ fail("Flock file locks check failed");
+
+ kill(pid, SIGTERM);
+ waitpid(pid, NULL, 0);
+ close(fd);
+ unlink(filename);
+
+ return 0;
+}
diff --git a/test/zdtm/static/file_locks02.desc b/test/zdtm/static/file_locks02.desc
new file mode 100644
index 000000000..80cd04e28
--- /dev/null
+++ b/test/zdtm/static/file_locks02.desc
@@ -0,0 +1 @@
+{'flags': 'excl', 'opts': '--file-locks'}
diff --git a/test/zdtm/static/file_locks02.opts b/test/zdtm/static/file_locks02.opts
new file mode 100644
index 000000000..738083ade
--- /dev/null
+++ b/test/zdtm/static/file_locks02.opts
@@ -0,0 +1 @@
+--file-locks
diff --git a/test/zdtm/static/file_locks03.c b/test/zdtm/static/file_locks03.c
new file mode 100644
index 000000000..167fd2e19
--- /dev/null
+++ b/test/zdtm/static/file_locks03.c
@@ -0,0 +1,101 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that 'inherited' flocks work";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+static int check_file_locks(int alt_pid)
+{
+ FILE *fp_locks = NULL;
+ char buf[100], fl_flag[16], fl_type[16], fl_option[16];
+ pid_t pid = getpid();
+ int found = 0, num, fl_owner;
+
+ fp_locks = fopen("/proc/locks", "r");
+ if (!fp_locks)
+ return -1;
+
+ test_msg("C: %d/%d\n", pid, alt_pid);
+
+ while (fgets(buf, sizeof(buf), fp_locks)) {
+ test_msg("c: %s", buf);
+
+ if (strstr(buf, "->"))
+ continue;
+
+ num = sscanf(buf,
+ "%*d:%s %s %s %d %*02x:%*02x:%*d %*d %*s",
+ fl_flag, fl_type, fl_option, &fl_owner);
+
+ if (num < 4) {
+ pr_perror("Invalid lock info.");
+ break;
+ }
+
+ if (fl_owner != pid && fl_owner != alt_pid)
+ continue;
+
+ if (!strcmp(fl_flag, "FLOCK") &&
+ !strcmp(fl_type, "ADVISORY") &&
+ !strcmp(fl_option, "WRITE"))
+ found++;
+
+ memset(fl_flag, 0, sizeof(fl_flag));
+ memset(fl_type, 0, sizeof(fl_type));
+ memset(fl_option, 0, sizeof(fl_option));
+ }
+
+ fclose(fp_locks);
+
+ return found == 1;
+}
+
+int main(int argc, char **argv)
+{
+ int fd, pid;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_CREAT | O_RDWR, 0600);
+ if (fd < 0) {
+ pr_perror("No file");
+ return -1;
+ }
+
+ flock(fd, LOCK_EX);
+
+ pid = fork();
+ if (pid == 0) {
+ test_waitsig();
+ exit(0);
+ }
+
+ close(fd);
+
+ test_daemon();
+ test_waitsig();
+
+ if (check_file_locks(pid))
+ pass();
+ else
+ fail("Flock file locks check failed");
+
+ kill(pid, SIGTERM);
+ waitpid(pid, NULL, 0);
+ close(fd);
+ unlink(filename);
+
+ return 0;
+}
diff --git a/test/zdtm/static/file_locks03.desc b/test/zdtm/static/file_locks03.desc
new file mode 100644
index 000000000..80cd04e28
--- /dev/null
+++ b/test/zdtm/static/file_locks03.desc
@@ -0,0 +1 @@
+{'flags': 'excl', 'opts': '--file-locks'}
diff --git a/test/zdtm/static/file_locks03.opts b/test/zdtm/static/file_locks03.opts
new file mode 100644
index 000000000..738083ade
--- /dev/null
+++ b/test/zdtm/static/file_locks03.opts
@@ -0,0 +1 @@
+--file-locks
diff --git a/test/zdtm/static/file_locks04.c b/test/zdtm/static/file_locks04.c
new file mode 100644
index 000000000..995dd099c
--- /dev/null
+++ b/test/zdtm/static/file_locks04.c
@@ -0,0 +1,119 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that 'overlapping' flocks work";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+static int check_file_locks(int alt_pid, int fd)
+{
+ FILE *fp_locks = NULL;
+ char buf[100], fl_flag[16], fl_type[16], fl_option[16];
+ pid_t pid = getpid();
+ int found = 0, num, fl_owner;
+
+ fp_locks = fopen("/proc/locks", "r");
+ if (!fp_locks)
+ return -1;
+
+ test_msg("C: %d/%d\n", pid, alt_pid);
+
+ while (fgets(buf, sizeof(buf), fp_locks)) {
+ test_msg("c: %s", buf);
+
+ if (strstr(buf, "->"))
+ continue;
+
+ num = sscanf(buf,
+ "%*d:%s %s %s %d %*02x:%*02x:%*d %*d %*s",
+ fl_flag, fl_type, fl_option, &fl_owner);
+
+ if (num < 4) {
+ pr_perror("Invalid lock info.");
+ break;
+ }
+
+ if (fl_owner != pid && fl_owner != alt_pid)
+ continue;
+
+ if (!strcmp(fl_flag, "FLOCK") &&
+ !strcmp(fl_type, "ADVISORY") &&
+ !strcmp(fl_option, "WRITE"))
+ found++;
+
+ memset(fl_flag, 0, sizeof(fl_flag));
+ memset(fl_type, 0, sizeof(fl_type));
+ memset(fl_option, 0, sizeof(fl_option));
+ }
+
+ fclose(fp_locks);
+
+ if (flock(fd, LOCK_EX | LOCK_NB) == 0)
+ return 0;
+
+ return found == 1;
+}
+
+int main(int argc, char **argv)
+{
+ int fd, pid;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_CREAT | O_RDWR, 0600);
+ if (fd < 0) {
+ pr_perror("No file");
+ return -1;
+ }
+
+ flock(fd, LOCK_EX);
+
+ pid = fork();
+ if (pid == 0) {
+ test_waitsig();
+ exit(0);
+ }
+
+ close(fd);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("No file 2");
+ kill(pid, SIGTERM);
+ waitpid(pid, NULL, 0);
+ return -1;
+ }
+
+ if (flock(fd, LOCK_EX | LOCK_NB) == 0) {
+ pr_perror("Bogus locks");
+ kill(pid, SIGTERM);
+ waitpid(pid, NULL, 0);
+ return -1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (check_file_locks(pid, fd))
+ pass();
+ else
+ fail("Flock file locks check failed");
+
+ kill(pid, SIGTERM);
+ waitpid(pid, NULL, 0);
+ close(fd);
+ unlink(filename);
+
+ return 0;
+}
diff --git a/test/zdtm/static/file_locks04.desc b/test/zdtm/static/file_locks04.desc
new file mode 100644
index 000000000..80cd04e28
--- /dev/null
+++ b/test/zdtm/static/file_locks04.desc
@@ -0,0 +1 @@
+{'flags': 'excl', 'opts': '--file-locks'}
diff --git a/test/zdtm/static/file_locks04.opts b/test/zdtm/static/file_locks04.opts
new file mode 100644
index 000000000..738083ade
--- /dev/null
+++ b/test/zdtm/static/file_locks04.opts
@@ -0,0 +1 @@
+--file-locks
diff --git a/test/zdtm/static/file_locks05.c b/test/zdtm/static/file_locks05.c
new file mode 100644
index 000000000..f7d34b19e
--- /dev/null
+++ b/test/zdtm/static/file_locks05.c
@@ -0,0 +1,52 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Sanity check for criu lock-test quirk";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char **argv)
+{
+ int fd, fd2;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_CREAT | O_RDWR, 0600);
+ if (fd < 0) {
+ pr_perror("No file");
+ return -1;
+ }
+
+ fd2 = open(filename, O_RDWR);
+ if (fd2 < 0) {
+ pr_perror("No file2");
+ return -1;
+ }
+
+ flock(fd, LOCK_SH);
+
+ test_daemon();
+ test_waitsig();
+
+ if (flock(fd2, LOCK_SH) == 0)
+ pass();
+ else
+ fail("Flock file locks check failed (%d)", errno);
+
+ close(fd);
+ close(fd2);
+ unlink(filename);
+
+ return 0;
+}
diff --git a/test/zdtm/static/file_locks05.desc b/test/zdtm/static/file_locks05.desc
new file mode 100644
index 000000000..80cd04e28
--- /dev/null
+++ b/test/zdtm/static/file_locks05.desc
@@ -0,0 +1 @@
+{'flags': 'excl', 'opts': '--file-locks'}
diff --git a/test/zdtm/static/file_locks05.opts b/test/zdtm/static/file_locks05.opts
new file mode 100644
index 000000000..738083ade
--- /dev/null
+++ b/test/zdtm/static/file_locks05.opts
@@ -0,0 +1 @@
+--file-locks
diff --git a/test/zdtm/static/file_shared.c b/test/zdtm/static/file_shared.c
new file mode 100644
index 000000000..8f1acb160
--- /dev/null
+++ b/test/zdtm/static/file_shared.c
@@ -0,0 +1,117 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include "zdtmtst.h"
+#define OFFSET 1000
+#define OFFSET2 500
+
+const char *test_doc = "Check shared struct file-s";
+const char *test_author = "Andrey Vagin <avagin@openvz.org>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char **argv)
+{
+ pid_t pid;
+ int fd, fd2, fd3, ret, status;
+ off_t off;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_RDWR | O_CREAT, 0644);
+ if (fd == -1)
+ return 1;
+
+ fd2 = dup(fd);
+ if (fd < 0)
+ return 1;
+
+ fd3 = open(filename, O_RDWR | O_CREAT, 0644);
+ if (fd3 == -1)
+ return 1;
+
+ pid = test_fork();
+
+ if (pid == -1)
+ return 1;
+ else if (pid) {
+ fcntl(fd2, F_SETFD, 1);
+
+ test_daemon();
+ test_waitsig();
+ off = lseek(fd, OFFSET, SEEK_SET);
+ if (off == (off_t) -1)
+ return 1;
+
+ off = lseek(fd3, OFFSET2, SEEK_SET);
+ if (off == (off_t) -1)
+ return 1;
+
+ ret = kill(pid, SIGTERM);
+ if (ret == -1) {
+ pr_perror("kill() failed");
+ }
+
+ ret = wait(&status);
+ if (ret == -1) {
+ pr_perror("wait() failed");
+ return 1;
+ }
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+ fail("Child exited with non-zero status");
+ return 1;
+ }
+ off = lseek(fd2, 0, SEEK_CUR);
+ if (off != OFFSET) {
+ fail("offset1 fail\n");
+ return 1;
+ }
+ off = lseek(fd3, 0, SEEK_CUR);
+ if (off != OFFSET2) {
+ fail("offset2 fail\n");
+ return 1;
+ }
+
+ ret = fcntl(fd, F_GETFD, 0);
+ if (ret != 0) {
+ fail("fd cloexec broken\n");
+ return 1;
+ }
+
+ ret = fcntl(fd2, F_GETFD, 0);
+ if (ret != 1) {
+ fail("fd2 cloexec broken\n");
+ return 1;
+ }
+
+ } else {
+ test_waitsig();
+ off = lseek(fd, 0, SEEK_CUR);
+ if (off != OFFSET) {
+ fail("offset3 fail\n");
+ return 1;
+ }
+ off = lseek(fd2, 0, SEEK_CUR);
+ if (off != OFFSET) {
+ fail("offset4 fail\n");
+ return 1;
+ }
+ off = lseek(fd3, 0, SEEK_CUR);
+ if (off != OFFSET2) {
+ fail("offset5 fail\n");
+ return 1;
+ }
+ return 0;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/fpu00.c b/test/zdtm/static/fpu00.c
new file mode 100644
index 000000000..f04fac4f2
--- /dev/null
+++ b/test/zdtm/static/fpu00.c
@@ -0,0 +1,85 @@
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Start a calculation, leaving FPU in a certain state,\n"
+"before migration, continue after";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+#if defined(__i386__) || defined(__x86_64__)
+void start(float a, float b, float c, float d)
+{
+ __asm__ volatile (
+ "fld %0\n"
+ "fadd %1\n"
+ "fld %2\n"
+ "fadd %3\n"
+ "fmulp %%st(1)\n"
+ :
+ : "m" (a), "m" (b), "m" (c), "m" (d)
+ );
+}
+
+float finish(void)
+{
+ float res;
+
+ __asm__ volatile (
+ "fstp %0\n"
+ : "=m" (res)
+ );
+ return res;
+}
+
+int chk_proc_fpu(void)
+{
+ unsigned long fi;
+
+ __asm__ volatile (
+ "mov $1, %%eax\n"
+ "cpuid\n"
+ : "=d" (fi) : : "eax"
+ );
+ return fi & (1 << 0);
+}
+#endif
+
+int main(int argc, char ** argv)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ float a, b, c, d;
+ float res1, res2;
+#endif
+
+ test_init(argc, argv);
+#if defined(__i386__) || defined(__x86_64__)
+ if (!chk_proc_fpu()) {
+ skip("FPU not supported");
+ return 1;
+ }
+
+ a = drand48();
+ b = drand48();
+ c = drand48();
+ d = drand48();
+
+
+ start(a, b, c, d);
+ res1 = finish();
+
+ start(a, b, c, d);
+
+ test_daemon();
+ test_waitsig();
+
+ res2 = finish();
+
+ if (res1 != res2)
+ fail("%f != %f\n", res1, res2);
+ else
+ pass();
+#else
+ skip("Unsupported arch");
+#endif
+ return 0;
+}
diff --git a/test/zdtm/static/fpu00.desc b/test/zdtm/static/fpu00.desc
new file mode 100644
index 000000000..d2f501de2
--- /dev/null
+++ b/test/zdtm/static/fpu00.desc
@@ -0,0 +1 @@
+{'arch': 'x86_64'}
diff --git a/test/zdtm/static/fpu01.c b/test/zdtm/static/fpu01.c
new file mode 100644
index 000000000..c8e6ca19e
--- /dev/null
+++ b/test/zdtm/static/fpu01.c
@@ -0,0 +1,119 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+
+#include "zdtmtst.h"
+
+#if defined(__x86_64__)
+
+#include "cpuid.h"
+
+const char *test_doc = "Test if FPU data in YMM registers do survive the c/r";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+static int verify_cpu(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ /* Do we have xsave? */
+ cpuid(1, &eax, &ebx, &ecx, &edx);
+ if (!(ecx & (1u << 26)))
+ return -1;
+
+ /* Is YMM here? */
+ cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
+ if ((eax & (0x4)) != 0x4)
+ return -1;
+
+ return 0;
+}
+
+#define __aligned __attribute__((aligned(64)))
+
+/*
+ * These are random strings generated by pwgen.
+ */
+static __aligned unsigned char ymm1[32 + 1] = "code9Ee5sohphie1ae1kaeMahngoh5oe";
+static __aligned unsigned char ymm2[32 + 1] = "Tacuthahhien9Fi7aGhaa5toGh6vi7Ch";
+
+static __aligned unsigned char ymm3[32 + 1];
+static __aligned unsigned char ymm4[32 + 1];
+
+static int fpu_test(void)
+{
+ int ret = 0;
+
+ asm volatile("vmovapd %0, %%ymm0 \n"
+ :
+ : "m" (*ymm1)
+ : "memory");
+
+ asm volatile("vmovapd %0, %%ymm7 \n"
+ :
+ : "m" (*ymm2)
+ : "memory");
+
+ test_daemon();
+ test_waitsig();
+
+ asm volatile("vmovapd %%ymm0, %0 \n"
+ : "=m" (*ymm3)
+ :
+ : "memory");
+
+ asm volatile("vmovapd %%ymm7, %0 \n"
+ : "=m" (*ymm4)
+ :
+ : "memory");
+
+ if (memcmp(ymm1, ymm3, 32) || memcmp(ymm2, ymm4, 32)) {
+ test_msg("Data mismatch ('%s' '%s' '%s' '%s')\n",
+ ymm1, ymm2, ymm3, ymm4);
+ ret = -1;
+ } else {
+ test_msg("Data match ('%s' '%s' '%s' '%s')\n",
+ ymm1, ymm2, ymm3, ymm4);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int bare_run(void)
+{
+ test_msg("Your cpu doesn't support ymm registers, skipping\n");
+
+ test_daemon();
+ test_waitsig();
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+
+ test_init(argc, argv);
+
+ ret = verify_cpu() ? bare_run() : fpu_test();
+
+ if (!ret)
+ pass();
+ else
+ fail();
+
+ return 0;
+}
+
+#else
+
+int main(int argc, char *argv[])
+{
+ test_init(argc, argv);
+ skip("Unsupported arch");
+ return 0;
+}
+
+#endif
diff --git a/test/zdtm/static/fpu01.desc b/test/zdtm/static/fpu01.desc
new file mode 100644
index 000000000..d2f501de2
--- /dev/null
+++ b/test/zdtm/static/fpu01.desc
@@ -0,0 +1 @@
+{'arch': 'x86_64'}
diff --git a/test/zdtm/static/futex-rl.c b/test/zdtm/static/futex-rl.c
new file mode 100644
index 000000000..9a3181a2c
--- /dev/null
+++ b/test/zdtm/static/futex-rl.c
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <sys/mman.h>
+#include <sys/syscall.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check the futex robust list c/r";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+struct args {
+ task_waiter_t waiter;
+ int result;
+};
+
+static pid_t __gettid(void)
+{
+ return syscall(__NR_gettid);
+}
+
+void *thread_fn(void *arg)
+{
+ struct robust_list_head *head_orig = NULL, *head_new = NULL;
+ size_t len_orig = 0, len_new = 0;
+ struct args *args = arg;
+
+ test_msg("Obtaining old RL\n");
+ if (syscall(__NR_get_robust_list, __gettid(), &head_orig, &len_orig)) {
+ args->result = -1;
+ fail("__NR_get_robust_list failed");
+ }
+
+ test_msg("Complete\n");
+ task_waiter_complete(&args->waiter, 1);
+ if (args->result == -1)
+ goto out;
+
+ task_waiter_wait4(&args->waiter, 2);
+
+ test_msg("Obtaining new RL\n");
+ if (syscall(__NR_get_robust_list, __gettid(), &head_new, &len_new)) {
+ args->result = -1;
+ fail("__NR_get_robust_list failed");
+ }
+ if (args->result == -1)
+ goto out;
+
+ if (head_orig != head_new || len_orig != len_new) {
+ args->result = -1;
+ fail("comparison failed");
+ }
+
+ args->result = 0;
+out:
+ return NULL;
+}
+
+int main(int argc, char **argv)
+{
+ struct robust_list_head *head_orig = NULL, *head_new = NULL;
+ size_t len_orig = 0, len_new = 0;
+ pthread_t thread;
+ struct args *args;
+
+ test_init(argc, argv);
+
+ args = (struct args *)mmap(NULL, sizeof(*args), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if ((void *)args == MAP_FAILED) {
+ fail("mmap failed\n");
+ exit(1);
+ }
+
+ test_msg("Obtaining old RL for thread-leader\n");
+ if (syscall(__NR_get_robust_list, __gettid(), &head_orig, &len_orig)) {
+ fail("__NR_get_robust_list failed");
+ exit(1);
+ }
+
+ task_waiter_init(&args->waiter);
+ args->result = 0;
+
+ test_msg("Createing thread\n");
+ if (pthread_create(&thread, NULL, thread_fn, (void *)args)) {
+ fail("Can't create thread\n");
+ exit(1);
+ }
+
+ test_msg("Wait for thread work\n");
+ task_waiter_wait4(&args->waiter, 1);
+ if (args->result == -1) {
+ fail("thread failed\n");
+ exit(1);
+ }
+
+ test_msg("C/R cycle\n");
+ test_daemon();
+ test_waitsig();
+
+ task_waiter_complete(&args->waiter, 2);
+
+ test_msg("Obtaining new RL for thread-leader\n");
+ if (syscall(__NR_get_robust_list, __gettid(), &head_new, &len_new)) {
+ fail("__NR_get_robust_list failed");
+ exit(1);
+ }
+
+ if (head_orig != head_new || len_orig != len_new) {
+ fail("comparison failed");
+ exit(1);
+ }
+
+ pthread_join(thread, NULL);
+ if (args->result)
+ fail();
+ else
+ pass();
+
+ munmap((void *)args, sizeof(*args));
+
+ return 0;
+}
diff --git a/test/zdtm/static/futex.c b/test/zdtm/static/futex.c
new file mode 100644
index 000000000..2ad82d28b
--- /dev/null
+++ b/test/zdtm/static/futex.c
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check (via pthread/NPTL) that futeces behave through migration";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+volatile int kid_passed;
+
+void *thread_fn(void *lock)
+{
+ pthread_mutex_t *mutex;
+
+ mutex = (pthread_mutex_t *)lock;
+ pthread_mutex_lock(mutex);
+ kid_passed++;
+ pthread_mutex_unlock(mutex);
+ return NULL;
+}
+
+#define DEF_NUM_THREADS 10
+#define MAX_NUM_THREADS 50
+int num_threads = DEF_NUM_THREADS;
+TEST_OPTION(num_threads, int, "number of threads "
+ "(default " __stringify(DEF_NUM_THREADS)
+ " maximum " __stringify(MAX_NUM_THREADS) ")", 0);
+
+int main(int argc, char **argv)
+{
+ int i;
+ pthread_t thr[num_threads];
+ pthread_mutex_t m;
+
+ test_init(argc, argv);
+
+ if (num_threads > MAX_NUM_THREADS) {
+ pr_perror("%d threads it too much. max is %d",
+ num_threads, MAX_NUM_THREADS);
+ goto out;
+ }
+
+ pthread_mutex_init(&m, NULL);
+ pthread_mutex_lock(&m);
+
+ for (i = 0; i < num_threads; i++)
+ if (pthread_create(&thr[i], NULL, thread_fn, &m)) {
+ pr_perror("Can't create %d'th thread", i + 1);
+ goto out_kill;
+ }
+
+ kid_passed = 0;
+
+ test_daemon();
+ test_waitsig();
+
+ sleep(1);
+ if (kid_passed != 0)
+ fail("some kids broke through\n");
+
+ pthread_mutex_unlock(&m);
+ for (i = 0; i < num_threads; i++)
+ pthread_join(thr[i], NULL);
+
+ if (pthread_mutex_trylock(&m)) {
+ if (errno == EBUSY)
+ fail("kids left my mutex locked\n");
+ else
+ pr_perror("kids spoiled my mutex");
+ }
+
+ if (kid_passed != num_threads)
+ fail("some kids died during migration\n");
+
+ pass();
+out:
+ return 0;
+
+out_kill:
+ for (i--; i >= 0; i--) {
+ pthread_kill(thr[i], SIGKILL);
+ pthread_join(thr[i], NULL);
+ }
+ goto out;
+}
diff --git a/test/zdtm/static/get_smaps_bits.c b/test/zdtm/static/get_smaps_bits.c
new file mode 100644
index 000000000..9253f4db6
--- /dev/null
+++ b/test/zdtm/static/get_smaps_bits.c
@@ -0,0 +1,127 @@
+#include <string.h>
+#include <sys/mman.h>
+#include "zdtmtst.h"
+
+#ifndef MAP_HUGETLB
+# define MAP_HUGETLB 0x40000
+#endif
+
+#ifndef MADV_HUGEPAGE
+# define MADV_HUGEPAGE 14
+#endif
+
+#ifndef MADV_NOHUGEPAGE
+# define MADV_NOHUGEPAGE 15
+#endif
+
+#ifndef MADV_DONTDUMP
+# define MADV_DONTDUMP 16
+#endif
+
+static void parse_vmflags(char *buf, unsigned long *flags, unsigned long *madv)
+{
+ char *tok;
+
+ if (!buf[0])
+ return;
+
+ tok = strtok(buf, " \n");
+ if (!tok)
+ return;
+
+#define _vmflag_match(_t, _s) (_t[0] == _s[0] && _t[1] == _s[1])
+
+ do {
+ /* mmap() block */
+ if (_vmflag_match(tok, "gd"))
+ *flags |= MAP_GROWSDOWN;
+ else if (_vmflag_match(tok, "lo"))
+ *flags |= MAP_LOCKED;
+ else if (_vmflag_match(tok, "nr"))
+ *flags |= MAP_NORESERVE;
+ else if (_vmflag_match(tok, "ht"))
+ *flags |= MAP_HUGETLB;
+
+ /* madvise() block */
+ if (_vmflag_match(tok, "sr"))
+ *madv |= (1ul << MADV_SEQUENTIAL);
+ else if (_vmflag_match(tok, "rr"))
+ *madv |= (1ul << MADV_RANDOM);
+ else if (_vmflag_match(tok, "dc"))
+ *madv |= (1ul << MADV_DONTFORK);
+ else if (_vmflag_match(tok, "dd"))
+ *madv |= (1ul << MADV_DONTDUMP);
+ else if (_vmflag_match(tok, "mg"))
+ *madv |= (1ul << MADV_MERGEABLE);
+ else if (_vmflag_match(tok, "hg"))
+ *madv |= (1ul << MADV_HUGEPAGE);
+ else if (_vmflag_match(tok, "nh"))
+ *madv |= (1ul << MADV_NOHUGEPAGE);
+
+ /*
+ * Anything else is just ignored.
+ */
+ } while ((tok = strtok(NULL, " \n")));
+
+#undef _vmflag_match
+}
+
+#define is_hex_digit(c) \
+ (((c) >= '0' && (c) <= '9') || \
+ ((c) >= 'a' && (c) <= 'f') || \
+ ((c) >= 'A' && (c) <= 'F'))
+
+static int is_vma_range_fmt(char *line, unsigned long *start, unsigned long *end)
+{
+ char *p = line;
+ while (*line && is_hex_digit(*line))
+ line++;
+
+ if (*line++ != '-')
+ return 0;
+
+ while (*line && is_hex_digit(*line))
+ line++;
+
+ if (*line++ != ' ')
+ return 0;
+
+ sscanf(p, "%lx-%lx", start, end);
+ return 1;
+}
+
+int get_smaps_bits(unsigned long where, unsigned long *flags, unsigned long *madv)
+{
+ unsigned long start = 0, end = 0;
+ FILE *smaps = NULL;
+ char buf[1024];
+ int found = 0;
+
+ if (!where)
+ return 0;
+
+ smaps = fopen("/proc/self/smaps", "r");
+ if (!smaps) {
+ pr_perror("Can't open smaps");
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), smaps)) {
+ is_vma_range_fmt(buf, &start, &end);
+
+ if (!strncmp(buf, "VmFlags: ", 9) && start == where) {
+ found = 1;
+ parse_vmflags(buf, flags, madv);
+ break;
+ }
+ }
+
+ fclose(smaps);
+
+ if (!found) {
+ pr_perror("VmFlags not found for %lx", where);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/test/zdtm/static/groups.c b/test/zdtm/static/groups.c
new file mode 100644
index 000000000..1aed8f50b
--- /dev/null
+++ b/test/zdtm/static/groups.c
@@ -0,0 +1,64 @@
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <grp.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that supplementary groups are supported";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+int main(int argc, char **argv)
+{
+ unsigned int ng, *grp, *grp2, i, max;
+
+ test_init(argc, argv);
+
+ ng = getgroups(0, NULL);
+ if (ng < 0) {
+ pr_perror("Can't get groups");
+ return -1;
+ }
+
+ grp = malloc((ng + 1) * sizeof(*grp));
+ ng = getgroups(ng, grp);
+ if (ng < 0) {
+ pr_perror("Can't get groups2");
+ return -1;
+ }
+
+ max = 0;
+ for (i = 0; i < ng; i++)
+ if (max < grp[i])
+ max = grp[i];
+
+ grp[ng++] = max + 1;
+
+ if (setgroups(ng, grp) < 0) {
+ pr_perror("Can't set groups");
+ return -1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ grp2 = malloc(ng * sizeof(*grp2));
+
+ if (getgroups(ng, grp2) != ng) {
+ fail("Nr groups changed");
+ return -1;
+ }
+
+ if (memcmp(grp, grp2, ng * sizeof(*grp))) {
+ fail("Groups have changed");
+ return -1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/groups.desc b/test/zdtm/static/groups.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/groups.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/grow_map.c b/test/zdtm/static/grow_map.c
new file mode 100644
index 000000000..c26cfc84a
--- /dev/null
+++ b/test/zdtm/static/grow_map.c
@@ -0,0 +1,66 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that VMA-s with MAP_GROWSDOWN are restored correctly";
+const char *test_author = "Andrew Vagin <avagin@openvz.org>";
+
+int main(int argc, char **argv)
+{
+ char *start_addr, *fake_grow_down, *test_addr, *grow_down;
+ test_init(argc, argv);
+
+ start_addr = mmap(NULL, PAGE_SIZE * 10, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (start_addr == MAP_FAILED) {
+ pr_perror("Can't mal a new region");
+ return 1;
+ }
+ munmap(start_addr, PAGE_SIZE * 10);
+
+ fake_grow_down = mmap(start_addr + PAGE_SIZE * 5, PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED | MAP_GROWSDOWN, -1, 0);
+ if (fake_grow_down == MAP_FAILED) {
+ pr_perror("Can't mal a new region");
+ return 1;
+ }
+
+ fake_grow_down[0] = 'c';
+ *(fake_grow_down - 1) = 'b';
+
+ /* overlap the guard page of fake_grow_down */
+ test_addr = mmap(start_addr + PAGE_SIZE * 3, PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
+ if (test_addr == MAP_FAILED) {
+ pr_perror("Can't mal a new region");
+ return 1;
+ }
+
+ grow_down = mmap(start_addr + PAGE_SIZE * 2, PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED | MAP_GROWSDOWN, -1, 0);
+ if (grow_down == MAP_FAILED) {
+ pr_perror("Can't mal a new region");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ munmap(test_addr, PAGE_SIZE);
+ if (fake_grow_down[0] != 'c' || *(fake_grow_down - 1) != 'b') {
+ fail("%c %c\n", fake_grow_down[0], *(fake_grow_down - 1));
+ return 1;
+ }
+
+ grow_down[0] = 'z';
+ *(grow_down - 1) = 'x';
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/grow_map02.c b/test/zdtm/static/grow_map02.c
new file mode 100644
index 000000000..0b93714f4
--- /dev/null
+++ b/test/zdtm/static/grow_map02.c
@@ -0,0 +1,63 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that a few grow-down VMA-s are restored correctly";
+const char *test_author = "Andrew Vagin <avagin@openvz.org>";
+
+int main(int argc, char **argv)
+{
+ char *start_addr, *grow_down;
+ test_init(argc, argv);
+
+ start_addr = mmap(NULL, PAGE_SIZE * 10, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (start_addr == MAP_FAILED) {
+ pr_perror("Can't mal a new region");
+ return 1;
+ }
+ munmap(start_addr, PAGE_SIZE * 10);
+
+ grow_down = mmap(start_addr + PAGE_SIZE * 3, PAGE_SIZE * 3,
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED | MAP_GROWSDOWN, -1, 0);
+ if (grow_down == MAP_FAILED) {
+ pr_perror("Can't mal a new region");
+ return 1;
+ }
+
+ grow_down[0 * PAGE_SIZE] = 'x';
+ grow_down[1 * PAGE_SIZE] = 'y';
+ grow_down[2 * PAGE_SIZE] = 'z';
+
+ /*
+ * Split the grow-down vma on three parts.
+ * Only the irst one will have a guard page
+ */
+ if (mprotect(grow_down + PAGE_SIZE, PAGE_SIZE, PROT_READ)) {
+ pr_perror("Can't change set protection on a region of memory");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ test_msg("%c %c %c\n", grow_down[0 * PAGE_SIZE],
+ grow_down[1 * PAGE_SIZE], grow_down[2 * PAGE_SIZE]);
+
+ if (grow_down[0 * PAGE_SIZE] != 'x')
+ return 1;
+ if (grow_down[1 * PAGE_SIZE] != 'y')
+ return 1;
+ if (grow_down[2 * PAGE_SIZE] != 'z')
+ return 1;
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/grow_map03.c b/test/zdtm/static/grow_map03.c
new file mode 100644
index 000000000..6310386a4
--- /dev/null
+++ b/test/zdtm/static/grow_map03.c
@@ -0,0 +1,40 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that VMA-s with MAP_GROWSDOWN are restored correctly";
+const char *test_author = "Andrew Vagin <avagin@openvz.org>";
+
+/*
+* This test case creates two consecutive grows down vmas with a hole
+* between them.
+*/
+
+int main(int argc, char **argv)
+{
+ char *start_addr, *addr1, *addr2;
+
+ test_init(argc, argv);
+
+ start_addr = mmap(NULL, PAGE_SIZE * 10, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (start_addr == MAP_FAILED) {
+ pr_perror("Can't mal a new region");
+ return 1;
+ }
+ munmap(start_addr, PAGE_SIZE * 10);
+
+ addr1 = mmap(start_addr + PAGE_SIZE * 5, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0);
+ addr2 = mmap(start_addr + PAGE_SIZE * 3, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0);
+
+ test_msg("%p %p\n", addr1, addr2);
+
+ test_daemon();
+ test_waitsig();
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/inotify00.c b/test/zdtm/static/inotify00.c
new file mode 100644
index 000000000..00190dc58
--- /dev/null
+++ b/test/zdtm/static/inotify00.c
@@ -0,0 +1,257 @@
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include <unistd.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <signal.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check for inotify delivery";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+#define TEST_FILE "inotify-removed"
+#define TEST_LINK "inotify-hardlink"
+
+#define BUFF_SIZE ((sizeof(struct inotify_event) + PATH_MAX))
+
+static void decode_event_mask(char *buf, size_t size, unsigned int mask)
+{
+ static const char *names[32] = {
+ [ 0] = "IN_ACCESS",
+ [ 1] = "IN_MODIFY",
+ [ 2] = "IN_ATTRIB",
+ [ 3] = "IN_CLOSE_WRITE",
+ [ 4] = "IN_CLOSE_NOWRITE",
+ [ 5] = "IN_OPEN",
+ [ 6] = "IN_MOVED_FROM",
+ [ 7] = "IN_MOVED_TO",
+ [ 8] = "IN_CREATE",
+ [ 9] = "IN_DELETE",
+ [10] = "IN_DELETE_SELF",
+ [11] = "IN_MOVE_SELF",
+
+ [13] = "IN_UNMOUNT",
+ [14] = "IN_Q_OVERFLOW",
+ [15] = "IN_IGNORED",
+
+ [24] = "IN_ONLYDIR",
+ [25] = "IN_DONT_FOLLOW",
+ [26] = "IN_EXCL_UNLINK",
+
+ [29] = "IN_MASK_ADD",
+ [30] = "IN_ISDIR",
+ [31] = "IN_ONESHOT",
+ };
+
+ size_t i, j;
+
+ memset(buf, 0, size);
+ for (i = 0, j = 0; i < 32 && j < size; i++) {
+ if (!(mask & (1u << i)))
+ continue;
+ if (j)
+ j += snprintf(&buf[j], size - j, " | %s", names[i]);
+ else
+ j += snprintf(&buf[j], size - j, "%s", names[i]);
+ }
+}
+
+static int inotify_read_events(char *prefix, int inotify_fd, unsigned int *expected)
+{
+ struct inotify_event *event;
+ char buf[BUFF_SIZE * 8];
+ int ret, off, n = 0;
+
+ while (1) {
+ ret = read(inotify_fd, buf, sizeof(buf));
+ if (ret < 0) {
+ if (errno != EAGAIN) {
+ pr_perror("Can't read inotify queue");
+ return -1;
+ } else {
+ ret = 0;
+ goto out;
+ }
+ } else if (ret == 0)
+ break;
+
+ for (off = 0; off < ret; n++, off += sizeof(*event) + event->len) {
+ char emask[128];
+
+ event = (void *)(buf + off);
+ decode_event_mask(emask, sizeof(emask), event->mask);
+ test_msg("\t%-16s: event %#10x -> %s\n",
+ prefix, event->mask, emask);
+ if (expected)
+ *expected &= ~event->mask;
+ }
+ }
+
+out:
+ test_msg("\t%-16s: read %2d events\n", prefix, n);
+ return ret;
+}
+
+int main (int argc, char *argv[])
+{
+ unsigned int mask = IN_DELETE | IN_CLOSE_WRITE | IN_DELETE_SELF | IN_CREATE;
+ char test_file_path[PATH_MAX];
+ int fd, real_fd;
+ unsigned int emask;
+
+ test_init(argc, argv);
+
+ if (mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
+ pr_perror("Can't create directory %s", dirname);
+ exit(1);
+ }
+
+#ifdef INOTIFY01
+{
+ pid_t pid;
+ task_waiter_t t;
+ task_waiter_init(&t);
+ static char buf[PATH_MAX];
+
+ if (mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL)) {
+ pr_perror("Unable to remount /");
+ return 1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("Can't fork a test process");
+ exit(1);
+ }
+ if (pid == 0) {
+ int fd;
+
+ prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+ if (unshare(CLONE_NEWNS)) {
+ pr_perror("Unable to unshare mount namespace");
+ exit(1);
+ }
+
+ if (mount("zdtm", dirname, "tmpfs", 0, NULL)) {
+ pr_perror("Unable to mount tmpfs");
+ exit(1);
+ }
+ fd = open(dirname, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("Unable to open %s", dirname);
+ exit(1);
+ }
+ dup2(fd, 100);
+ task_waiter_complete_current(&t);
+ while (1)
+ sleep(1000);
+ exit(1);
+ }
+ task_waiter_wait4(&t, pid);
+ snprintf(buf, sizeof(buf), "/proc/%d/fd/100", pid);
+ dirname = buf;
+}
+#endif
+
+ fd = inotify_init1(IN_NONBLOCK);
+ if (fd < 0) {
+ pr_perror("inotify_init failed");
+ exit(1);
+ }
+
+ snprintf(test_file_path, sizeof(test_file_path), "%s/%s", dirname, TEST_FILE);
+
+ real_fd = open(test_file_path, O_CREAT | O_TRUNC | O_RDWR, 0644);
+ if (real_fd < 0) {
+ pr_perror("Can't create %s", test_file_path);
+ exit(1);
+ }
+
+ if (inotify_add_watch(fd, dirname, mask) < 0) {
+ pr_perror("inotify_add_watch failed");
+ exit(1);
+ }
+
+ if (inotify_add_watch(fd, test_file_path, mask) < 0) {
+ pr_perror("inotify_add_watch failed");
+ exit(1);
+ }
+
+ /*
+ * At this moment we have a file inside testing
+ * directory and a hardlink to it. The file and
+ * hardlink are opened.
+ */
+
+#ifndef INOTIFY01
+ if (unlink(test_file_path)) {
+ pr_perror("can't unlink %s", test_file_path);
+ exit(1);
+ }
+
+ emask = IN_DELETE;
+ inotify_read_events("unlink 02", fd, &emask);
+ if (emask) {
+ char emask_bits[128];
+ decode_event_mask(emask_bits, sizeof(emask_bits), emask);
+ pr_perror("Unhandled events in emask %#x -> %s",
+ emask, emask_bits);
+ exit(1);
+ }
+#endif
+
+ test_daemon();
+ test_waitsig();
+
+ close(real_fd);
+
+ emask = IN_CLOSE_WRITE;
+ inotify_read_events("after", fd, &emask);
+ if (emask) {
+ char emask_bits[128];
+ decode_event_mask(emask_bits, sizeof(emask_bits), emask);
+ fail("Unhandled events in emask %#x -> %s",
+ emask, emask_bits);
+ return 1;
+ }
+
+#ifndef INOTIFY01
+ real_fd = open(test_file_path, O_CREAT | O_TRUNC | O_RDWR, 0644);
+ if (real_fd < 0) {
+ pr_perror("Can't create %s", test_file_path);
+ exit(1);
+ }
+ close(real_fd);
+
+ emask = IN_CREATE | IN_CLOSE_WRITE;
+ inotify_read_events("after2", fd, &emask);
+ if (emask) {
+ char emask_bits[128];
+ decode_event_mask(emask_bits, sizeof(emask_bits), emask);
+ fail("Unhandled events in emask %#x -> %s",
+ emask, emask_bits);
+ return 1;
+ }
+#endif
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/inotify00.desc b/test/zdtm/static/inotify00.desc
new file mode 100644
index 000000000..083b58305
--- /dev/null
+++ b/test/zdtm/static/inotify00.desc
@@ -0,0 +1 @@
+{'opts': '--link-remap', 'flags': 'nouser'}
diff --git a/test/zdtm/static/inotify00.opts b/test/zdtm/static/inotify00.opts
new file mode 100644
index 000000000..472294671
--- /dev/null
+++ b/test/zdtm/static/inotify00.opts
@@ -0,0 +1 @@
+--link-remap
diff --git a/test/zdtm/static/inotify01.c b/test/zdtm/static/inotify01.c
new file mode 120000
index 000000000..a7937cf57
--- /dev/null
+++ b/test/zdtm/static/inotify01.c
@@ -0,0 +1 @@
+inotify00.c \ No newline at end of file
diff --git a/test/zdtm/static/inotify01.desc b/test/zdtm/static/inotify01.desc
new file mode 100644
index 000000000..a8849e097
--- /dev/null
+++ b/test/zdtm/static/inotify01.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid', 'feature': 'mnt_id'}
diff --git a/test/zdtm/static/inotify02.c b/test/zdtm/static/inotify02.c
new file mode 100644
index 000000000..eca36dde8
--- /dev/null
+++ b/test/zdtm/static/inotify02.c
@@ -0,0 +1,101 @@
+#define _GNU_SOURCE
+
+#include <unistd.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <signal.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check for inotify file-handles storm";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+static int num_of_handles(int fd)
+{
+ char path[64];
+ char buf[512];
+ int ret = 0;
+ FILE *f;
+
+ snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
+ f = fopen(path, "r");
+ if (!f) {
+ pr_err("Can't open %s", path);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ if (memcmp(buf, "inotify ", 8))
+ continue;
+ ret++;
+ }
+
+ fclose(f);
+ return ret;
+}
+
+int main (int argc, char *argv[])
+{
+ const unsigned int mask = IN_DELETE | IN_CLOSE_WRITE | IN_DELETE_SELF | IN_CREATE;
+ const int nr_dirs = 64;
+ char temp[nr_dirs][16];
+ char path[PATH_MAX];
+ int fd, i;
+
+ test_init(argc, argv);
+
+ if (mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
+ pr_err("Can't create directory %s", dirname);
+ exit(1);
+ }
+
+ fd = inotify_init1(IN_NONBLOCK);
+ if (fd < 0) {
+ pr_err("inotify_init failed");
+ exit(1);
+ }
+
+ for (i = 0; i < nr_dirs; i++) {
+ snprintf(temp[i], sizeof(temp[0]), "d.%03d", i);
+ snprintf(path, sizeof(path), "%s/%s", dirname, temp[i]);
+ if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
+ pr_err("Can't create %s", path);
+ exit(1);
+ }
+
+ if (inotify_add_watch(fd, path, mask) < 0) {
+ pr_err("inotify_add_watch failed on %s", path);
+ exit(1);
+ }
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ i = num_of_handles(fd);
+ close(fd);
+
+ if (i < nr_dirs)
+ fail("Expected %d handles but got %d", nr_dirs, i);
+ else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/inotify02.desc b/test/zdtm/static/inotify02.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/inotify02.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/inotify_irmap.c b/test/zdtm/static/inotify_irmap.c
new file mode 100644
index 000000000..e16a4181b
--- /dev/null
+++ b/test/zdtm/static/inotify_irmap.c
@@ -0,0 +1,69 @@
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include <unistd.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check for irmap";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+#define TDIR "/etc"
+#define TFIL TDIR"/zdtm-test"
+
+#define BUFF_SIZE ((sizeof(struct inotify_event) + PATH_MAX))
+
+int main (int argc, char *argv[])
+{
+ char buf[BUFF_SIZE];
+ int fd, wd;
+
+ test_init(argc, argv);
+
+ unlink(TFIL);
+ if (creat(TFIL, 0600) < 0) {
+ pr_perror("Can't make test file");
+ exit(1);
+ }
+
+ fd = inotify_init1(IN_NONBLOCK);
+ if (fd < 0) {
+ fail("inotify_init failed");
+ unlink(TFIL);
+ exit(1);
+ }
+
+ wd = inotify_add_watch(fd, TFIL, IN_OPEN);
+ if (wd < 0) {
+ fail("inotify_add_watch failed");
+ unlink(TFIL);
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ memset(buf, 0, sizeof(buf));
+ wd = open(TFIL, O_RDONLY);
+ if (read(fd, buf, sizeof(buf)) <= 0) {
+ unlink(TFIL);
+ fail("No events in queue");
+ exit(1);
+ }
+
+ close(wd);
+ close(fd);
+ unlink(TFIL);
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/inotify_irmap.desc b/test/zdtm/static/inotify_irmap.desc
new file mode 100644
index 000000000..9c5994e92
--- /dev/null
+++ b/test/zdtm/static/inotify_irmap.desc
@@ -0,0 +1 @@
+{'flags': 'suid', 'opts' : '--force-irmap'}
diff --git a/test/zdtm/static/inotify_system.c b/test/zdtm/static/inotify_system.c
new file mode 100644
index 000000000..6a95f5b2c
--- /dev/null
+++ b/test/zdtm/static/inotify_system.c
@@ -0,0 +1,391 @@
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <signal.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Inotify on symlink should be checked";
+#ifndef NODEL
+char filename[] = "file";
+char linkname[] = "file.lnk";
+const char *inot_dir = "./inotify";
+#else
+char filename[] = "file.nodel";
+char linkname[] = "file.nodel.lnk";
+const char *inot_dir = "./inotify.nodel";
+#endif
+
+#ifdef __NR_inotify_init
+#include <sys/inotify.h>
+
+#ifndef IN_DONT_FOLLOW
+/* Missed in SLES 10 header */
+#define IN_DONT_FOLLOW 0x02000000
+#endif
+
+#define EVENT_MAX 1024
+/* size of the event structure, not counting name */
+#define EVENT_SIZE (sizeof (struct inotify_event))
+/* reasonable guess as to size of 1024 events */
+#define EVENT_BUF_LEN (EVENT_MAX * (EVENT_SIZE + 16))
+#define BUF_SIZE 256
+
+#define min_value(a,b) (a<b) ? a : b
+#define handle_event(MASK) (MASK == IN_ACCESS) ? "IN_ACCESS" : \
+(MASK == IN_MODIFY) ? "IN_MODIFY" : \
+(MASK == IN_ATTRIB) ? "IN_ATTRIB" : \
+(MASK == IN_CLOSE) ? "IN_CLOSE" : \
+(MASK == IN_CLOSE_WRITE) ? "IN_CLOSE_WRITE" : \
+(MASK == IN_CLOSE_NOWRITE) ? "IN_CLOSE_NOWRITE" : \
+(MASK == IN_OPEN) ? "IN_OPEN" : \
+(MASK == IN_MOVED_FROM) ? "IN_MOVED_FROM" : \
+(MASK == IN_MOVED_TO) ? "IN_MOVED_TO" : \
+(MASK == IN_DELETE) ? "IN_DELETE" : \
+(MASK == IN_CREATE) ? "IN_CREATE" : \
+(MASK == IN_DELETE_SELF) ? "IN_DELETE_SELF" : \
+(MASK == IN_MOVE_SELF) ? "IN_MOVE_SELF" : \
+(MASK == IN_UNMOUNT) ? "IN_UNMOUNT" : \
+(MASK == IN_Q_OVERFLOW) ? "IN_Q_OVERFLOW" : \
+(MASK == IN_IGNORED) ? "IN_IGNORED" : \
+"UNKNOWN"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+typedef struct {
+ int inot;
+ uint32_t file;
+ uint32_t link;
+ uint32_t dir;
+} desc;
+
+void do_wait() {
+ test_daemon();
+ test_waitsig();
+}
+
+int createFiles(char *path, char *target, char *link) {
+ int fd;
+ fd = open(path,O_CREAT, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", path);
+ return -1;
+ }
+ close(fd);
+ if (symlink(target, link) < 0) {
+ pr_perror("can't symlink %s to %s", path, link);
+ return -1;
+ }
+ return 0;
+}
+
+int addWatcher(int fd, const char *path) {
+ int wd;
+ wd = inotify_add_watch(fd, path, IN_ALL_EVENTS | IN_DONT_FOLLOW);
+ if (wd < 0) {
+ pr_perror("inotify_add_watch(%d, %s, IN_ALL_EVENTS) Failed, %s",
+ fd, path, strerror(errno));
+ return -1;
+ }
+ return wd;
+}
+
+int fChmod(char *path) {
+ if (chmod(path, 0755) < 0) {
+ pr_perror("chmod(%s, 0755) Failed, %s",
+ path, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int fWriteClose(char *path) {
+ int fd = open(path, O_RDWR | O_CREAT, 0700);
+ if (fd == -1) {
+ pr_perror("open(%s, O_RDWR|O_CREAT,0700) Failed, %s",
+ path, strerror(errno));
+ return -1;
+ }
+ if (write(fd, "string", 7) == -1) {
+ pr_perror("write(%d, %s, 1) Failed, %s", fd, path, strerror(errno));
+ return -1;
+ }
+ if (close(fd) == -1) {
+ pr_perror("close(%s) Failed, %s", path, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int fNoWriteClose(char *path) {
+ char buf[BUF_SIZE];
+ int fd = open(path, O_RDONLY);
+ if ( fd < 0 ) {
+ pr_perror("open(%s, O_RDONLY) Failed, %s",
+ path, strerror(errno));
+ return -1;
+ }
+ if (read(fd, buf, BUF_SIZE) == -1) {
+ pr_perror("read error: %s", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ if (close(fd) == -1) {
+ pr_perror("close(%s) Failed, %s", path, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int fMove(char *from, char *to) {
+ if (rename(from, to) == -1) {
+ pr_perror("rename error (from: %s to: %s) : %s",
+ from, to, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+desc init_env(const char *dir, char *file_path, char *link_path) {
+ desc in_desc = {-1, -1, -1, -1};
+ if (mkdir(dir, 0777) < 0) {
+ pr_perror("error in creating directory: %s, %s",
+ dir, strerror(errno));
+ return in_desc;
+ }
+ in_desc.inot = inotify_init();
+ if (in_desc.inot < 0) {
+ pr_perror("inotify_init () Failed, %s", strerror(errno));
+ rmdir(dir);
+ return in_desc;
+ }
+
+ if (snprintf(file_path, BUF_SIZE, "%s/%s", dir, filename) >= BUF_SIZE) {
+ pr_perror("filename %s is too long", filename);
+ rmdir(dir);
+ return in_desc;
+ }
+
+ if (snprintf(link_path, BUF_SIZE, "%s/%s", dir, linkname) >= BUF_SIZE) {
+ pr_perror("filename %s is too long", linkname);
+ rmdir(dir);
+ return in_desc;
+ }
+
+ in_desc.dir = addWatcher(in_desc.inot, dir);
+ if (createFiles(file_path, filename, link_path)) {
+ return in_desc;
+ }
+ in_desc.link = addWatcher(in_desc.inot, link_path);
+ in_desc.file = addWatcher(in_desc.inot, file_path);
+
+ return in_desc;
+}
+
+int fDelete(char *path) {
+ if (unlink(path) != 0) {
+ pr_perror("unlink: (%s)", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int fRemDir(const char *target) {
+ if(rmdir(target)) {
+ pr_perror("rmdir: (%s)", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int test_actions(const char *dir, char *file_path, char *link_path) {
+
+ if (
+ fChmod(link_path) == 0 &&
+ fWriteClose(link_path) == 0 &&
+ fNoWriteClose(link_path) == 0 &&
+ fMove(file_path, filename) == 0 &&
+ fMove(filename, file_path) == 0
+#ifndef NODEL
+ && fDelete(file_path) == 0 &&
+ fDelete(link_path) == 0 &&
+ fRemDir(dir) == 0
+#endif
+ )
+ {
+ return 0;
+ }
+ return -1;
+}
+
+void dump_events(char *buf, int len) {
+ int marker = 0;
+ struct inotify_event *event;
+ while (marker < len) {
+ event = (struct inotify_event *) &buf[marker];
+ test_msg("\t%s (%x mask, %d len", handle_event(event->mask), event->mask, event->len);
+ if (event->len)
+ test_msg(", '%s' name", event->name);
+ test_msg(")\n");
+ marker += EVENT_SIZE + event->len;
+ }
+}
+
+int harmless(int mask)
+{
+ switch (mask) {
+ case IN_CLOSE_NOWRITE:
+ case IN_ATTRIB:
+ return 1;
+ }
+ return 0;
+}
+
+int errors(int exp_len, int len, char *etalon_buf, char *buf) {
+ int marker=0;
+ int error=0;
+ while (marker < len){
+ struct inotify_event *event;
+ struct inotify_event *exp_event;
+ event = (struct inotify_event *) &buf[marker];
+ /* It's OK if some additional events are recevived */
+ if (marker < exp_len)
+ exp_event = (struct inotify_event *) &etalon_buf[marker];
+ else {
+ if (!harmless(event->mask)) {
+ fail("got unexpected event %s (%x mask)\n",
+ handle_event(event->mask), event->mask);
+ error++;
+ }
+ goto next_event;
+ }
+
+ if (event->mask != exp_event->mask) {
+ fail("Handled %s (%x mask), expected %s (%x mask)",
+ handle_event(event->mask), event->mask,
+ handle_event(exp_event->mask),
+ exp_event->mask);
+ error++;
+ }
+ if (event->len != exp_event->len) {
+ fail("Incorrect length of field name.");
+ error++;
+ break;
+ }
+ else if (event->len && strncmp(event->name, exp_event->name, event->len)) {
+ fail("Handled file name %s, expected %s",
+ event->name,
+ exp_event->name);
+ error++;
+ }
+next_event:
+ marker += EVENT_SIZE + event->len;
+ }
+ return error;
+}
+
+int read_set(int inot_fd, char *event_set) {
+ int len;
+ if ((len = read(inot_fd, event_set, EVENT_BUF_LEN)) < 0) {
+ pr_perror("read(%d, buf, %d) Failed, errno=%d : %s",
+ inot_fd, EVENT_BUF_LEN, errno, strerror(errno));
+ return -1;
+ }
+ return len;
+}
+
+void common_close(desc *descr) {
+ if (descr->inot > 0) {
+ close(descr->inot);
+ descr->inot=-1;
+ descr->file=-1;
+ descr->dir=-1;
+ descr->link=-1;
+ }
+}
+
+int get_event_set(char *event_set, int wait) {
+ int len;
+ char link_path[BUF_SIZE];
+ char file_path[BUF_SIZE];
+ desc common_desc;
+
+ common_desc = init_env(inot_dir, file_path, link_path);
+ if ((common_desc.inot < 0) || (common_desc.file < 0) || \
+ (common_desc.dir < 0) || (common_desc.link < 0)) {
+ common_close(&common_desc);
+ return -1;
+ }
+ if(test_actions(inot_dir, file_path, link_path) < 0) {
+ common_close(&common_desc);
+ return -1;
+ }
+ if (wait) {
+ do_wait();
+ }
+ len = read_set(common_desc.inot, event_set);
+ common_close(&common_desc);
+#ifdef NODEL
+ if (! (fDelete(file_path) == 0 &&
+ fDelete(link_path) == 0 &&
+ fRemDir(inot_dir) == 0))
+ return -1;
+#endif
+ return len;
+}
+
+int check(int len, char *event_set, int exp_len, char *etalon_event_set) {
+
+ if ((exp_len < 0) || (len < 0)){
+ fail("Error in preparing event sets.");
+ return -1;
+ }
+ if (len < exp_len) {
+ fail("Events are lost. Read: %d, Expected: %d", len, exp_len);
+ test_msg("expected events\n");
+ dump_events(etalon_event_set, exp_len);
+ test_msg("real events\n");
+ dump_events(event_set, len);
+ return -1;
+ }
+ if (errors(exp_len, len, etalon_event_set, event_set) == 0) {
+ pass();
+ return 0;
+ }
+ return -1;
+}
+
+int main(int argc, char ** argv)
+{
+ int exp_len=-1, len=-1;
+ char etalon_event_set[EVENT_BUF_LEN];
+ char event_set[EVENT_BUF_LEN];
+
+ test_init(argc, argv);
+
+ exp_len = get_event_set(etalon_event_set, 0);
+ len = get_event_set(event_set, 1);
+
+ if (check(len, event_set, exp_len, etalon_event_set)) {
+ return 1;
+ }
+ return 0;
+}
+#else
+
+int main(int argc, char ** argv)
+{
+ test_init(argc, argv);
+ skip("Inotify not supported.");
+ return 0;
+}
+#endif //__NR_inotify_init
diff --git a/test/zdtm/static/inotify_system.desc b/test/zdtm/static/inotify_system.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/inotify_system.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/inotify_system_nodel.c b/test/zdtm/static/inotify_system_nodel.c
new file mode 100644
index 000000000..6a95f5b2c
--- /dev/null
+++ b/test/zdtm/static/inotify_system_nodel.c
@@ -0,0 +1,391 @@
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <signal.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Inotify on symlink should be checked";
+#ifndef NODEL
+char filename[] = "file";
+char linkname[] = "file.lnk";
+const char *inot_dir = "./inotify";
+#else
+char filename[] = "file.nodel";
+char linkname[] = "file.nodel.lnk";
+const char *inot_dir = "./inotify.nodel";
+#endif
+
+#ifdef __NR_inotify_init
+#include <sys/inotify.h>
+
+#ifndef IN_DONT_FOLLOW
+/* Missed in SLES 10 header */
+#define IN_DONT_FOLLOW 0x02000000
+#endif
+
+#define EVENT_MAX 1024
+/* size of the event structure, not counting name */
+#define EVENT_SIZE (sizeof (struct inotify_event))
+/* reasonable guess as to size of 1024 events */
+#define EVENT_BUF_LEN (EVENT_MAX * (EVENT_SIZE + 16))
+#define BUF_SIZE 256
+
+#define min_value(a,b) (a<b) ? a : b
+#define handle_event(MASK) (MASK == IN_ACCESS) ? "IN_ACCESS" : \
+(MASK == IN_MODIFY) ? "IN_MODIFY" : \
+(MASK == IN_ATTRIB) ? "IN_ATTRIB" : \
+(MASK == IN_CLOSE) ? "IN_CLOSE" : \
+(MASK == IN_CLOSE_WRITE) ? "IN_CLOSE_WRITE" : \
+(MASK == IN_CLOSE_NOWRITE) ? "IN_CLOSE_NOWRITE" : \
+(MASK == IN_OPEN) ? "IN_OPEN" : \
+(MASK == IN_MOVED_FROM) ? "IN_MOVED_FROM" : \
+(MASK == IN_MOVED_TO) ? "IN_MOVED_TO" : \
+(MASK == IN_DELETE) ? "IN_DELETE" : \
+(MASK == IN_CREATE) ? "IN_CREATE" : \
+(MASK == IN_DELETE_SELF) ? "IN_DELETE_SELF" : \
+(MASK == IN_MOVE_SELF) ? "IN_MOVE_SELF" : \
+(MASK == IN_UNMOUNT) ? "IN_UNMOUNT" : \
+(MASK == IN_Q_OVERFLOW) ? "IN_Q_OVERFLOW" : \
+(MASK == IN_IGNORED) ? "IN_IGNORED" : \
+"UNKNOWN"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+typedef struct {
+ int inot;
+ uint32_t file;
+ uint32_t link;
+ uint32_t dir;
+} desc;
+
+void do_wait() {
+ test_daemon();
+ test_waitsig();
+}
+
+int createFiles(char *path, char *target, char *link) {
+ int fd;
+ fd = open(path,O_CREAT, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", path);
+ return -1;
+ }
+ close(fd);
+ if (symlink(target, link) < 0) {
+ pr_perror("can't symlink %s to %s", path, link);
+ return -1;
+ }
+ return 0;
+}
+
+int addWatcher(int fd, const char *path) {
+ int wd;
+ wd = inotify_add_watch(fd, path, IN_ALL_EVENTS | IN_DONT_FOLLOW);
+ if (wd < 0) {
+ pr_perror("inotify_add_watch(%d, %s, IN_ALL_EVENTS) Failed, %s",
+ fd, path, strerror(errno));
+ return -1;
+ }
+ return wd;
+}
+
+int fChmod(char *path) {
+ if (chmod(path, 0755) < 0) {
+ pr_perror("chmod(%s, 0755) Failed, %s",
+ path, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int fWriteClose(char *path) {
+ int fd = open(path, O_RDWR | O_CREAT, 0700);
+ if (fd == -1) {
+ pr_perror("open(%s, O_RDWR|O_CREAT,0700) Failed, %s",
+ path, strerror(errno));
+ return -1;
+ }
+ if (write(fd, "string", 7) == -1) {
+ pr_perror("write(%d, %s, 1) Failed, %s", fd, path, strerror(errno));
+ return -1;
+ }
+ if (close(fd) == -1) {
+ pr_perror("close(%s) Failed, %s", path, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int fNoWriteClose(char *path) {
+ char buf[BUF_SIZE];
+ int fd = open(path, O_RDONLY);
+ if ( fd < 0 ) {
+ pr_perror("open(%s, O_RDONLY) Failed, %s",
+ path, strerror(errno));
+ return -1;
+ }
+ if (read(fd, buf, BUF_SIZE) == -1) {
+ pr_perror("read error: %s", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ if (close(fd) == -1) {
+ pr_perror("close(%s) Failed, %s", path, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int fMove(char *from, char *to) {
+ if (rename(from, to) == -1) {
+ pr_perror("rename error (from: %s to: %s) : %s",
+ from, to, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+desc init_env(const char *dir, char *file_path, char *link_path) {
+ desc in_desc = {-1, -1, -1, -1};
+ if (mkdir(dir, 0777) < 0) {
+ pr_perror("error in creating directory: %s, %s",
+ dir, strerror(errno));
+ return in_desc;
+ }
+ in_desc.inot = inotify_init();
+ if (in_desc.inot < 0) {
+ pr_perror("inotify_init () Failed, %s", strerror(errno));
+ rmdir(dir);
+ return in_desc;
+ }
+
+ if (snprintf(file_path, BUF_SIZE, "%s/%s", dir, filename) >= BUF_SIZE) {
+ pr_perror("filename %s is too long", filename);
+ rmdir(dir);
+ return in_desc;
+ }
+
+ if (snprintf(link_path, BUF_SIZE, "%s/%s", dir, linkname) >= BUF_SIZE) {
+ pr_perror("filename %s is too long", linkname);
+ rmdir(dir);
+ return in_desc;
+ }
+
+ in_desc.dir = addWatcher(in_desc.inot, dir);
+ if (createFiles(file_path, filename, link_path)) {
+ return in_desc;
+ }
+ in_desc.link = addWatcher(in_desc.inot, link_path);
+ in_desc.file = addWatcher(in_desc.inot, file_path);
+
+ return in_desc;
+}
+
+int fDelete(char *path) {
+ if (unlink(path) != 0) {
+ pr_perror("unlink: (%s)", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int fRemDir(const char *target) {
+ if(rmdir(target)) {
+ pr_perror("rmdir: (%s)", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int test_actions(const char *dir, char *file_path, char *link_path) {
+
+ if (
+ fChmod(link_path) == 0 &&
+ fWriteClose(link_path) == 0 &&
+ fNoWriteClose(link_path) == 0 &&
+ fMove(file_path, filename) == 0 &&
+ fMove(filename, file_path) == 0
+#ifndef NODEL
+ && fDelete(file_path) == 0 &&
+ fDelete(link_path) == 0 &&
+ fRemDir(dir) == 0
+#endif
+ )
+ {
+ return 0;
+ }
+ return -1;
+}
+
+void dump_events(char *buf, int len) {
+ int marker = 0;
+ struct inotify_event *event;
+ while (marker < len) {
+ event = (struct inotify_event *) &buf[marker];
+ test_msg("\t%s (%x mask, %d len", handle_event(event->mask), event->mask, event->len);
+ if (event->len)
+ test_msg(", '%s' name", event->name);
+ test_msg(")\n");
+ marker += EVENT_SIZE + event->len;
+ }
+}
+
+int harmless(int mask)
+{
+ switch (mask) {
+ case IN_CLOSE_NOWRITE:
+ case IN_ATTRIB:
+ return 1;
+ }
+ return 0;
+}
+
+int errors(int exp_len, int len, char *etalon_buf, char *buf) {
+ int marker=0;
+ int error=0;
+ while (marker < len){
+ struct inotify_event *event;
+ struct inotify_event *exp_event;
+ event = (struct inotify_event *) &buf[marker];
+ /* It's OK if some additional events are recevived */
+ if (marker < exp_len)
+ exp_event = (struct inotify_event *) &etalon_buf[marker];
+ else {
+ if (!harmless(event->mask)) {
+ fail("got unexpected event %s (%x mask)\n",
+ handle_event(event->mask), event->mask);
+ error++;
+ }
+ goto next_event;
+ }
+
+ if (event->mask != exp_event->mask) {
+ fail("Handled %s (%x mask), expected %s (%x mask)",
+ handle_event(event->mask), event->mask,
+ handle_event(exp_event->mask),
+ exp_event->mask);
+ error++;
+ }
+ if (event->len != exp_event->len) {
+ fail("Incorrect length of field name.");
+ error++;
+ break;
+ }
+ else if (event->len && strncmp(event->name, exp_event->name, event->len)) {
+ fail("Handled file name %s, expected %s",
+ event->name,
+ exp_event->name);
+ error++;
+ }
+next_event:
+ marker += EVENT_SIZE + event->len;
+ }
+ return error;
+}
+
+int read_set(int inot_fd, char *event_set) {
+ int len;
+ if ((len = read(inot_fd, event_set, EVENT_BUF_LEN)) < 0) {
+ pr_perror("read(%d, buf, %d) Failed, errno=%d : %s",
+ inot_fd, EVENT_BUF_LEN, errno, strerror(errno));
+ return -1;
+ }
+ return len;
+}
+
+void common_close(desc *descr) {
+ if (descr->inot > 0) {
+ close(descr->inot);
+ descr->inot=-1;
+ descr->file=-1;
+ descr->dir=-1;
+ descr->link=-1;
+ }
+}
+
+int get_event_set(char *event_set, int wait) {
+ int len;
+ char link_path[BUF_SIZE];
+ char file_path[BUF_SIZE];
+ desc common_desc;
+
+ common_desc = init_env(inot_dir, file_path, link_path);
+ if ((common_desc.inot < 0) || (common_desc.file < 0) || \
+ (common_desc.dir < 0) || (common_desc.link < 0)) {
+ common_close(&common_desc);
+ return -1;
+ }
+ if(test_actions(inot_dir, file_path, link_path) < 0) {
+ common_close(&common_desc);
+ return -1;
+ }
+ if (wait) {
+ do_wait();
+ }
+ len = read_set(common_desc.inot, event_set);
+ common_close(&common_desc);
+#ifdef NODEL
+ if (! (fDelete(file_path) == 0 &&
+ fDelete(link_path) == 0 &&
+ fRemDir(inot_dir) == 0))
+ return -1;
+#endif
+ return len;
+}
+
+int check(int len, char *event_set, int exp_len, char *etalon_event_set) {
+
+ if ((exp_len < 0) || (len < 0)){
+ fail("Error in preparing event sets.");
+ return -1;
+ }
+ if (len < exp_len) {
+ fail("Events are lost. Read: %d, Expected: %d", len, exp_len);
+ test_msg("expected events\n");
+ dump_events(etalon_event_set, exp_len);
+ test_msg("real events\n");
+ dump_events(event_set, len);
+ return -1;
+ }
+ if (errors(exp_len, len, etalon_event_set, event_set) == 0) {
+ pass();
+ return 0;
+ }
+ return -1;
+}
+
+int main(int argc, char ** argv)
+{
+ int exp_len=-1, len=-1;
+ char etalon_event_set[EVENT_BUF_LEN];
+ char event_set[EVENT_BUF_LEN];
+
+ test_init(argc, argv);
+
+ exp_len = get_event_set(etalon_event_set, 0);
+ len = get_event_set(event_set, 1);
+
+ if (check(len, event_set, exp_len, etalon_event_set)) {
+ return 1;
+ }
+ return 0;
+}
+#else
+
+int main(int argc, char ** argv)
+{
+ test_init(argc, argv);
+ skip("Inotify not supported.");
+ return 0;
+}
+#endif //__NR_inotify_init
diff --git a/test/zdtm/static/inotify_system_nodel.desc b/test/zdtm/static/inotify_system_nodel.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/inotify_system_nodel.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/ipc_namespace.c b/test/zdtm/static/ipc_namespace.c
new file mode 100644
index 000000000..99c25a706
--- /dev/null
+++ b/test/zdtm/static/ipc_namespace.c
@@ -0,0 +1,365 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <linux/msg.h>
+#include <linux/sem.h>
+#include <linux/shm.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+#define CLONE_NEWIPC 0x08000000
+
+extern int msgctl (int __msqid, int __cmd, struct msqid_ds *__buf) __THROW;
+extern int semctl (int __semid, int __semnum, int __cmd, ...) __THROW;
+extern int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf) __THROW;
+
+struct ipc_ids {
+ int in_use; /* TODO: Check for 0 */
+// unsigned short seq;
+// unsigned short seq_max;
+// struct rw_semaphore rw_mutex;
+// struct idr ipcs_idr; /* TODO */
+};
+
+struct ipc_ns {
+ struct ipc_ids ids[3];
+
+ int sem_ctls[4]; // +
+ int used_sems; // +
+
+ int msg_ctlmax; // +
+ int msg_ctlmnb; // +
+ int msg_ctlmni; // +
+ int msg_bytes; // +
+ int msg_hdrs; // +
+ int auto_msgmni; // +
+
+ size_t shm_ctlmax;
+ size_t shm_ctlall;
+ int shm_ctlmni;
+ int shm_tot;
+ int shm_rmid_forced;
+
+// struct vfsmount *mq_mnt;
+
+// unsigned int mq_queues_count;
+
+ unsigned int mq_queues_max; /* initialized to DFLT_QUEUESMAX */
+ unsigned int mq_msg_max; /* initialized to DFLT_MSGMAX */
+ unsigned int mq_msgsize_max; /* initialized to DFLT_MSGSIZEMAX */
+
+ struct user_ns *user_ns;
+};
+
+#define IPC_SEM_IDS 0
+#define IPC_MSG_IDS 1
+#define IPC_SHM_IDS 2
+
+const char *test_doc = "Check that ipc ns context migrated successfully";
+const char *test_author = "Stanislav Kinsbursky <skinsbursky@parallels.com>";
+
+struct ipc_ns ipc_before, ipc_after;
+
+static int read_ipc_sysctl(char *name, int *data, size_t size)
+{
+ int fd;
+ int ret;
+ char buf[32];
+
+ fd = open(name, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("Can't open %d", name);
+ return fd;
+ }
+ ret = read(fd, buf, 32);
+ if (ret < 0) {
+ pr_perror("Can't read %s", name);
+ ret = -errno;
+ goto err;
+ }
+ *data = (int)strtoul(buf, NULL, 10);
+ ret = 0;
+err:
+ close(fd);
+ return ret;
+}
+
+static int get_messages_info(struct ipc_ns *ipc)
+{
+ struct msginfo info;
+ int ret;
+
+ ret = msgctl(0, MSG_INFO, (struct msqid_ds *)&info);
+ if (ret < 0) {
+ pr_perror("msgctl failed with %d", errno);
+ return ret;
+ }
+
+ ipc->msg_ctlmax = info.msgmax;
+ ipc->msg_ctlmnb = info.msgmnb;
+ ipc->msg_ctlmni = info.msgmni;
+ ipc->msg_bytes = info.msgtql;
+ ipc->msg_hdrs = info.msgmap;
+ ipc->ids[IPC_MSG_IDS].in_use = info.msgpool;
+
+ if (read_ipc_sysctl("/proc/sys/kernel/auto_msgmni",
+ &ipc->auto_msgmni, sizeof(ipc->auto_msgmni)))
+ return -1;
+ return 0;
+}
+
+static int get_semaphores_info(struct ipc_ns *ipc)
+{
+ int err;
+ struct seminfo info;
+
+ err = semctl(0, 0, SEM_INFO, &info);
+ if (err < 0)
+ pr_perror("semctl failed with %d", errno);
+
+ ipc->sem_ctls[0] = info.semmsl;
+ ipc->sem_ctls[1] = info.semmns;
+ ipc->sem_ctls[2] = info.semopm;
+ ipc->sem_ctls[3] = info.semmni;
+ ipc->used_sems = info.semaem;
+ ipc->ids[IPC_SEM_IDS].in_use = info.semusz;
+
+ return 0;
+}
+
+static int get_shared_memory_info(struct ipc_ns *ipc)
+{
+ int ret;
+ union {
+ struct shminfo64 shminfo64;
+ struct shm_info shminfo;
+ struct shmid_ds shmid;
+ } u;
+
+ ret = shmctl(0, IPC_INFO, &u.shmid);
+ if (ret < 0)
+ pr_perror("semctl failed with %d", errno);
+
+ ipc->shm_ctlmax = u.shminfo64.shmmax;
+ ipc->shm_ctlall = u.shminfo64.shmall;
+ ipc->shm_ctlmni = u.shminfo64.shmmni;
+
+ ret = shmctl(0, SHM_INFO, &u.shmid);
+ if (ret < 0)
+ pr_perror("semctl failed with %d", errno);
+
+ ipc->shm_tot = u.shminfo.shm_tot;
+ ipc->ids[IPC_SHM_IDS].in_use = u.shminfo.used_ids;
+
+ if (read_ipc_sysctl("/proc/sys/kernel/shm_rmid_forced",
+ &ipc->shm_rmid_forced, sizeof(ipc->shm_rmid_forced)))
+ return -1;
+ if (read_ipc_sysctl("/proc/sys/fs/mqueue/queues_max",
+ (int *)&ipc->mq_queues_max, sizeof(ipc->mq_queues_max)))
+ return -1;
+ if (read_ipc_sysctl("/proc/sys/fs/mqueue/msg_max",
+ (int *)&ipc->mq_msg_max, sizeof(ipc->mq_msg_max)))
+ return -1;
+ if (read_ipc_sysctl("/proc/sys/fs/mqueue/msgsize_max",
+ (int *)&ipc->mq_msgsize_max, sizeof(ipc->mq_msgsize_max)))
+ return -1;
+
+ return 0;
+}
+
+
+int fill_ipc_ns(struct ipc_ns *ipc)
+{
+ int ret;
+
+ ret = get_messages_info(ipc);
+ if (ret < 0) {
+ pr_perror("Failed to collect messages");
+ return ret;
+ }
+
+ ret = get_semaphores_info(ipc);
+ if (ret < 0) {
+ pr_perror("Failed to collect semaphores");
+ return ret;
+ }
+
+ ret = get_shared_memory_info(ipc);
+ if (ret < 0) {
+ pr_perror("Failed to collect shared memory");
+ return ret;
+ }
+ return 0;
+}
+
+static int rand_ipc_sysctl(char *name, unsigned int val)
+{
+ int fd;
+ int ret;
+ char buf[32];
+
+ fd = open(name, O_WRONLY);
+ if (fd < 0) {
+ pr_perror("Can't open %d", name);
+ return fd;
+ }
+ sprintf(buf, "%d\n", val);
+ ret = write(fd, buf, strlen(buf));
+ if (ret < 0) {
+ pr_perror("Can't write %u into %s", val, name);
+ return -errno;
+ }
+ close(fd);
+ return 0;
+}
+
+static int rand_ipc_sem(void)
+{
+ int fd;
+ int ret;
+ char buf[128];
+ char *name = "/proc/sys/kernel/sem";
+
+ fd = open(name, O_WRONLY);
+ if (fd < 0) {
+ pr_perror("Can't open %s", name);
+ return fd;
+ }
+ sprintf(buf, "%d %d %d %d\n", (unsigned)lrand48(), (unsigned)lrand48(),
+ (unsigned)lrand48(), (unsigned)lrand48());
+ ret = write(fd, buf, 128);
+ if (ret < 0) {
+ pr_perror("Can't write %s: %d", name, errno);
+ return -errno;
+ }
+ close(fd);
+ return 0;
+}
+
+static int rand_ipc_ns(void)
+{
+ int ret;
+
+ ret = rand_ipc_sem();
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/kernel/msgmax", (unsigned)lrand48());
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/kernel/msgmnb", (unsigned)lrand48());
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/kernel/msgmni", (unsigned)lrand48());
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/kernel/auto_msgmni", 0);
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/kernel/shmmax", (unsigned)lrand48());
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/kernel/shmall", (unsigned)lrand48());
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/kernel/shmmni", (unsigned)lrand48());
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/kernel/shm_rmid_forced", (unsigned)lrand48() & 1);
+
+
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/fs/mqueue/queues_max", (((unsigned)lrand48()) % 1023) + 1);
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/fs/mqueue/msg_max", ((unsigned)lrand48() % 65536) + 1);
+ if (!ret)
+ ret = rand_ipc_sysctl("/proc/sys/fs/mqueue/msgsize_max", ((unsigned)lrand48() & (8192 * 128 - 1)) | 128);
+
+ if (ret < 0)
+ pr_perror("Failed to randomize ipc namespace tunables");
+
+ return ret;
+}
+
+static void show_ipc_entry(struct ipc_ns *old, struct ipc_ns *new)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (old->ids[i].in_use != new->ids[i].in_use)
+ pr_perror("ids[%d].in_use differs: %d ---> %d", i,
+ old->ids[i].in_use, new->ids[i].in_use);
+
+ }
+ for (i = 0; i < 4; i++) {
+ if (old->sem_ctls[i] != new->sem_ctls[i])
+ pr_perror("sem_ctls[%d] differs: %d ---> %d", i,
+ old->sem_ctls[i], new->sem_ctls[i]);
+
+ }
+
+ if (old->msg_ctlmax != new->msg_ctlmax)
+ pr_perror("msg_ctlmax differs: %d ---> %d",
+ old->msg_ctlmax, new->msg_ctlmax);
+ if (old->msg_ctlmnb != new->msg_ctlmnb)
+ pr_perror("msg_ctlmnb differs: %d ---> %d",
+ old->msg_ctlmnb, new->msg_ctlmnb);
+ if (old->msg_ctlmni != new->msg_ctlmni)
+ pr_perror("msg_ctlmni differs: %d ---> %d",
+ old->msg_ctlmni, new->msg_ctlmni);
+ if (old->auto_msgmni != new->auto_msgmni)
+ pr_perror("auto_msgmni differs: %d ---> %d",
+ old->auto_msgmni, new->auto_msgmni);
+ if (old->shm_ctlmax != new->shm_ctlmax)
+ pr_perror("shm_ctlmax differs: %d ---> %d",
+ old->shm_ctlmax, new->shm_ctlmax);
+ if (old->shm_ctlall != new->shm_ctlall)
+ pr_perror("shm_ctlall differs: %d ---> %d",
+ old->shm_ctlall, new->shm_ctlall);
+ if (old->shm_ctlmni != new->shm_ctlmni)
+ pr_perror("shm_ctlmni differs: %d ---> %d",
+ old->shm_ctlmni, new->shm_ctlmni);
+ if (old->shm_rmid_forced != new->shm_rmid_forced)
+ pr_perror("shm_rmid_forced differs: %d ---> %d",
+ old->shm_rmid_forced, new->shm_rmid_forced);
+ if (old->mq_queues_max != new->mq_queues_max)
+ pr_perror("mq_queues_max differs: %d ---> %d",
+ old->mq_queues_max, new->mq_queues_max);
+ if (old->mq_msg_max != new->mq_msg_max)
+ pr_perror("mq_msg_max differs: %d ---> %d",
+ old->mq_msg_max, new->mq_msg_max);
+ if (old->mq_msgsize_max != new->mq_msgsize_max)
+ pr_perror("mq_msgsize_max differs: %d ---> %d",
+ old->mq_msgsize_max, new->mq_msgsize_max);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ test_init(argc, argv);
+
+ ret = rand_ipc_ns();
+ if (ret) {
+ pr_perror("Failed to randomize ipc ns before migration");
+ return -1;
+ }
+
+ ret = fill_ipc_ns(&ipc_before);
+ if (ret) {
+ pr_perror("Failed to collect ipc ns before migration");
+ return ret;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = fill_ipc_ns(&ipc_after);
+ if (ret) {
+ pr_perror("Failed to collect ipc ns after migration");
+ return ret;
+ }
+
+ if (memcmp(&ipc_before, &ipc_after, sizeof(ipc_after))) {
+ pr_perror("IPC's differ");
+ show_ipc_entry(&ipc_before, &ipc_after);
+ return -EINVAL;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/ipc_namespace.desc b/test/zdtm/static/ipc_namespace.desc
new file mode 100644
index 000000000..7b165283c
--- /dev/null
+++ b/test/zdtm/static/ipc_namespace.desc
@@ -0,0 +1 @@
+{'flavor': 'ns', 'flags' : 'suid'}
diff --git a/test/zdtm/static/jobctl00.c b/test/zdtm/static/jobctl00.c
new file mode 100644
index 000000000..397747b24
--- /dev/null
+++ b/test/zdtm/static/jobctl00.c
@@ -0,0 +1,301 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <pty.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that job control migrates correctly";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+#define JOBS_DEF 8
+#define JOBS_MAX 64
+unsigned int num_jobs = JOBS_DEF;
+TEST_OPTION(num_jobs, uint, "# \"jobs\" in a \"shell\" "
+ "(default " __stringify(JOBS_DEF)
+ ", max " __stringify(JOBS_MAX) ")", 0);
+
+#define PROCS_DEF 4
+unsigned int num_procs = PROCS_DEF;
+TEST_OPTION(num_procs, uint, "# processes in a \"job\" "
+ "(default " __stringify(PROCS_DEF) ")", 0);
+
+static const char wr_string[] = "All you need is love!\n";
+static const char rd_string[] = "We all live in a yellow submarine\n";
+static const char susp_char = '\032'; /* ^Z */
+
+static volatile sig_atomic_t signo = 0;
+
+static void record_sig(int sig)
+{
+ signo = sig;
+}
+
+static void record_and_raise_sig(int sig)
+{
+ signo = sig;
+ signal(sig, SIG_DFL);
+ raise(sig);
+}
+
+static int wait4sig(int sig)
+{
+ sigset_t mask, oldmask;
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ sigaddset(&mask, SIGCHLD); /* to see our children die */
+
+ sigprocmask(SIG_BLOCK, &mask, &oldmask);
+ while (!signo)
+ sigsuspend (&oldmask);
+ sigprocmask (SIG_UNBLOCK, &mask, NULL);
+
+ return signo != sig;
+}
+
+static int is_fg(void)
+{
+ pid_t pgid = getpgrp();
+ pid_t tcpgid = tcgetpgrp(1);
+
+ return (pgid != -1) && (pgid == tcpgid);
+}
+
+static int reader(int sig)
+{
+ char str[sizeof(rd_string) + 1];
+ return read(0, str, sizeof(str)) < 0 ||
+ strcmp(str, rd_string);
+}
+
+static int post_reader(int fd)
+{
+ if (write(fd, rd_string, sizeof(rd_string) - 1) < 0) {
+ fail("write failed: %m");
+ return -1;
+ }
+ return 0;
+}
+
+static int writer(int sig)
+{
+ return write(1, wr_string, sizeof(wr_string) - 1) < 0;
+}
+
+static int post_writer(int fd)
+{
+ char str[sizeof(wr_string) + 1];
+ if (read(0, str, sizeof(str)) < 0) {
+ fail("read failed: %m");
+ return -1;
+ }
+ /*
+ if (strcmp(str, wr_string)) {
+ fail("read string mismatch");
+ return -1;
+ }
+ */
+ return 0;
+}
+
+static struct job_type {
+ int sig;
+ int (*action)(int sig);
+ int (*post)(int fd);
+} job_types[] = {
+ { SIGTTOU, writer, post_writer },
+ { SIGTTIN, reader, post_reader },
+ { SIGCONT, wait4sig, NULL },
+};
+
+static int process(int (*action)(int), int sig)
+{
+ int ret;
+ if (is_fg()) /* we must be in background on entry */
+ return 1;
+
+ if (signal(sig, record_and_raise_sig) == SIG_ERR)
+ return 2;
+
+ kill(getppid(), SIGUSR2); /* tell the parent we're ready */
+
+ ret = action(sig); /* will be busy doing nothing for the duration of migration */
+ if (ret)
+ return 3;
+
+ if (!is_fg()) /* we must be in foreground now */
+ return 4;
+
+ ret = signo != sig; /* have we got the desired signal? */
+
+ test_waitsig();
+ return ret;
+}
+
+static int job(int (*action)(int), int sig)
+{
+ int i;
+
+ if (setpgrp() < 0)
+ return 1;
+
+ for (i = num_procs; i; i--) {
+ pid_t pid = fork();
+ if (pid < 0)
+ kill(0, SIGKILL); /* kill the whole job */
+
+ if (pid == 0)
+ /* the last is worker, others are sleepers */
+ exit(process(i == 1 ? action : wait4sig, sig));
+
+ /* wait for the child to grow up before going to next one
+ * ignore return code as the child may get stopped and SIGCHILD
+ * us */
+ wait4sig(SIGUSR2);
+ signo = 0; /* rearm sighandler */
+ }
+
+ kill(getppid(), SIGUSR2); /* tell the parent we're ready */
+
+ /* we (or our children) will get suspended somehow here, so the rest
+ * will hopefully happen after migration */
+ for (i = num_procs; i; i--) {
+ int ret;
+ wait(&ret);
+ if (!WIFEXITED(ret) || WEXITSTATUS(ret))
+ kill(0, SIGKILL);
+ }
+
+ return 0;
+}
+
+static int make_pty_pair(int *fdmaster, int *fdslave)
+{
+ struct termios tio;
+
+ if (openpty(fdmaster, fdslave, NULL, &tio, NULL) < 0)
+ return -1;
+
+ if (ioctl(*fdslave, TIOCSCTTY, NULL) < 0)
+ return -1;
+
+ tio.c_lflag |= (ICANON | ISIG | TOSTOP);
+ if (tcsetattr(*fdslave, TCSANOW, &tio) < 0)
+ return -1;
+ return 0;
+}
+
+int start_jobs(pid_t *jobs, int njobs, int fdmaster, int fdslave)
+{
+ int i;
+
+ /* the children will signal readiness via SIGUSR2 or get stopped (or
+ * exit :) and signal that via SIGCHLD */
+ if (signal(SIGUSR2, record_sig) == SIG_ERR ||
+ signal(SIGCHLD, record_sig) == SIG_ERR) {
+ pr_perror("can't install signal handler");
+ return -1;
+ }
+
+ for (i = 0; i < njobs; i++) {
+ int jtno = i % (sizeof(job_types) / sizeof(job_types[0]));
+
+ jobs[i] = fork();
+ if (jobs[i] < 0) { /* we're busted - bail out */
+ pr_perror("fork failed");
+ goto killout;
+ }
+
+ if (jobs[i] == 0) {
+ close(fdmaster);
+ dup2(fdslave, 0);
+ dup2(fdslave, 1);
+ dup2(fdslave, 2);
+ close(fdslave);
+
+ exit(job(job_types[jtno].action, job_types[jtno].sig));
+ }
+
+ /* wait for the child to grow up before proceeding */
+ wait4sig(SIGUSR2);
+ signo = 0; /* rearm sighandler */
+ }
+
+ return 0;
+killout:
+ for (; i >= 0; i--)
+ kill(-jobs[i], SIGKILL);
+ return -1;
+}
+
+int finish_jobs(pid_t *jobs, int njobs, int fdmaster, int fdslave)
+{
+ int i;
+
+ for (i = num_jobs; i--; ) {
+ int ret;
+ int jtno = i % (sizeof(job_types) / sizeof(job_types[0]));
+
+ if (tcsetpgrp(fdslave, jobs[i]) < 0) {
+ fail("can't bring a job into foreground: %m");
+ goto killout;
+ }
+
+ kill(-jobs[i], SIGCONT);
+
+ if (job_types[jtno].post && job_types[jtno].post(fdmaster))
+ goto killout;
+
+ kill(-jobs[i], SIGTERM);
+
+ waitpid(jobs[i], &ret, 0);
+ if (!WIFEXITED(ret) || WEXITSTATUS(ret)) {
+ fail("job didn't exit cleanly: %d", ret);
+ goto killout;
+ }
+ }
+ return 0;
+killout:
+ for (; i >= 0; i--)
+ kill(-jobs[i], SIGKILL);
+ return -1;
+}
+
+int main(int argc, char ** argv)
+{
+ int fdmaster, fdslave;
+ pid_t jobs[JOBS_MAX] = {};
+
+ test_init(argc, argv);
+
+ if (num_jobs > JOBS_MAX) {
+ pr_perror("%d jobs is too many", num_jobs);
+ exit(1);
+ }
+
+ if (make_pty_pair(&fdmaster, &fdslave) < 0) {
+ pr_perror("can't make pty pair");
+ exit(1);
+ }
+
+ sleep(30);
+
+ if (start_jobs(jobs, num_jobs, fdmaster, fdslave)) {
+ pr_perror("failed to start jobs");
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (finish_jobs(jobs, num_jobs, fdmaster, fdslave))
+ fail("failed to finish jobs");
+ else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/lib/criu-rtc.so b/test/zdtm/static/lib/criu-rtc.so
new file mode 120000
index 000000000..17f3ef78e
--- /dev/null
+++ b/test/zdtm/static/lib/criu-rtc.so
@@ -0,0 +1 @@
+../criu-rtc.so \ No newline at end of file
diff --git a/test/zdtm/static/link10.c b/test/zdtm/static/link10.c
new file mode 100644
index 000000000..9f4c7f099
--- /dev/null
+++ b/test/zdtm/static/link10.c
@@ -0,0 +1,79 @@
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Migrate two hardlinked, open, and unlinked files";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char ** argv)
+{
+ int fd, fd2 = 0;
+ struct stat stat, stat2;
+ char filename2[256];
+
+ test_init(argc, argv);
+
+ if (snprintf(filename2, sizeof(filename2), "%s.lnk", filename) >=
+ sizeof(filename2)) {
+ pr_perror("filename %s is too long", filename);
+ exit(1);
+ }
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ if (link(filename, filename2) < 0) {
+ pr_perror("can't link %s to %s", filename, filename2);
+ goto unlink;
+ }
+
+ fd2 = open(filename2, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename2);
+ goto unlink;
+ }
+
+ unlink(filename2);
+ unlink(filename);
+
+ test_daemon();
+ test_waitsig();
+
+ if (fstat(fd, &stat) < 0 || fstat(fd2, &stat2) < 0) {
+ fail("fstat failed: %m");
+ goto out;
+ }
+
+ if (stat.st_ino != stat2.st_ino ||
+ stat.st_dev != stat2.st_dev) {
+ fail("files are different: st_ino %d != %d or st_dev %d != %d",
+ stat.st_ino, stat2.st_ino, stat.st_dev, stat2.st_dev);
+ }
+
+ pass();
+
+out:
+ close(fd);
+ close(fd2);
+ return 0;
+
+unlink:
+ close(fd);
+ close(fd2);
+ unlink(filename2);
+ unlink(filename);
+ return 1;
+}
diff --git a/test/zdtm/static/loginuid.c b/test/zdtm/static/loginuid.c
new file mode 100644
index 000000000..628fe64b9
--- /dev/null
+++ b/test/zdtm/static/loginuid.c
@@ -0,0 +1,99 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check for /proc/self/loginuid restore";
+const char *test_author = "Dmitry Safonov <dsafonov@odin.com>";
+
+const char loginuid_self[] = "/proc/self/loginuid";
+const uid_t test_value = 3;
+const uid_t INVALID_UID = (uid_t)-1;
+
+uid_t get_loginuid(const char *path, int *err)
+{
+ int fd;
+ ssize_t num;
+ char buf[11];
+
+ *err = 0;
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("Failed to open %s", path);
+ goto out;
+ }
+
+ num = read(fd, buf, 10);
+ close(fd);
+ if (num < 0) {
+ pr_perror("Unable to read %s", path);
+ goto out;
+ }
+ buf[num] = '\0';
+
+ return strtol(buf, NULL, 10);
+
+out:
+ *err = -1;
+ return 0;
+}
+
+int set_loginuid(const char *path, uid_t value)
+{
+ int fd, ret = 0;
+ char buf[11];
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ pr_perror("Failed to open %s", path);
+ return -1;
+ }
+
+ snprintf(buf, 11, "%u", value);
+
+ if (write(fd, buf, 11) < 0) {
+ pr_perror("Write %s to %s failed", buf, path);
+ ret = -1;
+ }
+
+ close(fd);
+ return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ uid_t new_loginuid;
+
+ /* unset before test */
+ if (set_loginuid(loginuid_self, INVALID_UID) < 0)
+ return -1;
+
+ test_init(argc, argv);
+
+ if (set_loginuid(loginuid_self, test_value) < 0)
+ return -1;
+
+ test_daemon();
+ test_waitsig();
+
+ new_loginuid = get_loginuid(loginuid_self, &ret);
+ if (ret < 0)
+ return -1;
+
+ if (new_loginuid != test_value) {
+ fail("loginuid value %d is different after restore: %d\n",
+ test_value, new_loginuid);
+ return -1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/loginuid.desc b/test/zdtm/static/loginuid.desc
new file mode 100644
index 000000000..e27f6ccf9
--- /dev/null
+++ b/test/zdtm/static/loginuid.desc
@@ -0,0 +1 @@
+{'feature': 'loginuid'}
diff --git a/test/zdtm/static/maps00.c b/test/zdtm/static/maps00.c
new file mode 100644
index 000000000..aa3c29e6a
--- /dev/null
+++ b/test/zdtm/static/maps00.c
@@ -0,0 +1,268 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Create all sorts of maps and compare /proc/pid/maps\n"
+ "before and after migration\n";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+const static int map_prots[] = {
+ PROT_NONE,
+ PROT_READ,
+ PROT_READ | PROT_WRITE,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+};
+#define NUM_MPROTS sizeof(map_prots) / sizeof(int)
+#define RW_PROT(x) ((x) & (PROT_READ | PROT_WRITE))
+#define X_PROT(x) ((x) & PROT_EXEC)
+
+int check_prot(int src_prot, int dst_prot)
+{
+ if (RW_PROT(src_prot) != RW_PROT(dst_prot))
+ return 0;
+ /* If exec bit will be enabled may depend on NX capablity of CPUs of
+ * source and destination nodes. In any case, migrated mapping should
+ * not have less permissions than newly created one
+ **
+ * A is a subset of B iff (A & B) == A
+ */
+ return (X_PROT(dst_prot) & X_PROT(src_prot)) == X_PROT(dst_prot);
+}
+
+const static int map_flags[] = {
+ MAP_PRIVATE,
+ MAP_SHARED,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ MAP_SHARED | MAP_ANONYMOUS
+};
+#define NUM_MFLAGS sizeof(map_flags) / sizeof(int)
+#define NUM_MAPS NUM_MPROTS * NUM_MFLAGS
+#define ONE_MAP_SIZE 0x2000
+
+struct map
+{
+ int prot;
+ int prot_real;
+ int flag;
+ char filename[256];
+ int fd;
+ void *ptr;
+};
+
+static void init_map(struct map *map, int prot_no, int flag_no)
+{
+ map->fd = -1;
+ map->prot = map_prots[prot_no];
+ map->flag = map_flags[flag_no];
+}
+
+static int make_map(struct map *map)
+{
+ uint32_t crc;
+ uint8_t buf[ONE_MAP_SIZE];
+ static int i = 0;
+
+ if (!(map->flag & MAP_ANONYMOUS)) {
+ /* need file */
+ if (snprintf(map->filename, sizeof(map->filename),
+ "%s-%02d", filename, i++) >= sizeof(map->filename)) {
+ pr_perror("filename %s is too long", filename);
+ return -1;
+ }
+
+ map->fd = open(map->filename, O_RDWR | O_CREAT, 0600);
+ if (map->fd < 0) {
+ pr_perror("can't open %s", map->filename);
+ return -1;
+ }
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+ if (write(map->fd, buf, sizeof(buf)) != sizeof(buf)) {
+ pr_perror("failed to write %s", map->filename);
+ return -1;
+ }
+ }
+
+ map->ptr = mmap(NULL, ONE_MAP_SIZE, map->prot, map->flag, map->fd, 0);
+ if (map->ptr == MAP_FAILED) {
+ pr_perror("can't create mapping");
+ return -1;
+ }
+
+ if ((map->flag & MAP_ANONYMOUS) && (map->prot & PROT_WRITE)) {
+ /* can't fill it with data otherwise */
+ crc = ~0;
+ datagen(map->ptr, ONE_MAP_SIZE, &crc);
+ }
+
+ test_msg("map: ptr %p flag %8x prot %8x\n",
+ map->ptr, map->flag, map->prot);
+
+ return 0;
+}
+
+static sigjmp_buf segv_ret; /* we need sig*jmp stuff, otherwise SIGSEGV will reset our handler */
+static void segfault(int signo)
+{
+ siglongjmp(segv_ret, 1);
+}
+
+/*
+ * after test func should be placed check map, because size of test_func
+ * is calculated as (check_map-test_func)
+ */
+int test_func()
+{
+ return 1;
+}
+static int check_map(struct map *map)
+{
+ int prot = PROT_WRITE | PROT_READ | PROT_EXEC;
+
+ if (signal(SIGSEGV, segfault) == SIG_ERR)
+ {
+ fail("setting SIGSEGV handler failed: %m\n");
+ return -1;
+ }
+ if (!sigsetjmp(segv_ret, 1))
+ {
+ uint32_t crc = ~0;
+ if (datachk(map->ptr, ONE_MAP_SIZE, &crc)) /* perform read access */
+ if (!(map->flag & MAP_ANONYMOUS) ||
+ (map->prot & PROT_WRITE)) { /* anon maps could only be filled when r/w */
+ fail("CRC mismatch: ptr %p flag %8x prot %8x\n",
+ map->ptr, map->flag, map->prot);
+ return -1;
+ }
+ /* prot |= PROT_READ// need barrier before this line,
+ because compiler change order commands.
+ I finded one method: look at next lines*/
+ } else
+ prot &= PROT_WRITE | !PROT_READ | PROT_EXEC;
+
+ if (signal(SIGSEGV, segfault) == SIG_ERR)
+ {
+ fail("setting SIGSEGV handler failed: %m\n");
+ return -1;
+ }
+
+ if (!sigsetjmp(segv_ret, 1))
+ {
+ * (int *) (map->ptr) = 1234; /* perform write access */
+ } else
+ prot &= !PROT_WRITE | PROT_READ | PROT_EXEC;
+
+ if (signal(SIGSEGV, segfault) == SIG_ERR)
+ {
+ fail("restoring SIGSEGV handler failed: %m\n");
+ return -1;
+ }
+
+ if (!sigsetjmp(segv_ret, 1))
+ {
+ if (map->prot & PROT_WRITE) {
+ memcpy(map->ptr,test_func, getpagesize());
+ } else {
+ if (!(map->flag & MAP_ANONYMOUS)) {
+ lseek(map->fd,0,SEEK_SET);
+ if (write(map->fd,test_func,check_map - test_func)<check_map - test_func) {
+ pr_perror("failed to write %s", map->filename);
+ return -1;
+ }
+ }
+ }
+ if (!(map->flag & MAP_ANONYMOUS) || map->prot & PROT_WRITE)
+ /* Function body has been copied into the mapping */
+ ((int (*)())map->ptr)(); /* perform exec access */
+ else
+ /* No way to copy function body into mapping,
+ * clear exec bit from effective protection
+ */
+ prot &= PROT_WRITE | PROT_READ | !PROT_EXEC;
+ } else
+ prot &= PROT_WRITE | PROT_READ | !PROT_EXEC;
+
+ if (signal(SIGSEGV, SIG_DFL) == SIG_ERR)
+ {
+ fail("restoring SIGSEGV handler failed: %m\n");
+ return -1;
+ }
+
+ return prot;
+}
+
+static void destroy_map(struct map *map)
+{
+ munmap(map->ptr, ONE_MAP_SIZE);
+
+ if (map->fd >= 0)
+ {
+ close(map->fd);
+ unlink(map->filename);
+ }
+}
+
+
+#define MAPS_LEN 0x10000
+
+int main(int argc, char ** argv)
+{
+ struct map maps[NUM_MAPS] = {}, maps_compare[NUM_MAPS] = {};
+ int i, j, k;
+ test_init(argc, argv);
+
+ k = 0;
+ for (i = 0; i < NUM_MPROTS; i++)
+ for (j = 0; j < NUM_MFLAGS; j++)
+ init_map(maps + k++, i, j);
+
+ for (i = 0; i < NUM_MAPS; i++)
+ if (make_map(maps + i))
+ goto err;
+
+ test_daemon();
+ test_waitsig();
+
+ for (i = 0; i < NUM_MAPS; i++)
+ if ((maps[i].prot_real=check_map(maps + i))<0)
+ goto err;
+ k=0;
+ for (i = 0; i < NUM_MPROTS; i++)
+ for (j = 0; j < NUM_MFLAGS; j++)
+ init_map(maps_compare + k++, i, j);
+ for (i = 0; i < NUM_MAPS; i++)
+ if (make_map(maps_compare+ i))
+ goto err;
+ for (i = 0; i < NUM_MAPS; i++)
+ if ((maps_compare[i].prot_real=check_map(maps_compare + i))<0)
+ goto err;
+ for (i = 0; i< NUM_MAPS; i++)
+ if (!check_prot(maps[i].prot_real, maps_compare[i].prot_real)){
+ fail("protection on %i (flag=%d prot=%d) maps has changed (prot=%d(expected %d))",
+ i, maps[i].flag, maps[i].prot, maps[i].prot_real, maps_compare[i].prot_real);
+ goto err;
+ }
+
+ pass();
+
+ for (i = 0; i < NUM_MAPS; i++) {
+ destroy_map(maps + i);
+ destroy_map(maps_compare + i);
+ }
+ return 0;
+
+err:
+ return 1;
+}
diff --git a/test/zdtm/static/maps01.c b/test/zdtm/static/maps01.c
new file mode 100644
index 000000000..024dff1fc
--- /dev/null
+++ b/test/zdtm/static/maps01.c
@@ -0,0 +1,171 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <linux/limits.h>
+#include "zdtmtst.h"
+
+#define MEM_SIZE (1L << 30)
+#define MEM_OFFSET (1L << 29)
+#define MEM_OFFSET2 (MEM_SIZE - PAGE_SIZE)
+#define MEM_OFFSET3 (20 * PAGE_SIZE)
+
+const char *test_doc = "Test shared memory";
+const char *test_author = "Andrew Vagin <avagin@openvz.org";
+
+int main(int argc, char ** argv)
+{
+ void *m, *m2, *p, *p2;
+ char path[PATH_MAX];
+ uint32_t crc;
+ pid_t pid = -1;
+ int status, fd;
+ task_waiter_t t;
+
+ test_init(argc, argv);
+
+ task_waiter_init(&t);
+
+ m = mmap(NULL, MEM_SIZE, PROT_WRITE | PROT_READ,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+
+ if (m == MAP_FAILED)
+ goto err;
+
+ p = mmap(NULL, MEM_SIZE, PROT_WRITE | PROT_READ,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+
+ if (p == MAP_FAILED)
+ goto err;
+
+ p2 = mmap(NULL, MEM_OFFSET, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (p2 == MAP_FAILED)
+ goto err;
+
+ pid = test_fork();
+ if (pid < 0) {
+ goto err;
+ } else if (pid == 0) {
+ void *p3;
+
+ p3 = mmap(NULL, MEM_OFFSET3, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (p3 == MAP_FAILED)
+ goto err;
+
+ crc = ~0;
+ datagen(m + MEM_OFFSET, PAGE_SIZE, &crc);
+ crc = ~0;
+ datagen(m + MEM_OFFSET2, PAGE_SIZE, &crc);
+ crc = ~0;
+ datagen(p + MEM_OFFSET + MEM_OFFSET3, PAGE_SIZE, &crc);
+ crc = ~0;
+ datagen(p + MEM_OFFSET + 2 * MEM_OFFSET3, PAGE_SIZE, &crc);
+ crc = ~0;
+ datagen(p + MEM_OFFSET3, PAGE_SIZE, &crc);
+ crc = ~0;
+ datagen(p3, PAGE_SIZE, &crc);
+
+ task_waiter_complete(&t, 1);
+
+ test_waitsig();
+
+ crc = ~0;
+ status = datachk(m + MEM_OFFSET, PAGE_SIZE, &crc);
+ if (status)
+ return 1;
+ crc = ~0;
+ status = datachk(m + MEM_OFFSET2, PAGE_SIZE, &crc);
+ if (status)
+ return 1;
+ crc = ~0;
+ status = datachk(m + PAGE_SIZE, PAGE_SIZE, &crc);
+ if (status)
+ return 1;
+ crc = ~0;
+ status = datachk(p + MEM_OFFSET + 2 * MEM_OFFSET3, PAGE_SIZE, &crc);
+ if (status)
+ return 1;
+ crc = ~0;
+ status = datachk(p + MEM_OFFSET3, PAGE_SIZE, &crc);
+ if (status)
+ return 1;
+ crc = ~0;
+ status = datachk(p3, PAGE_SIZE, &crc);
+ if (status)
+ return 1;
+ return 0;
+ }
+ task_waiter_wait4(&t, 1);
+
+ munmap(p, MEM_OFFSET);
+ p2 = mremap(p + MEM_OFFSET, MEM_OFFSET, MEM_OFFSET, MREMAP_FIXED | MREMAP_MAYMOVE, p2);
+ if (p2 == MAP_FAILED)
+ goto err;
+
+ snprintf(path, PATH_MAX, "/proc/self/map_files/%lx-%lx",
+ (unsigned long) m,
+ (unsigned long) m + MEM_SIZE);
+ fd = open(path, O_RDWR);
+ if (fd == -1) {
+ pr_perror("Can't open file %s", path);
+ goto err;
+ }
+
+ m2 = mmap(NULL, PAGE_SIZE, PROT_WRITE | PROT_READ, MAP_SHARED, fd, MEM_OFFSET3);
+ if (m2 == MAP_FAILED) {
+ pr_perror("Can't map file %s", path);
+ goto err;
+ }
+ close(fd);
+
+ munmap(m, PAGE_SIZE);
+ munmap(m + PAGE_SIZE * 10, PAGE_SIZE);
+ munmap(m + MEM_OFFSET2, PAGE_SIZE);
+
+ crc = ~0;
+ datagen(m + PAGE_SIZE, PAGE_SIZE, &crc);
+
+ crc = ~0;
+ datagen(m2, PAGE_SIZE, &crc);
+
+ test_daemon();
+ test_waitsig();
+
+ kill(pid, SIGTERM);
+ wait(&status);
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status))
+ goto err;
+ } else
+ goto err;
+
+ crc = ~0;
+ if (datachk(m + MEM_OFFSET, PAGE_SIZE, &crc))
+ goto err;
+
+ crc = ~0;
+ if (datachk(m2, PAGE_SIZE, &crc))
+ goto err;
+
+ crc = ~0;
+ if (datachk(p2 + MEM_OFFSET3, PAGE_SIZE, &crc))
+ goto err;
+
+ pass();
+
+ return 0;
+err:
+ if (waitpid(-1, NULL, WNOHANG) == 0) {
+ kill(pid, SIGTERM);
+ wait(NULL);
+ }
+ return 1;
+}
diff --git a/test/zdtm/static/maps01.desc b/test/zdtm/static/maps01.desc
new file mode 100644
index 000000000..d969725f6
--- /dev/null
+++ b/test/zdtm/static/maps01.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/maps02.c b/test/zdtm/static/maps02.c
new file mode 100644
index 000000000..4c12bec99
--- /dev/null
+++ b/test/zdtm/static/maps02.c
@@ -0,0 +1,110 @@
+#include <sys/mman.h>
+#include "zdtmtst.h"
+
+#ifndef MADV_DONTDUMP
+#define MADV_DONTDUMP 16
+#endif
+
+const char *test_doc = "Test shared memory with advises";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+struct mmap_data {
+ void *start;
+ unsigned long orig_flags;
+ unsigned long orig_madv;
+ unsigned long new_flags;
+ unsigned long new_madv;
+};
+
+#define MEM_SIZE (8192)
+
+static int alloc_anon_mmap(struct mmap_data *m, int flags, int adv)
+{
+ m->start = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE,
+ flags, -1, 0);
+ if (m->start == MAP_FAILED) {
+ pr_perror("mmap failed");
+ return -1;
+ }
+
+ if (madvise(m->start, MEM_SIZE, adv)) {
+ if (errno == EINVAL) {
+ test_msg("madvise failed, no kernel support\n");
+ munmap(m->start, MEM_SIZE);
+ *m = (struct mmap_data){ };
+ } else {
+ pr_perror("madvise failed");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct mmap_data m[5] = { };
+ size_t i;
+
+ test_init(argc, argv);
+
+ test_msg("Alloc growsdown\n");
+ if (alloc_anon_mmap(&m[0], MAP_PRIVATE | MAP_ANONYMOUS, MADV_DONTFORK))
+ return -1;
+
+ test_msg("Alloc locked/sequential\n");
+ if (alloc_anon_mmap(&m[1], MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, MADV_SEQUENTIAL))
+ return -1;
+
+ test_msg("Alloc noreserve/dontdump\n");
+ if (alloc_anon_mmap(&m[2], MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, MADV_DONTDUMP))
+ return -1;
+
+ test_msg("Alloc hugetlb/hugepage\n");
+ if (alloc_anon_mmap(&m[3], MAP_PRIVATE | MAP_ANONYMOUS, MADV_HUGEPAGE))
+ return -1;
+
+ test_msg("Alloc dontfork/random|mergeable\n");
+ if (alloc_anon_mmap(&m[4], MAP_PRIVATE | MAP_ANONYMOUS, MADV_MERGEABLE))
+ return -1;
+
+ test_msg("Fetch existing flags/adv\n");
+ for (i = 0; i < sizeof(m)/sizeof(m[0]); i++) {
+ if (get_smaps_bits((unsigned long)m[i].start,
+ &m[i].orig_flags,
+ &m[i].orig_madv))
+ return -1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ test_msg("Fetch restored flags/adv\n");
+ for (i = 0; i < sizeof(m)/sizeof(m[0]); i++) {
+ if (get_smaps_bits((unsigned long)m[i].start,
+ &m[i].new_flags,
+ &m[i].new_madv))
+ return -1;
+
+ if (m[i].orig_flags != m[i].new_flags) {
+ pr_perror("Flags are changed %lx %lx -> %lx (%d)",
+ (unsigned long)m[i].start,
+ m[i].orig_flags, m[i].new_flags, i);
+ fail();
+ return -1;
+ }
+
+ if (m[i].orig_madv != m[i].new_madv) {
+ pr_perror("Madvs are changed %lx %lx -> %lx (%d)",
+ (unsigned long)m[i].start,
+ m[i].orig_madv, m[i].new_madv, i);
+ fail();
+ return -1;
+ }
+
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/maps03.c b/test/zdtm/static/maps03.c
new file mode 100644
index 000000000..c4aba196a
--- /dev/null
+++ b/test/zdtm/static/maps03.c
@@ -0,0 +1,38 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Test for huge VMA area";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+int main(int argc, char **argv)
+{
+ test_init(argc, argv);
+ unsigned char *mem;
+
+ test_msg("Alloc huge VMA\n");
+ mem = (void *)mmap(NULL, (10L << 30), PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if ((void *)mem == MAP_FAILED) {
+ pr_perror("mmap failed");
+ return -1;
+ }
+
+ mem[4L << 30] = 1;
+ mem[8L << 30] = 2;
+
+ test_daemon();
+ test_waitsig();
+
+ test_msg("Testing restored data\n");
+
+ if (mem[4L << 30] != 1 || mem[8L << 30] != 2) {
+ fail("Data corrupted!\n");
+ exit(1);
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/maps03.desc b/test/zdtm/static/maps03.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/maps03.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/maps04.c b/test/zdtm/static/maps04.c
new file mode 100644
index 000000000..780c5667d
--- /dev/null
+++ b/test/zdtm/static/maps04.c
@@ -0,0 +1,57 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <linux/limits.h>
+#include "zdtmtst.h"
+
+#define MEM_SIZE (1L << 29)
+
+const char *test_doc = "Test big mappings";
+const char *test_author = "Andrew Vagin <avagin@openvz.org";
+
+int main(int argc, char ** argv)
+{
+ void *m;
+ uint32_t crc;
+ int i;
+
+ test_init(argc, argv);
+
+ m = mmap(NULL, MEM_SIZE, PROT_WRITE | PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (m == MAP_FAILED) {
+ fail();
+ return 1;
+ }
+
+ crc = ~0;
+ datagen(m, MEM_SIZE, &crc);
+
+ for (i = 0; i < MEM_SIZE / (1<<20); i++)
+ if (mprotect(m + (lrand48() * PAGE_SIZE % MEM_SIZE), PAGE_SIZE, PROT_NONE)) {
+ pr_perror("mprotect");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (mprotect(m, MEM_SIZE, PROT_READ))
+ pr_perror("mprotect");
+
+ crc = ~0;
+ if (datachk(m, MEM_SIZE, &crc))
+ fail("Mem corrupted");
+ else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/maps05.c b/test/zdtm/static/maps05.c
new file mode 100644
index 000000000..faa09ee9a
--- /dev/null
+++ b/test/zdtm/static/maps05.c
@@ -0,0 +1,91 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Create a bunch of small VMAs and test they survive transferring\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+#define NR_MAPS 4096
+
+#define NR_MAPS_1 (NR_MAPS + 0)
+#define NR_MAPS_2 (NR_MAPS + 1)
+
+#define MAPS_SIZE_1 (140 << 10)
+#define MAPS_SIZE_2 (8192)
+
+int main(int argc, char *argv[])
+{
+ void *map[NR_MAPS + 2] = { }, *addr;
+ size_t i, summary;
+
+ test_init(argc, argv);
+
+ summary = NR_MAPS * 2 * 4096 + MAPS_SIZE_1 + MAPS_SIZE_2 + (1 << 20);
+
+ addr = mmap(NULL, summary, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (addr == MAP_FAILED) {
+ pr_perror("Can't mmap");
+ return 1;
+ }
+ munmap(addr, summary);
+
+ for (i = 0; i < NR_MAPS; i++) {
+ map[i] = mmap(i > 0 ? map[i - 1] + 8192 : addr, 4096, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (map[i] == MAP_FAILED) {
+ pr_perror("Can't mmap");
+ return 1;
+ } else {
+ /* Dirtify it */
+ int *v = (void *)map[i];
+ *v = i;
+ }
+ }
+
+ map[NR_MAPS_1] = mmap(map[NR_MAPS_1 - 1] + 8192, MAPS_SIZE_1, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0);
+ if (map[NR_MAPS_1] == MAP_FAILED) {
+ pr_perror("Can't mmap");
+ return 1;
+ } else {
+ /* Dirtify it */
+ int *v = (void *)map[NR_MAPS_1];
+ *v = i;
+ test_msg("map-1: %p %p\n", map[NR_MAPS_1], map[NR_MAPS_1] + MAPS_SIZE_1);
+ }
+
+ map[NR_MAPS_2] = mmap(map[NR_MAPS_1] + MAPS_SIZE_1, MAPS_SIZE_2, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0);
+ if (map[NR_MAPS_2] == MAP_FAILED) {
+ pr_perror("Can't mmap");
+ return 1;
+ } else {
+ /* Dirtify it */
+ int *v = (void *)map[NR_MAPS_2];
+ *v = i;
+ test_msg("map-2: %p %p\n", map[NR_MAPS_2], map[NR_MAPS_2] + MAPS_SIZE_2);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ for (i = 0; i < NR_MAPS; i++) {
+ int *v = (void *)map[i];
+
+ if (*v != i) {
+ fail("Data corrupted at page %lu", (unsigned long)i);
+ return 1;
+ }
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/maps_file_prot.c b/test/zdtm/static/maps_file_prot.c
new file mode 100644
index 000000000..3b28c1ff1
--- /dev/null
+++ b/test/zdtm/static/maps_file_prot.c
@@ -0,0 +1,53 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/limits.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Test mappings of same file with different prot";
+const char *test_author = "Jamie Liu <jamieliu@google.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+#define die(fmt, arg...) do { pr_perror(fmt, ## arg); return 1; } while (0)
+
+int main(int argc, char ** argv)
+{
+ void *ro_map, *rw_map;
+ int fd;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_RDWR | O_CREAT, 0644);
+ if (fd < 0)
+ die("open failed");
+ if (ftruncate(fd, 2 * PAGE_SIZE))
+ die("ftruncate failed");
+
+ ro_map = mmap(NULL, 2 * PAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0);
+ if (ro_map == MAP_FAILED)
+ die("mmap failed");
+ rw_map = ro_map + PAGE_SIZE;
+ if (mprotect(rw_map, PAGE_SIZE, PROT_READ | PROT_WRITE))
+ die("mprotect failed");
+
+ close(fd);
+
+ test_daemon();
+ test_waitsig();
+
+ /* Check that rw_map is still writeable */
+ *(volatile char *)rw_map = 1;
+
+ if (mprotect(ro_map, PAGE_SIZE, PROT_READ | PROT_WRITE)) {
+ fail("mprotect after restore failed");
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/mem-touch.c b/test/zdtm/static/mem-touch.c
new file mode 100644
index 000000000..c14b48050
--- /dev/null
+++ b/test/zdtm/static/mem-touch.c
@@ -0,0 +1,62 @@
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/mman.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check changing memory";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+#define MEM_PAGES 16
+
+int main(int argc, char **argv)
+{
+ void *mem;
+ int i, fail = 0;
+ unsigned rover = 1;
+ unsigned backup[MEM_PAGES] = {};
+
+ srand(time(NULL));
+
+ test_init(argc, argv);
+
+ mem = mmap(NULL, MEM_PAGES * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0);
+ if (mem == MAP_FAILED)
+ return 1;
+
+ test_msg("mem %p backup %p\n", mem, backup);
+
+ test_daemon();
+ while (test_go()) {
+ unsigned pfn;
+ struct timespec req = { .tv_sec = 0, .tv_nsec = 100000, };
+
+ pfn = random() % MEM_PAGES;
+ *(unsigned *)(mem + pfn * PAGE_SIZE) = rover;
+ backup[pfn] = rover;
+ test_msg("t %u %u\n", pfn, rover);
+ rover++;
+ nanosleep(&req, NULL);
+ }
+ test_waitsig();
+
+ test_msg("final rover %u\n", rover);
+ for (i = 0; i < MEM_PAGES; i++)
+ if (backup[i] != *(unsigned *)(mem + i * PAGE_SIZE)) {
+ test_msg("Page %u differs want %u has %u\n", i,
+ backup[i], *(unsigned *)(mem + i * PAGE_SIZE));
+ fail = 1;
+ } else
+ test_msg("Page %u matches %u\n", i, backup[i]);
+
+ if (fail)
+ fail("Memory corruption\n");
+ else
+ pass();
+
+ return 0;
+}
+
diff --git a/test/zdtm/static/mem-touch.desc b/test/zdtm/static/mem-touch.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/mem-touch.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/mlock_setuid.c b/test/zdtm/static/mlock_setuid.c
new file mode 100644
index 000000000..92b313ed5
--- /dev/null
+++ b/test/zdtm/static/mlock_setuid.c
@@ -0,0 +1,55 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include "zdtmtst.h"
+
+#define MEM_SIZE (69632)
+
+int main(int argc, char **argv)
+{
+ int ret;
+ void *start;
+ unsigned long new_flags = 0;
+ unsigned long new_madv = 0;
+ test_init(argc, argv);
+
+ test_msg("Alloc vma of size %d\n", MEM_SIZE);
+ start = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (start == MAP_FAILED) {
+ pr_perror("mmap failed");
+ return -1;
+ }
+
+ test_msg("Lock vma from %lx to %lx\n", start, start + MEM_SIZE);
+ ret = mlock(start, MEM_SIZE);
+ if (ret < 0) {
+ pr_perror("mlock");
+ return -1;
+ }
+
+ test_daemon();
+
+ test_msg("Setuid to 18943\n");
+ ret = setuid(18943);
+ if (ret < 0) {
+ pr_perror("setuid");
+ return -1;
+ }
+
+ test_waitsig();
+
+ ret = get_smaps_bits((unsigned long)start, &new_flags, &new_madv);
+ if (ret < 0)
+ return -1;
+
+ test_msg("Check smaps flags for MAP_LOCKED\n");
+ if (new_flags & MAP_LOCKED) {
+ pass();
+ } else {
+ fail("Vma is not locked after c/r\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/test/zdtm/static/mlock_setuid.desc b/test/zdtm/static/mlock_setuid.desc
new file mode 100644
index 000000000..d969725f6
--- /dev/null
+++ b/test/zdtm/static/mlock_setuid.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/mmx00.c b/test/zdtm/static/mmx00.c
new file mode 100644
index 000000000..f0f7c3c9e
--- /dev/null
+++ b/test/zdtm/static/mmx00.c
@@ -0,0 +1,99 @@
+#include <string.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Start a calculation, leaving MMX in a certain state,\n"
+"before migration, continue after";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+#if defined(__i386__) || defined(__x86_64__)
+void start(uint8_t *bytes, uint16_t *words)
+{
+ __asm__ volatile (
+ "movq %0, %%mm0\n"
+ "movq %1, %%mm1\n"
+ "movq %2, %%mm2\n"
+ "movq %3, %%mm3\n"
+ "paddb %%mm0, %%mm1\n"
+ "psubw %%mm2, %%mm3\n"
+ :
+ : "m" (bytes[0]), "m" (bytes[8]),
+ "m" (words[0]), "m" (words[4])
+ );
+}
+
+void finish(uint8_t *bytes, uint16_t *words)
+{
+ __asm__ volatile (
+ "movq %%mm1, %0\n"
+ "movq %%mm3, %1\n"
+ : "=m" (bytes[0]), "=m" (words[0])
+ );
+}
+
+static inline void cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
+{
+ __asm__("cpuid"
+ : "=a" (*eax),
+ "=b" (*ebx),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "0" (op), "c"(0));
+}
+
+int chk_proc_mmx(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ cpuid(1, &eax, &ebx, &ecx, &edx);
+ return edx & (1 << 23);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ uint8_t bytes[16];
+ uint16_t words[8];
+ uint32_t rnd[8];
+ int i;
+
+ uint8_t resbytes1[8], resbytes2[8];
+ uint16_t reswords1[4], reswords2[4];
+#endif
+
+ test_init(argc, argv);
+#if defined(__i386__) || defined(__x86_64__)
+ if (!chk_proc_mmx()) {
+ skip("MMX not supported");
+ return 1;
+ }
+
+ for (i = 0; i < (sizeof(bytes) + sizeof(words)) / 4; i++)
+ rnd[i] = mrand48();
+
+ memcpy((uint8_t *) bytes, (uint8_t *) rnd, sizeof(bytes));
+ memcpy((uint8_t *) words, (uint8_t *) rnd + sizeof(bytes), sizeof(words));
+
+ start(bytes, words);
+ finish(resbytes1, reswords1);
+
+ start(bytes, words);
+
+ test_daemon();
+ test_waitsig();
+
+ finish(resbytes2, reswords2);
+
+ if (memcmp((uint8_t *) resbytes1, (uint8_t *) resbytes2, sizeof(resbytes1)))
+ fail("byte op mismatch\n");
+ else if (memcmp((uint8_t *) reswords1, (uint8_t *) reswords2, sizeof(reswords2)))
+ fail("word op mismatch\n");
+ else
+ pass();
+#else
+ skip("Unsupported arch");
+#endif
+ return 0;
+}
diff --git a/test/zdtm/static/mmx00.desc b/test/zdtm/static/mmx00.desc
new file mode 100644
index 000000000..d2f501de2
--- /dev/null
+++ b/test/zdtm/static/mmx00.desc
@@ -0,0 +1 @@
+{'arch': 'x86_64'}
diff --git a/test/zdtm/static/mnt_ext_auto.c b/test/zdtm/static/mnt_ext_auto.c
new file mode 100644
index 000000000..4a931b9a3
--- /dev/null
+++ b/test/zdtm/static/mnt_ext_auto.c
@@ -0,0 +1,71 @@
+#include <sys/mount.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <linux/limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Run busy loop while migrating";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *dirname = "mnt_ext_auto.test";
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char ** argv)
+{
+ char src[PATH_MAX], dst[PATH_MAX], *root;
+ char *dname = "/tmp/zdtm_ext_auto.XXXXXX";
+ int status;
+ pid_t pid;
+
+ root = getenv("ZDTM_ROOT");
+ if (root == NULL) {
+ pr_perror("root");
+ return 1;
+ }
+
+ sprintf(dst, "%s/ext_mounts", getenv("ZDTM_ROOT"));
+
+ if (strcmp(getenv("ZDTM_NEWNS"), "1"))
+ goto test;
+
+ pid = fork();
+ if (pid < 0)
+ return 1;
+ if (pid == 0) {
+ test_ext_init(argc, argv);
+
+ mkdir(dname, 755);
+ sprintf(src, "%s/test", dname);
+ if (mount("zdtm_auto_ext_mnt", dname, "tmpfs", 0, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+ mkdir(src, 755);
+ mkdir(dst, 755);
+ if (mount(src, dst, NULL, MS_BIND, NULL)) {
+ pr_perror("bind");
+ return 1;
+ }
+ return 0;
+ }
+
+ wait(&status);
+ if (status != 0)
+ return 1;
+
+test:
+ test_init(argc, argv);
+
+ test_daemon();
+ test_waitsig();
+
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/mnt_ext_auto.desc b/test/zdtm/static/mnt_ext_auto.desc
new file mode 100644
index 000000000..12d1427dd
--- /dev/null
+++ b/test/zdtm/static/mnt_ext_auto.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'feature': 'mnt_id', 'opts': '--ext-mount-map auto --enable-external-sharing'}
diff --git a/test/zdtm/static/mnt_ext_auto.opts b/test/zdtm/static/mnt_ext_auto.opts
new file mode 100644
index 000000000..aab0bcd0a
--- /dev/null
+++ b/test/zdtm/static/mnt_ext_auto.opts
@@ -0,0 +1 @@
+--ext-mount-map auto --enable-external-sharing
diff --git a/test/zdtm/static/mnt_ext_master.c b/test/zdtm/static/mnt_ext_master.c
new file mode 100644
index 000000000..290868ef5
--- /dev/null
+++ b/test/zdtm/static/mnt_ext_master.c
@@ -0,0 +1,78 @@
+#include <sys/mount.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <linux/limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that mounts with external master peers are c/r'd";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+char *dirname = "mnt_ext_auto.test";
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char ** argv)
+{
+ char src[PATH_MAX], dst[PATH_MAX], *root;
+ char *dname = "/tmp/zdtm_ext_auto.XXXXXX";
+ int status;
+ pid_t pid;
+
+ root = getenv("ZDTM_ROOT");
+ if (root == NULL) {
+ pr_perror("root");
+ return 1;
+ }
+
+ sprintf(dst, "%s/ext_mounts", getenv("ZDTM_ROOT"));
+
+ if (strcmp(getenv("ZDTM_NEWNS"), "1"))
+ goto test;
+
+ pid = fork();
+ if (pid < 0)
+ return 1;
+ if (pid == 0) {
+ test_ext_init(argc, argv);
+
+ mkdir(dname, 755);
+ sprintf(src, "%s/test", dname);
+ if (mount("zdtm_auto_ext_mnt", dname, "tmpfs", 0, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+ mkdir(src, 755);
+ mkdir(dst, 755);
+ if (mount(src, dst, NULL, MS_BIND, NULL)) {
+ pr_perror("bind");
+ return 1;
+ }
+
+ if (mount(src, dst, NULL, MS_SLAVE, NULL)) {
+ pr_perror("slave");
+ return 1;
+ }
+
+ return 0;
+ }
+
+ wait(&status);
+ if (status != 0)
+ return 1;
+
+test:
+ test_init(argc, argv);
+
+ test_daemon();
+ test_waitsig();
+
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/mnt_ext_master.desc b/test/zdtm/static/mnt_ext_master.desc
new file mode 100644
index 000000000..57d3787c6
--- /dev/null
+++ b/test/zdtm/static/mnt_ext_master.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'feature': 'mnt_id', 'opts': '--ext-mount-map auto --enable-external-masters'}
diff --git a/test/zdtm/static/mnt_ext_master.opts b/test/zdtm/static/mnt_ext_master.opts
new file mode 100644
index 000000000..bbcdf9f33
--- /dev/null
+++ b/test/zdtm/static/mnt_ext_master.opts
@@ -0,0 +1 @@
+--ext-mount-map auto --enable-external-masters
diff --git a/test/zdtm/static/mnt_ro_bind.c b/test/zdtm/static/mnt_ro_bind.c
new file mode 100644
index 000000000..1d9881408
--- /dev/null
+++ b/test/zdtm/static/mnt_ro_bind.c
@@ -0,0 +1,84 @@
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+#include <errno.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check read-only bind-mounts";
+const char *test_author = "Andrew Vagin <avagin@openvz.org>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+#define TEST_WORD "testtest"
+#define TEST_WORD2 "TESTTEST"
+
+int main(int argc, char **argv)
+{
+ int fd, ret = 1;
+ char rw_path[PATH_MAX], ro_path[PATH_MAX], rw_f[PATH_MAX], ro_f[PATH_MAX];
+
+ test_init(argc, argv);
+
+ snprintf(rw_path, sizeof(rw_path), "%s/rw", dirname);
+ snprintf(ro_path, sizeof(ro_path), "%s/ro", dirname);
+ snprintf(rw_f, sizeof(rw_f), "%s/rw/test", dirname);
+ snprintf(ro_f, sizeof(ro_f), "%s/ro/test", dirname);
+
+ mkdir(dirname, 0700);
+ if (mount("none", dirname, "tmpfs", 0, "") < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+ mkdir(rw_path, 0700);
+ mkdir(ro_path, 0700);
+
+ if (mount("zdtm_rw", rw_path, "tmpfs", 0, "") < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ if (mount(rw_path, ro_path, NULL, MS_BIND, NULL) < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ if (mount(NULL, ro_path, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ fd = open(ro_f, O_CREAT | O_WRONLY, 0666);
+ if (fd != -1 || errno != EROFS) {
+ fail("%s is created", ro_f);
+ goto err;
+ }
+
+ fd = open(rw_f, O_CREAT | O_WRONLY, 0666);
+ if (fd < 0) {
+ fail("Unable to create %s", rw_f);
+ goto err;
+ }
+ close(fd);
+
+ fd = open(ro_f, O_RDONLY);
+ if (fd < 0) {
+ fail("Unable to create %s", rw_f);
+ goto err;
+ }
+
+ pass();
+ ret = 0;
+err:
+ umount2(dirname, MNT_DETACH);
+ rmdir(dirname);
+ return ret;
+}
diff --git a/test/zdtm/static/mnt_ro_bind.desc b/test/zdtm/static/mnt_ro_bind.desc
new file mode 100644
index 000000000..7657ba45c
--- /dev/null
+++ b/test/zdtm/static/mnt_ro_bind.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid'}
diff --git a/test/zdtm/static/mntns_deleted.c b/test/zdtm/static/mntns_deleted.c
new file mode 100644
index 000000000..b38d8340b
--- /dev/null
+++ b/test/zdtm/static/mntns_deleted.c
@@ -0,0 +1,104 @@
+#define _GNU_SOURCE
+
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sched.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "zdtmtst.h"
+
+#ifndef CLONE_NEWNS
+#define CLONE_NEWNS 0x00020000
+#endif
+
+const char *test_doc = "Check the restore of deleted bindmounts";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+#define TEST_DIR_SRC "test-src"
+#define TEST_DIR_DST "test-dst"
+
+#define TEST_FILE_SRC "mntns-deleted-src"
+#define TEST_FILE_DST "mntns-deleted-dst"
+
+int main(int argc, char *argv[])
+{
+ char path_src[PATH_MAX], path_dst[PATH_MAX];
+ int fd1, fd2;
+
+ test_init(argc, argv);
+
+ if (mkdir(dirname, 0700)) {
+ pr_perror("mkdir %s", dirname);
+ exit(1);
+ }
+
+ if (mount("none", dirname, "tmpfs", MS_MGC_VAL, NULL)) {
+ pr_perror("mount %s", dirname);
+ return 1;
+ }
+
+ snprintf(path_src, sizeof(path_src), "%s/%s", dirname, TEST_DIR_SRC);
+ snprintf(path_dst, sizeof(path_dst), "%s/%s", dirname, TEST_DIR_DST);
+
+ rmdir(path_src);
+ rmdir(path_dst);
+
+ unlink(TEST_FILE_SRC);
+ unlink(TEST_FILE_DST);
+
+ if (mkdir(path_src, 0700) ||
+ mkdir(path_dst, 0700)) {
+ pr_perror("mkdir");
+ return 1;
+ }
+
+ if ((fd1 = open(TEST_FILE_SRC, O_WRONLY | O_CREAT | O_TRUNC, 0600) < 0)) {
+ pr_perror("touching %s", TEST_FILE_SRC);
+ return 1;
+ }
+ close(fd1);
+
+ if ((fd2 = open(TEST_FILE_DST, O_WRONLY | O_CREAT | O_TRUNC, 0600) < 0)) {
+ pr_perror("touching %s", TEST_FILE_DST);
+ return 1;
+ }
+ close(fd2);
+
+ if (mount(path_src, path_dst, NULL, MS_BIND | MS_MGC_VAL, NULL)) {
+ pr_perror("mount %s -> %s", path_src, path_dst);
+ return 1;
+ }
+
+ if (mount(TEST_FILE_SRC, TEST_FILE_DST, NULL, MS_BIND | MS_MGC_VAL, NULL)) {
+ pr_perror("mount %s -> %s", TEST_FILE_SRC, TEST_FILE_DST);
+ return 1;
+ }
+
+ if (rmdir(path_src)) {
+ pr_perror("rmdir %s", path_src);
+ return 1;
+ }
+
+ if (unlink(TEST_FILE_SRC)) {
+ pr_perror("unlink %s", TEST_FILE_SRC);
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/mntns_deleted.desc b/test/zdtm/static/mntns_deleted.desc
new file mode 100644
index 000000000..a8849e097
--- /dev/null
+++ b/test/zdtm/static/mntns_deleted.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid', 'feature': 'mnt_id'}
diff --git a/test/zdtm/static/mntns_link_ghost.c b/test/zdtm/static/mntns_link_ghost.c
new file mode 120000
index 000000000..0314c6281
--- /dev/null
+++ b/test/zdtm/static/mntns_link_ghost.c
@@ -0,0 +1 @@
+mntns_link_remap.c \ No newline at end of file
diff --git a/test/zdtm/static/mntns_link_ghost.desc b/test/zdtm/static/mntns_link_ghost.desc
new file mode 100644
index 000000000..3fd8e03f7
--- /dev/null
+++ b/test/zdtm/static/mntns_link_ghost.desc
@@ -0,0 +1 @@
+{'flavor': 'ns', 'flags': 'suid', 'feature': 'mnt_id'}
diff --git a/test/zdtm/static/mntns_link_remap.c b/test/zdtm/static/mntns_link_remap.c
new file mode 100644
index 000000000..210188bae
--- /dev/null
+++ b/test/zdtm/static/mntns_link_remap.c
@@ -0,0 +1,250 @@
+#define _GNU_SOURCE
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sched.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/mount.h>
+
+#include "zdtmtst.h"
+
+#ifndef CLONE_NEWNS
+#define CLONE_NEWNS 0x00020000
+#endif
+
+const char *test_doc = "Check ghost and link-remap files in a few mntns";
+const char *test_author = "Andrew Vagin <avagin@parallels.com>";
+
+#define MPTS_FILE "F"
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+#define NS_STACK_SIZE 4096
+/* All arguments should be above stack, because it grows down */
+struct ns_exec_args {
+ char stack[NS_STACK_SIZE] __stack_aligned__;
+ char stack_ptr[0];
+ int fd;
+ int sync;
+};
+
+#define AWK_OK 13
+#define AWK_FAIL 42
+
+static int get_mntid(int fd)
+{
+ char str[256];
+ int mnt_id = -1;
+ FILE *f;
+
+ snprintf(str, sizeof(str), "/proc/self/fdinfo/%d", fd);
+ f = fopen(str, "r");
+ if (!f) {
+ pr_perror("Can't open %s to parse", str);
+ return -1;
+ }
+ while (fgets(str, sizeof(str), f)) {
+ if (sscanf(str, "mnt_id: %d", &mnt_id) == 1)
+ break;
+ }
+
+ fclose(f);
+ return mnt_id;
+}
+
+int ns_child(void *_arg)
+{
+ struct ns_exec_args *args = _arg;
+ int fd2;
+ int id1, id2;
+ struct stat st1, st2;
+ char lpath[PATH_MAX], fpath[PATH_MAX];
+
+ snprintf(fpath, sizeof(fpath), "%s/1", dirname);
+ if (umount(fpath)) {
+ pr_perror("umount");
+ return 1;
+ }
+
+ snprintf(lpath, sizeof(lpath), "%s/0/2", dirname);
+ snprintf(fpath, sizeof(fpath), "%s/2", dirname);
+
+ if (mkdir(fpath, 0600) < 0) {
+ fail("Can't make zdtm_sys");
+ return 1;
+ }
+
+ if (mount(lpath, fpath, NULL, MS_BIND, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+ snprintf(fpath, sizeof(fpath), "%s/0", dirname);
+ if (umount(fpath)) {
+ pr_perror("umount");
+ return 1;
+ }
+
+ snprintf(fpath, sizeof(fpath), "%s/2/%s", dirname, MPTS_FILE);
+ fd2 = open(fpath, O_RDWR);
+ if (fd2 < 0) {
+ pr_perror("open");
+ return -1;
+ }
+ close(args->sync);
+ test_waitsig();
+
+ id1 = get_mntid(args->fd);
+ id2 = get_mntid(fd2);
+ if (id1 <0 || id2 < 0)
+ exit(1);
+
+ if (fstat(args->fd, &st1) || fstat(fd2, &st2)) {
+ pr_perror("stat");
+ exit(1);
+ }
+
+ test_msg("%d %d", id1, id2);
+
+#ifdef ZDTM_LINK_REMAP
+ if (st1.st_nlink != 1) {
+#else
+ if (st1.st_nlink != 0) {
+#endif
+ pr_perror("Wrong number of links: %d", st1.st_nlink);
+ exit(1);
+ }
+
+ if (id1 > 0 && id1 != id2 && st1.st_ino == st2.st_ino)
+ exit(AWK_OK);
+ else
+ exit(AWK_FAIL);
+}
+
+int main(int argc, char **argv)
+{
+ struct ns_exec_args args;
+ pid_t pid = -1;
+ char lpath[PATH_MAX], fpath[PATH_MAX];
+ char buf[256];
+ int p[2];
+
+ test_init(argc, argv);
+
+ if (mkdir(dirname, 0600) < 0) {
+ fail("Can't make zdtm_sys");
+ return 1;
+ }
+
+ if (mount("test", dirname, "tmpfs", 0, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+ snprintf(fpath, sizeof(fpath), "%s/0", dirname);
+ if (mkdir(fpath, 0600) < 0) {
+ fail("Can't make zdtm_sys");
+ return 1;
+ }
+ if (mount("test", fpath, "tmpfs", 0, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+ snprintf(lpath, sizeof(lpath), "%s/0/1", dirname);
+ if (mkdir(lpath, 0600) < 0) {
+ fail("Can't make zdtm_sys");
+ return 1;
+ }
+ snprintf(fpath, sizeof(fpath), "%s/1", dirname);
+ if (mkdir(fpath, 0600) < 0) {
+ fail("Can't make zdtm_sys");
+ return 1;
+ }
+ if (mount(lpath, fpath, NULL, MS_BIND, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+ snprintf(lpath, sizeof(lpath), "%s/0/2", dirname);
+ if (mkdir(lpath, 0600) < 0) {
+ fail("Can't make zdtm_sys");
+ return 1;
+ }
+
+ if (pipe(p) == -1) {
+ pr_perror("pipe");
+ return 1;
+ }
+
+ if (getenv("ZDTM_NOSUBNS") == NULL) {
+ snprintf(fpath, sizeof(fpath), "%s/1/%s", dirname, MPTS_FILE);
+
+ args.fd = open(fpath, O_CREAT | O_RDWR, 0600);
+ if (args.fd < 0) {
+ fail("Can't open file");
+ return 1;
+ }
+ snprintf(fpath, sizeof(fpath), "%s/0/1/%s", dirname, MPTS_FILE);
+ snprintf(lpath, sizeof(fpath), "%s/0/2/%s", dirname, MPTS_FILE);
+ if (link(fpath, lpath) == -1) {
+ pr_perror("link");
+ return -1;
+ }
+#ifdef ZDTM_LINK_REMAP
+ snprintf(lpath, sizeof(fpath), "%s/0/%s", dirname, MPTS_FILE);
+ if (link(fpath, lpath) == -1) {
+ pr_perror("link");
+ return -1;
+ }
+#endif
+ args.sync = p[1];
+
+ pid = clone(ns_child, args.stack_ptr, CLONE_NEWNS | SIGCHLD, &args);
+ if (pid < 0) {
+ pr_perror("Unable to fork child");
+ return 1;
+ }
+
+ close(args.fd);
+ }
+
+ close(p[1]);
+ read(p[0], buf, sizeof(buf));
+
+ snprintf(fpath, sizeof(fpath), "%s/0/1/%s", dirname, MPTS_FILE);
+ if (unlink(fpath))
+ return 1;
+ snprintf(fpath, sizeof(fpath), "%s/0/2/%s", dirname, MPTS_FILE);
+ if (unlink(fpath))
+ return 1;
+
+ test_daemon();
+ test_waitsig();
+
+
+ if (pid > 0) {
+ kill(pid, SIGTERM);
+ int status = 1;
+ wait(&status);
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == AWK_OK)
+ pass();
+ else if (WEXITSTATUS(status) == AWK_FAIL)
+ fail("Mount ID not restored");
+ else
+ fail("Failed to check mount IDs (%d)", WEXITSTATUS(status));
+ } else
+ fail("Test died");
+ }
+
+ umount2(dirname, MNT_DETACH);
+ rmdir(dirname);
+ return 0;
+}
diff --git a/test/zdtm/static/mntns_link_remap.desc b/test/zdtm/static/mntns_link_remap.desc
new file mode 100644
index 000000000..a84aba32c
--- /dev/null
+++ b/test/zdtm/static/mntns_link_remap.desc
@@ -0,0 +1 @@
+{'flavor': 'ns', 'flags': 'suid', 'feature': 'mnt_id', 'opts': '--link-remap'}
diff --git a/test/zdtm/static/mntns_link_remap.opts b/test/zdtm/static/mntns_link_remap.opts
new file mode 100644
index 000000000..472294671
--- /dev/null
+++ b/test/zdtm/static/mntns_link_remap.opts
@@ -0,0 +1 @@
+--link-remap
diff --git a/test/zdtm/static/mntns_open.c b/test/zdtm/static/mntns_open.c
new file mode 100644
index 000000000..629d76275
--- /dev/null
+++ b/test/zdtm/static/mntns_open.c
@@ -0,0 +1,140 @@
+#define _GNU_SOURCE
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sched.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "zdtmtst.h"
+
+#ifndef CLONE_NEWNS
+#define CLONE_NEWNS 0x00020000
+#endif
+
+const char *test_doc = "Check that mnt_id is repsected";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+#define MPTS_FILE "F"
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+char fpath[PATH_MAX];
+
+#define NS_STACK_SIZE 4096
+/* All arguments should be above stack, because it grows down */
+struct ns_exec_args {
+ char stack[NS_STACK_SIZE] __stack_aligned__;
+ char stack_ptr[0];
+ int fd;
+};
+
+#define AWK_OK 13
+#define AWK_FAIL 42
+
+static int get_mntid(int fd)
+{
+ char str[256];
+ int mnt_id = -1;
+ FILE *f;
+
+ snprintf(str, sizeof(str), "/proc/self/fdinfo/%d", fd);
+ f = fopen(str, "r");
+ if (!f) {
+ pr_perror("Can't open %s to parse", str);
+ return -1;
+ }
+ while (fgets(str, sizeof(str), f)) {
+ if (sscanf(str, "mnt_id: %d", &mnt_id) == 1)
+ break;
+ }
+
+ fclose(f);
+ return mnt_id;
+}
+
+task_waiter_t t;
+
+int ns_child(void *_arg)
+{
+ struct ns_exec_args *args = _arg;
+ int fd2;
+ int id1, id2;
+
+ fd2 = open(fpath, O_RDWR);
+ task_waiter_complete(&t, 1);
+ test_waitsig();
+
+ id1 = get_mntid(args->fd);
+ id2 = get_mntid(fd2);
+
+ test_msg("%d %d", id1, id2);
+
+ if (id1 <0 || id2 < 0)
+ exit(1);
+ if (id1 > 0 && id1 != id2)
+ exit(AWK_OK);
+ else
+ exit(AWK_FAIL);
+}
+
+int main(int argc, char **argv)
+{
+ struct ns_exec_args args;
+ pid_t pid = -1;
+
+ test_init(argc, argv);
+
+ task_waiter_init(&t);
+
+ snprintf(fpath, sizeof(fpath), "%s/%s", dirname, MPTS_FILE);
+ if (mkdir(dirname, 0600) < 0) {
+ fail("Can't make zdtm_sys");
+ return 1;
+ }
+
+ if (getenv("ZDTM_NOSUBNS") == NULL) {
+ args.fd = open(fpath, O_CREAT | O_RDWR, 0600);
+ if (args.fd < 0) {
+ fail("Can't open file");
+ return 1;
+ }
+
+ pid = clone(ns_child, args.stack_ptr, CLONE_NEWNS | SIGCHLD, &args);
+ if (pid < 0) {
+ pr_perror("Unable to fork child");
+ return 1;
+ }
+
+ close(args.fd);
+ }
+
+ task_waiter_wait4(&t, 1);
+
+ test_daemon();
+ test_waitsig();
+
+ if (pid > 0) {
+ kill(pid, SIGTERM);
+ int status = 1;
+ wait(&status);
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == AWK_OK)
+ pass();
+ else if (WEXITSTATUS(status) == AWK_FAIL)
+ fail("Mount ID not restored");
+ else
+ fail("Failed to check mount IDs (%d)", WEXITSTATUS(status));
+ } else
+ fail("Test died");
+ }
+
+ unlink(fpath);
+ rmdir(dirname);
+ return 0;
+}
diff --git a/test/zdtm/static/mntns_open.desc b/test/zdtm/static/mntns_open.desc
new file mode 100644
index 000000000..a8849e097
--- /dev/null
+++ b/test/zdtm/static/mntns_open.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid', 'feature': 'mnt_id'}
diff --git a/test/zdtm/static/mntns_root_bind.c b/test/zdtm/static/mntns_root_bind.c
new file mode 100644
index 000000000..3bd0bece0
--- /dev/null
+++ b/test/zdtm/static/mntns_root_bind.c
@@ -0,0 +1,123 @@
+#define _GNU_SOURCE
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sched.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "zdtmtst.h"
+
+#ifndef CLONE_NEWNS
+#define CLONE_NEWNS 0x00020000
+#endif
+
+const char *test_doc = "Check bind-mouns of the root mount";
+const char *test_author = "Andrew Vagin <avagin@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+
+int main(int argc, char **argv)
+{
+ char subdir1[PATH_MAX], path[PATH_MAX], bpath[PATH_MAX], spath[PATH_MAX], bspath[PATH_MAX];
+ char subdir2[PATH_MAX], bsubdir2[PATH_MAX];
+ pid_t pid;
+ int status;
+ task_waiter_t t;
+
+ test_init(argc, argv);
+
+ task_waiter_init(&t);
+
+ mount(NULL, "/", NULL, MS_SHARED, NULL);
+
+ snprintf(subdir1, sizeof(subdir1), "%s/subdir1", dirname);
+ snprintf(path, sizeof(path), "%s/test", subdir1);
+ snprintf(bpath, sizeof(bpath), "%s/test.bind", subdir1);
+ snprintf(spath, sizeof(spath), "%s/test/sub", subdir1);
+ snprintf(bspath, sizeof(bspath), "%s/test.bind/sub", subdir1);
+
+ snprintf(subdir2, sizeof(subdir2), "%s/subdir2", dirname);
+ snprintf(bsubdir2, sizeof(bsubdir2), "%s/bsubdir2", dirname);
+
+ if (mkdir(dirname, 0700) ||
+ mkdir(subdir1, 0777) ||
+ mkdir(subdir2, 0777) ||
+ mkdir(bsubdir2, 0777) ||
+ mkdir(path, 0700) ||
+ mkdir(spath, 0700) ||
+ mkdir(bpath, 0700)) {
+ pr_perror("mkdir");
+ return 1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork");
+ return 1;
+ }
+ if (pid == 0) {
+ unshare(CLONE_NEWNS);
+ if (mount(path, bpath, NULL, MS_BIND, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+ task_waiter_complete(&t, 1);
+ task_waiter_wait4(&t, 2);
+
+ if (access(bspath, F_OK)) {
+ fail("%s isn't accessiable", bspath);
+ return 1;
+ }
+
+
+ if (umount2(bpath, MNT_DETACH)) {
+ fail("umount");
+ return 1;
+ }
+
+ return 0;
+ }
+
+ task_waiter_wait4(&t, 1);
+
+ if (mount("test", spath, "tmpfs", 0, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+#ifdef ROOT_BIND02
+ if (mount(subdir2, bsubdir2, NULL, MS_BIND, NULL)) {
+ pr_perror("Unable to mount %s to %s", subdir2, bsubdir2);
+ return 1;
+ }
+#endif
+
+ test_daemon();
+ test_waitsig();
+
+ task_waiter_complete(&t, 2);
+
+ if (waitpid(pid, &status, 0) != pid) {
+ pr_perror("waitpid %d", pid);
+ return 1;
+ }
+
+ if (status) {
+ pr_perror("%d/%d/%d/%d", WIFEXITED(status), WEXITSTATUS(status), WIFSIGNALED(status), WTERMSIG(status));
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/mntns_root_bind.desc b/test/zdtm/static/mntns_root_bind.desc
new file mode 100644
index 000000000..a8849e097
--- /dev/null
+++ b/test/zdtm/static/mntns_root_bind.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid', 'feature': 'mnt_id'}
diff --git a/test/zdtm/static/mntns_root_bind02.c b/test/zdtm/static/mntns_root_bind02.c
new file mode 120000
index 000000000..4957c1fa1
--- /dev/null
+++ b/test/zdtm/static/mntns_root_bind02.c
@@ -0,0 +1 @@
+mntns_root_bind.c \ No newline at end of file
diff --git a/test/zdtm/static/mntns_root_bind02.desc b/test/zdtm/static/mntns_root_bind02.desc
new file mode 120000
index 000000000..cd4ed51a4
--- /dev/null
+++ b/test/zdtm/static/mntns_root_bind02.desc
@@ -0,0 +1 @@
+mntns_root_bind.desc \ No newline at end of file
diff --git a/test/zdtm/static/mntns_rw_ro_rw.c b/test/zdtm/static/mntns_rw_ro_rw.c
new file mode 100644
index 000000000..7aed254b6
--- /dev/null
+++ b/test/zdtm/static/mntns_rw_ro_rw.c
@@ -0,0 +1,46 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test read-only bind mounts";
+const char *test_author = "Andrey Vagin <xemul@parallels.com>";
+
+int main(int argc, char **argv)
+{
+ test_init(argc, argv);
+
+ if (mount("/proc/sys/", "/proc/sys", NULL, MS_BIND, NULL)) {
+ pr_perror("Unable to bind-mount /proc/sys");
+ return 1;
+ }
+ if (mount("/proc/sys/net", "/proc/sys/net", NULL, MS_BIND, NULL)) {
+ pr_perror("Unable to bind-mount /proc/sys/net");
+ return 1;
+ }
+ if (mount("/proc/sys/", "/proc/sys", NULL, MS_RDONLY|MS_BIND|MS_REMOUNT, NULL)) {
+ pr_perror("Unable to remount /proc/sys");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (access("/proc/sys/net/ipv4/ip_forward", W_OK)) {
+ fail("Unable to access /proc/sys/net/core/wmem_max");
+ return 1;
+ }
+
+ if (access("/proc/sys/kernel/ns_last_pid", W_OK) != -1 || errno != EROFS) {
+ fail("Unable to access /proc/sys/kernel/pid_max");
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/mntns_rw_ro_rw.desc b/test/zdtm/static/mntns_rw_ro_rw.desc
new file mode 100644
index 000000000..7657ba45c
--- /dev/null
+++ b/test/zdtm/static/mntns_rw_ro_rw.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid'}
diff --git a/test/zdtm/static/mntns_shared_bind.c b/test/zdtm/static/mntns_shared_bind.c
new file mode 100644
index 000000000..f11b2df98
--- /dev/null
+++ b/test/zdtm/static/mntns_shared_bind.c
@@ -0,0 +1,128 @@
+#define _GNU_SOURCE
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sched.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "zdtmtst.h"
+
+#ifndef CLONE_NEWNS
+#define CLONE_NEWNS 0x00020000
+#endif
+
+const char *test_doc = "Check shared non-root bind-mounts";
+const char *test_author = "Andrew Vagin <avagin@gmail.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+
+int main(int argc, char **argv)
+{
+ char path[PATH_MAX], bpath[PATH_MAX], spath[PATH_MAX];
+ pid_t pid;
+ int status;
+ task_waiter_t t;
+
+ test_init(argc, argv);
+
+ task_waiter_init(&t);
+
+ snprintf(path, sizeof(path), "%s/test", dirname);
+ snprintf(bpath, sizeof(bpath), "%s/test.bind", dirname);
+ snprintf(spath, sizeof(spath), "%s/test/sub", dirname);
+ if (mkdir(dirname, 0700)) {
+ pr_perror("mkdir");
+ return 1;
+ }
+
+ if (mount(NULL, "/", NULL, MS_SHARED, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+#ifdef SHARED_BIND02
+ /* */
+ if (mount(dirname, dirname, "tmpfs", 0, NULL) ||
+ mount(NULL, dirname, NULL, MS_SHARED, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+#endif
+
+ if (mkdir(path, 0700) ||
+ mkdir(spath, 0700) ||
+ mkdir(bpath, 0700)) {
+ pr_perror("mkdir");
+ return 1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork");
+ return 1;
+ }
+ if (pid == 0) {
+ unshare(CLONE_NEWNS);
+ if (mount(path, bpath, NULL, MS_BIND, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+ task_waiter_complete(&t, 1);
+ task_waiter_wait4(&t, 2);
+ if (umount(spath)) {
+ task_waiter_complete(&t, 2);
+ fail("umount");
+ return 1;
+ }
+ task_waiter_complete(&t, 3);
+ task_waiter_wait4(&t, 4);
+
+ return 0;
+ }
+
+ task_waiter_wait4(&t, 1);
+
+ if (mount("test", spath, "tmpfs", 0, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+
+ test_daemon();
+ test_waitsig();
+
+ task_waiter_complete(&t, 2);
+ task_waiter_wait4(&t, 3);
+
+ if (umount(bpath)) {
+ task_waiter_complete(&t, 2);
+ fail("umount");
+ return 1;
+ }
+
+ task_waiter_complete(&t, 4);
+
+ if (waitpid(pid, &status, 0) != pid) {
+ pr_perror("waitpid %d", pid);
+ return 1;
+ }
+
+ if (status) {
+ pr_perror("%d/%d/%d/%d", WIFEXITED(status), WEXITSTATUS(status), WIFSIGNALED(status), WTERMSIG(status));
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/mntns_shared_bind.desc b/test/zdtm/static/mntns_shared_bind.desc
new file mode 100644
index 000000000..a8849e097
--- /dev/null
+++ b/test/zdtm/static/mntns_shared_bind.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid', 'feature': 'mnt_id'}
diff --git a/test/zdtm/static/mntns_shared_bind02.c b/test/zdtm/static/mntns_shared_bind02.c
new file mode 120000
index 000000000..5efca6707
--- /dev/null
+++ b/test/zdtm/static/mntns_shared_bind02.c
@@ -0,0 +1 @@
+mntns_shared_bind.c \ No newline at end of file
diff --git a/test/zdtm/static/mntns_shared_bind02.desc b/test/zdtm/static/mntns_shared_bind02.desc
new file mode 100644
index 000000000..a8849e097
--- /dev/null
+++ b/test/zdtm/static/mntns_shared_bind02.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid', 'feature': 'mnt_id'}
diff --git a/test/zdtm/static/mount_paths.c b/test/zdtm/static/mount_paths.c
new file mode 100644
index 000000000..a42b0eb3d
--- /dev/null
+++ b/test/zdtm/static/mount_paths.c
@@ -0,0 +1,57 @@
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that special charecters in paths are handled correctly";
+const char *test_author = "Andrew Vagin <avagin@virtuozzo.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+#define TEST_DIR "tmpfs \t \t\\\\\t test \t\t\\\\ \t\\"
+
+int main(int argc, char **argv)
+{
+ int ret = 1;
+ char buf[1024], test_dir[PATH_MAX], fname[PATH_MAX];
+
+ test_init(argc, argv);
+
+ mkdir(dirname, 0700);
+
+ snprintf(test_dir, sizeof(test_dir), "%s/%s", dirname, TEST_DIR);
+ mkdir(test_dir, 0700);
+
+ if (mount("", test_dir, "tmpfs", 0, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+ snprintf(fname, sizeof(buf), "%s/\\\t \\\\ \\tt", test_dir);
+ if (mkdir(fname, 0700)) {
+ pr_perror("mkdir");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (access(fname, F_OK)) {
+ fail();
+ goto err;
+ }
+
+ pass();
+ ret = 0;
+err:
+ umount2(test_dir, MNT_DETACH);
+ rmdir(test_dir);
+ rmdir(dirname);
+ return ret;
+}
diff --git a/test/zdtm/static/mount_paths.desc b/test/zdtm/static/mount_paths.desc
new file mode 100644
index 000000000..7657ba45c
--- /dev/null
+++ b/test/zdtm/static/mount_paths.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid'}
diff --git a/test/zdtm/static/mountpoints.c b/test/zdtm/static/mountpoints.c
new file mode 100644
index 000000000..0b46480e0
--- /dev/null
+++ b/test/zdtm/static/mountpoints.c
@@ -0,0 +1,357 @@
+#define _GNU_SOURCE
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sched.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that mountpoints (in mount namespace) are supported";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+#define MPTS_ROOT "/zdtm_mpts/"
+
+static char buf[1024];
+
+#define NS_STACK_SIZE 4096
+/* All arguments should be above stack, because it grows down */
+struct ns_exec_args {
+ char stack[NS_STACK_SIZE] __stack_aligned__;
+ char stack_ptr[0];
+ int status_pipe[2];
+};
+
+task_waiter_t t;
+
+int ns_child(void *_arg)
+{
+ struct stat st;
+ pid_t pid;
+ int fd, ufd;
+
+ mkdir(MPTS_ROOT"/dev/mntns2", 0600);
+ if (mount("none", MPTS_ROOT"/dev/mntns2", "tmpfs", 0, "") < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ mkdir(MPTS_ROOT"/dev/mntns2/test", 0600);
+
+ fd = open(MPTS_ROOT"/dev/mntns2/test/test.file", O_WRONLY | O_CREAT, 0666);
+ if (fd < 0)
+ return 1;
+
+ ufd = open(MPTS_ROOT"/dev/mntns2/test/test.file.unlinked", O_WRONLY | O_CREAT, 0666);
+ if (ufd < 0)
+ return 1;
+ unlink(MPTS_ROOT"/dev/mntns2/test/test.file.unlinked");
+
+ pid = fork();
+
+ task_waiter_complete(&t, 1);
+
+ test_waitsig();
+
+ if (pid) {
+ int status = 1;;
+ kill(pid, SIGTERM);
+ wait(&status);
+ if (status)
+ return 1;
+ }
+
+ if (stat(MPTS_ROOT"/dev/mntns2/test", &st)) {
+ pr_perror("Can't stat /dev/share-1/test.share/test.share");
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ FILE *f;
+ int fd, tmpfs_fd, have_bfmtm = 0;
+ unsigned fs_cnt, fs_cnt_last = 0;
+ struct ns_exec_args args;
+ pid_t pid = -1;
+
+ test_init(argc, argv);
+
+ task_waiter_init(&t);
+
+again:
+ fs_cnt = 0;
+ f = fopen("/proc/self/mountinfo", "r");
+ if (!f) {
+ fail("Can't open mountinfo");
+ return -1;
+ }
+
+ if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL)) {
+ pr_perror("Can't remount / with MS_PRIVATE");
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ char *mp = buf, *end;
+
+ mp = strchr(mp, ' ') + 1;
+ mp = strchr(mp, ' ') + 1;
+ mp = strchr(mp, ' ') + 1;
+ mp = strchr(mp, ' ') + 1;
+ end = strchr(mp, ' ');
+ *end = '\0';
+
+ if (!strcmp(mp, "/"))
+ continue;
+ if (!strcmp(mp, "/proc"))
+ continue;
+
+ if (umount(mp))
+ test_msg("umount(`%s') failed: %m\n", mp);
+
+ fs_cnt++;
+ }
+
+ fclose(f);
+
+ if (fs_cnt == 0)
+ goto done;
+
+ if (fs_cnt != fs_cnt_last) {
+ fs_cnt_last = fs_cnt;
+ goto again;
+ }
+
+ fail("Can't umount all the filesystems");
+ return -1;
+
+done:
+ rmdir(MPTS_ROOT);
+ if (mkdir(MPTS_ROOT, 0600) < 0) {
+ fail("Can't make zdtm_sys");
+ return 1;
+ }
+
+ if (mount("none", MPTS_ROOT, "sysfs", 0, "") < 0) {
+ fail("Can't mount sysfs");
+ return 1;
+ }
+
+ if (mount("none", MPTS_ROOT"/dev", "tmpfs", 0, "") < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+ tmpfs_fd = open(MPTS_ROOT"/dev/test", O_WRONLY | O_CREAT);
+ if (write(tmpfs_fd, "hello", 5) <= 0) {
+ pr_perror("write() failed");
+ return 1;
+ }
+
+ /* Check that over-mounted files are restored on tmpfs */
+ mkdir(MPTS_ROOT"/dev/overmount", 0600);
+ fd = open(MPTS_ROOT"/dev/overmount/test.over", O_WRONLY | O_CREAT);
+ if (fd == -1) {
+ pr_perror("Unable to open "MPTS_ROOT"/dev/overmount");
+ return -1;
+ }
+ close(fd);
+ if (mount("none", MPTS_ROOT"/dev/overmount", "tmpfs", 0, "") < 0) {
+ pr_perror("Can't mount "MPTS_ROOT"/dev/overmount");
+ return 1;
+ }
+
+ mkdir(MPTS_ROOT"/dev/non-root", 0600);
+ if (mount(MPTS_ROOT"/dev/non-root", MPTS_ROOT"/module", NULL, MS_BIND, NULL) < 0) {
+ pr_perror("Can't bind-mount %s -> %s", MPTS_ROOT"/dev/tdir", MPTS_ROOT"/module");
+ }
+ mkdir(MPTS_ROOT"/dev/non-root/test", 0600);
+
+ mkdir(MPTS_ROOT"/dev/share-1", 0600);
+ if (mount("none", MPTS_ROOT"/dev/share-1/", "tmpfs", 0, "") < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+ if (mount("none", MPTS_ROOT"/dev/share-1/", NULL, MS_SHARED, NULL) < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+//#define CR_NEXT
+#ifdef CR_NEXT
+ mkdir(MPTS_ROOT"/dev/share-1/alone", 0600);
+ if (mount("none", MPTS_ROOT"/dev/share-1/alone", "tmpfs", 0, "") < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+#endif
+
+ mkdir(MPTS_ROOT"/dev/share-2", 0600);
+ if (mount(MPTS_ROOT"/dev/share-1", MPTS_ROOT"/dev/share-2", NULL, MS_BIND, NULL) < 0) {
+ fail("Can't bind mount a tmpfs directory");
+ return 1;
+ }
+
+ mkdir(MPTS_ROOT"/dev/share-3", 0600);
+ if (mount(MPTS_ROOT"/dev/share-1", MPTS_ROOT"/dev/share-3", NULL, MS_BIND, NULL) < 0) {
+ fail("Can't bind mount a tmpfs directory");
+ return 1;
+ }
+ mkdir(MPTS_ROOT"/dev/slave", 0600);
+ if (mount(MPTS_ROOT"/dev/share-1", MPTS_ROOT"/dev/slave", NULL, MS_BIND, NULL) < 0) {
+ fail("Can't bind mount a tmpfs directory");
+ return 1;
+ }
+ if (mount("none", MPTS_ROOT"/dev/slave", NULL, MS_SLAVE, NULL) < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ mkdir(MPTS_ROOT"/dev/slave2", 0600);
+ if (mount(MPTS_ROOT"/dev/share-3", MPTS_ROOT"/dev/slave2", NULL, MS_BIND, NULL) < 0) {
+ fail("Can't bind mount a tmpfs directory");
+ return 1;
+ }
+ if (mount("none", MPTS_ROOT"/dev/slave2", NULL, MS_SLAVE, NULL) < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ mkdir(MPTS_ROOT"/dev/share-1/test.mnt.share", 0600);
+ if (mount("none", MPTS_ROOT"/dev/share-1/test.mnt.share", "tmpfs", 0, "size=1G") < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ mkdir(MPTS_ROOT"/dev/share-1/test.mnt.share/test.share", 0600);
+ if (umount(MPTS_ROOT"/dev/slave2/test.mnt.share")) {
+ pr_perror("Can't umount "MPTS_ROOT"/dev/slave2/test.mnt.share");
+ return 1;
+ }
+
+ mkdir(MPTS_ROOT"/dev/slave/test.mnt.slave", 0600);
+ if (mount("none", MPTS_ROOT"/dev/slave/test.mnt.slave", "tmpfs", 0, "") < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+ mkdir(MPTS_ROOT"/dev/slave/test.mnt.slave/test.slave", 0600);
+
+ fd = open(MPTS_ROOT"/dev/bmfile", O_CREAT | O_WRONLY);
+ if (fd < 0) {
+ pr_perror("Can't create " MPTS_ROOT "/dev/share-1/bmfile");
+ return 1;
+ }
+ close(fd);
+
+ fd = open(MPTS_ROOT"/dev/bmfile-mount", O_CREAT | O_WRONLY);
+ if (fd < 0) {
+ pr_perror("Can't create " MPTS_ROOT "/dev/share-1/bmfile");
+ return 1;
+ }
+ close(fd);
+
+ if (mount(MPTS_ROOT"/dev/bmfile", MPTS_ROOT"/dev/bmfile-mount", NULL, MS_BIND, NULL) < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ if (mount("none", MPTS_ROOT"/kernel", "proc", 0, "") < 0) {
+ fail("Can't mount proc");
+ return 1;
+ }
+
+ if (mount("none", MPTS_ROOT"/kernel/sys/fs/binfmt_misc",
+ "binfmt_misc", 0, "") == 0)
+ have_bfmtm = 1;
+
+ fd = open(MPTS_ROOT"/kernel/meminfo", O_RDONLY);
+ if (fd == -1)
+ return 1;
+
+ if (getenv("ZDTM_NOSUBNS") == NULL) {
+ pid = clone(ns_child, args.stack_ptr, CLONE_NEWNS | SIGCHLD, &args);
+ if (pid < 0) {
+ pr_perror("Unable to fork child");
+ return 1;
+ }
+ }
+
+ task_waiter_wait4(&t, 1);
+
+ test_daemon();
+ test_waitsig();
+
+ /* this checks both -- sys and proc presence */
+ if (access(MPTS_ROOT"/kernel/meminfo", F_OK)) {
+ fail("No proc after restore");
+ return 1;
+ }
+
+ if (have_bfmtm && access(MPTS_ROOT"/kernel/sys/fs/binfmt_misc/register", F_OK)) {
+ fail("No binfmt_misc after restore");
+ return 1;
+ }
+
+ if (umount(MPTS_ROOT"/dev/overmount") == -1) {
+ pr_perror("Can't umount "MPTS_ROOT"/dev/overmount");
+ return -1;
+ }
+ if (access(MPTS_ROOT"/dev/overmount/test.over", F_OK)) {
+ fail(MPTS_ROOT"/dev/overmount/test.over");
+ return -1;
+ }
+
+ {
+ struct stat st1, st2;
+ if (stat(MPTS_ROOT"/dev/share-1/test.mnt.share/test.share", &st1)) {
+ pr_perror("Can't stat /dev/share-1/test.share/test.share");
+ return 1;
+ }
+ if (stat(MPTS_ROOT"/dev/share-2/test.mnt.share/test.share", &st2)) {
+ pr_perror("Can't stat /dev/share-2/test.mnt.share/test.share");
+ return 1;
+ }
+ if (st1.st_ino != st2.st_ino) {
+ fail("/dev/share-1 and /dev/share-1 is not shared");
+ return 1;
+ }
+ if (stat(MPTS_ROOT"/dev/slave/test.mnt.share/test.share", &st2)) {
+ pr_perror("Can't stat /dev/slave/test.mnt.share/test.share");
+ return 1;
+ }
+ if (st1.st_ino != st2.st_ino) {
+ fail("/dev/slave is not slave of /dev/share-1");
+ return 1;
+ }
+ if (stat(MPTS_ROOT"/dev/share-1/test.mnt.slave/test.slave", &st1) != -1 || errno != ENOENT) {
+ pr_perror("/dev/share-1/test.mnt.slave/test.slave exists");
+ return 1;
+ }
+ if (stat(MPTS_ROOT"/dev/slave/test.mnt.slave/test.slave", &st2)) {
+ pr_perror("Can't stat /dev/slave/test.mnt.slave/test.slave");
+ return 1;
+ }
+ if (stat(MPTS_ROOT"/dev/non-root/test", &st1)) {
+ pr_perror("Can't stat /dev/non-root/test");
+ return 1;
+ }
+ }
+
+ if (pid > 0) {
+ kill(pid, SIGTERM);
+ int status = 1;
+ wait(&status);
+ if (status)
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/mountpoints.desc b/test/zdtm/static/mountpoints.desc
new file mode 100644
index 000000000..aba60c9e5
--- /dev/null
+++ b/test/zdtm/static/mountpoints.desc
@@ -0,0 +1 @@
+{'flavor': 'ns', 'flags': 'suid excl', 'feature': 'mnt_id'}
diff --git a/test/zdtm/static/mprotect00.c b/test/zdtm/static/mprotect00.c
new file mode 100644
index 000000000..be70135d6
--- /dev/null
+++ b/test/zdtm/static/mprotect00.c
@@ -0,0 +1,116 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <setjmp.h>
+#include <limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that memory protection migrates correctly\n";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+const static int prots[] = {
+ PROT_NONE,
+ PROT_READ,
+ /* PROT_WRITE, */ /* doesn't work w/o READ */
+ PROT_READ | PROT_WRITE,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+};
+#define NUM_MPROTS sizeof(prots) / sizeof(int)
+
+static sigjmp_buf segv_ret; /* we need sig*jmp stuff, otherwise SIGSEGV will reset our handler */
+static void segfault(int signo)
+{
+ siglongjmp(segv_ret, 1);
+}
+
+static int check_prot(char *ptr, int prot)
+{
+ if (signal(SIGSEGV, segfault) == SIG_ERR) {
+ fail("setting SIGSEGV handler failed: %m\n");
+ return -1;
+ }
+
+ if (!sigsetjmp(segv_ret, 1)) {
+ if (ptr[10] != 0) {
+ fail("read value doesn't match what I wrote");
+ return -1;
+ }
+ if (!(prot & PROT_READ)) {
+ fail("PROT_READ bypassed\n");
+ return -1;
+ }
+ }
+ else /* we come here on return from SIGSEGV handler */
+ if (prot & PROT_READ) {
+ fail("PROT_READ rejected\n");
+ return -1;
+ }
+
+ if (!sigsetjmp(segv_ret, 1)) {
+ ptr[20] = 67;
+ if (!(prot & PROT_WRITE)) {
+ fail("PROT_WRITE bypassed\n");
+ return -1;
+ }
+ }
+ else /* we come here on return from SIGSEGV handler */
+ if (prot & PROT_WRITE) {
+ fail("PROT_WRITE rejected\n");
+ return -1;
+ }
+
+
+ if (signal(SIGSEGV, SIG_DFL) == SIG_ERR) {
+ fail("restoring SIGSEGV handler failed: %m\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char ** argv)
+{
+ char *ptr, *ptr_aligned;
+ int pagesize;
+ int i;
+
+ test_init(argc, argv);
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize < 0) {
+ pr_perror("can't get PAGE_SIZE");
+ exit(1);
+ }
+
+ ptr = mmap(NULL, pagesize * (NUM_MPROTS + 1), PROT_NONE, MAP_PRIVATE | MAP_ANON, 0, 0);
+ if (ptr == MAP_FAILED) {
+ pr_perror("calloc failed");
+ return -1;
+ }
+
+ ptr_aligned = (char *)(((unsigned long) ptr + pagesize - 1) &
+ ~(pagesize - 1));
+
+ for (i = 0; i < NUM_MPROTS; i++)
+ if (mprotect(ptr_aligned + pagesize * i,
+ pagesize / 2, prots[i]) < 0) {
+ pr_perror("mprotect failed");
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ for (i = 0; i < NUM_MPROTS; i++)
+ if (check_prot(ptr_aligned + pagesize * i, prots[i]))
+ goto out;
+
+ pass();
+out:
+ return 0;
+}
diff --git a/test/zdtm/static/msgque.c b/test/zdtm/static/msgque.c
new file mode 100644
index 000000000..a855bec36
--- /dev/null
+++ b/test/zdtm/static/msgque.c
@@ -0,0 +1,138 @@
+#define _GNU_SOURCE
+#include <sched.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/sem.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc="Tests sysv5 msg queues supporting by checkpointing";
+const char *test_author="Stanislav Kinsbursky <skinsbursky@openvz.org>";
+
+struct msg1 {
+ long mtype;
+ char mtext[30];
+};
+#define TEST_STRING "Test sysv5 msg"
+#define MSG_TYPE 1
+
+#define ANOTHER_TEST_STRING "Yet another test sysv5 msg"
+#define ANOTHER_MSG_TYPE 26538
+
+int main(int argc, char **argv)
+{
+ key_t key;
+ int msg, pid;
+ struct msg1 msgbuf;
+ int chret;
+
+ test_init(argc, argv);
+
+ key = ftok(argv[0], 822155650);
+ if (key == -1) {
+ pr_perror("Can't make key");
+ exit(1);
+ }
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("Can't fork");
+ exit(1);
+ }
+
+ msg = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
+ if (msg == -1) {
+ msg = msgget(key, 0666);
+ if (msg == -1) {
+ pr_perror("Can't get queue");
+ goto err_kill;
+ }
+ }
+
+ if (pid == 0) {
+ test_waitsig();
+
+ if (msgrcv(msg, &msgbuf, sizeof(TEST_STRING), MSG_TYPE, IPC_NOWAIT) == -1) {
+ fail("Child: msgrcv failed (%m)");
+ return -errno;
+ }
+
+ if (strncmp(TEST_STRING, msgbuf.mtext, sizeof(TEST_STRING))) {
+ fail("Child: the source and received strings aren't equal");
+ return -errno;
+ }
+ test_msg("Child: received %s\n", msgbuf.mtext);
+
+ msgbuf.mtype = ANOTHER_MSG_TYPE;
+ memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING));
+ if (msgsnd(msg, &msgbuf, sizeof(ANOTHER_TEST_STRING), IPC_NOWAIT) != 0) {
+ fail("Child: msgsnd failed (%m)");
+ return -errno;
+ };
+ pass();
+ return 0;
+ } else {
+ msgbuf.mtype = MSG_TYPE;
+ memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING));
+ if (msgsnd(msg, &msgbuf, sizeof(TEST_STRING), IPC_NOWAIT) != 0) {
+ fail("Parent: msgsnd failed (%m)");
+ goto err_kill;
+ };
+
+ msgbuf.mtype = ANOTHER_MSG_TYPE;
+ memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING));
+ if (msgsnd(msg, &msgbuf, sizeof(ANOTHER_TEST_STRING), IPC_NOWAIT) != 0) {
+ fail("child: msgsnd (2) failed (%m)");
+ return -errno;
+ };
+
+ test_daemon();
+ test_waitsig();
+
+ kill(pid, SIGTERM);
+
+ wait(&chret);
+ chret = WEXITSTATUS(chret);
+ if (chret) {
+ fail("Parent: child exited with non-zero code %d (%s)\n",
+ chret, strerror(chret));
+ goto out;
+ }
+
+ if (msgrcv(msg, &msgbuf, sizeof(ANOTHER_TEST_STRING), ANOTHER_MSG_TYPE, IPC_NOWAIT) == -1) {
+ fail("Parent: msgrcv failed (%m)");
+ goto err;
+ }
+
+ if (strncmp(ANOTHER_TEST_STRING, msgbuf.mtext, sizeof(ANOTHER_TEST_STRING))) {
+ fail("Parent: the source and received strings aren't equal");
+ goto err;
+ }
+ test_msg("Parent: received %s\n", msgbuf.mtext);
+
+ pass();
+ }
+
+out:
+ if (msgctl(msg, IPC_RMID, 0)) {
+ fail("Failed to destroy message queue: %d\n", -errno);
+ return -errno;
+ }
+ return chret;
+
+err_kill:
+ kill(pid, SIGKILL);
+ wait(NULL);
+err:
+ chret = -errno;
+ goto out;
+}
diff --git a/test/zdtm/static/msgque.desc b/test/zdtm/static/msgque.desc
new file mode 100644
index 000000000..6c4afe5f0
--- /dev/null
+++ b/test/zdtm/static/msgque.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns'}
diff --git a/test/zdtm/static/mtime_mmap.c b/test/zdtm/static/mtime_mmap.c
new file mode 100644
index 000000000..6c97487ea
--- /dev/null
+++ b/test/zdtm/static/mtime_mmap.c
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "file mmaped for write and being written should change mtime\n"
+ "and be migrated with correct new data";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+#define FILE_SIZE (16 * 1024)
+
+int main(int argc, char **argv)
+{
+ int fd;
+ char buf[FILE_SIZE];
+ size_t count;
+ int i;
+ char *ptr;
+ struct stat fst;
+ time_t mtime_old, mtime_new;
+ time_t ctime_old, ctime_new;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_RDWR | O_CREAT, 0666);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ /* initialization */
+ count = sizeof(buf);
+ memset(buf, 1, count);
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ pr_perror("failed to write %s", filename);
+ exit(1);
+ }
+
+ if (fstat(fd, &fst) < 0) {
+ pr_perror("can't get %s file info", filename);
+ goto failed;
+ }
+
+ ptr = (char *)mmap(NULL, count, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ pr_perror("mmap() Failed, errno=%d : %s", errno, strerror(errno));
+ goto failed;
+ }
+
+ mtime_old = fst.st_mtime;
+ ctime_old = fst.st_ctime;
+ sleep(2);
+
+ for (i = 0; i < count; i++)
+ ptr[i]++;
+
+ if (munmap(ptr, count)) {
+ pr_perror("munmap Failed, errno=%d : %s", errno, strerror(errno));
+ goto failed;
+ }
+
+ if (fstat(fd, &fst) < 0) {
+ pr_perror("can't get %s file info", filename);
+ goto failed;
+ }
+
+
+ mtime_new = fst.st_mtime;
+ /* time of last modification */
+ if (mtime_new <= mtime_old) {
+ fail("mtime %d wasn't updated on mmapped %s file",
+ mtime_new, filename);
+ goto failed;
+ }
+
+ ctime_new = fst.st_ctime;
+ /* time of last status change */
+ if (ctime_new <= ctime_old) {
+ fail("time of last status change of %s file wasn't changed\n",
+ filename);
+ goto failed;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (fstat(fd, &fst) < 0) {
+ pr_perror("can't get %s file info", filename);
+ goto failed;
+ }
+
+ /* time of last modification */
+ if (fst.st_mtime != mtime_new) {
+ fail("After migration, mtime changed to %d",
+ fst.st_mtime);
+ goto failed;
+ }
+
+ pass();
+ unlink(filename);
+ close(fd);
+ return 0;
+failed:
+ return 1;
+}
diff --git a/test/zdtm/static/netns-dev.c b/test/zdtm/static/netns-dev.c
new file mode 100644
index 000000000..fa623aaee
--- /dev/null
+++ b/test/zdtm/static/netns-dev.c
@@ -0,0 +1,220 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+
+#include "zdtmtst.h"
+
+#define LO_CONF_DIR_PATH "/proc/sys/net/ipv4/conf/lo"
+#define DEF_CONF_DIR_PATH "/proc/sys/net/ipv4/conf/default"
+
+char *devconfs[] = {
+ "accept_local",
+ "accept_redirects",
+ "accept_source_route",
+ "arp_accept",
+ "arp_announce",
+ "arp_filter",
+ "arp_ignore",
+ "arp_notify",
+ "bootp_relay",
+ "disable_policy",
+ "disable_xfrm",
+ "force_igmp_version",
+ "forwarding",
+ "igmpv2_unsolicited_report_interval",
+ "igmpv3_unsolicited_report_interval",
+ "ignore_routes_with_linkdown",
+ "log_martians",
+ "mc_forwarding",
+ "medium_id",
+ "promote_secondaries",
+ "proxy_arp",
+ "proxy_arp_pvlan",
+ "route_localnet",
+ "rp_filter",
+ "secure_redirects",
+ "send_redirects",
+ "shared_media",
+ "src_valid_mark",
+ "tag",
+ NULL,
+};
+
+int rand_limit[] = {
+ 2, /* accept_local */
+ 2, /* accept_redirects */
+ 2, /* accept_source_route */
+ 2, /* arp_accept */
+ 3, /* arp_announce */
+ 2, /* arp_filter */
+ 9, /* arp_ignore */
+ 2, /* arp_notify */
+ 2, /* bootp_relay */
+ 2, /* disable_policy */
+ 2, /* disable_xfrm */
+ 0, /* force_igmp_version */
+ 2, /* forwarding */
+ 0, /* igmpv2_unsolicited_report_interval */
+ 0, /* igmpv3_unsolicited_report_interval */
+ 2, /* ignore_routes_with_linkdown */
+ 2, /* log_martians */
+ 2, /* mc_forwarding */
+ 0, /* medium_id */
+ 2, /* promote_secondaries */
+ 2, /* proxy_arp */
+ 2, /* proxy_arp_pvlan */
+ 2, /* route_localnet */
+ 3, /* rp_filter */
+ 2, /* secure_redirects */
+ 2, /* send_redirects */
+ 2, /* shared_media */
+ 0, /* src_valid_mark */
+ 0, /* tag */
+};
+
+struct test_conf {
+ int ipv4_conf[ARRAY_SIZE(devconfs)];
+ int ipv4_conf_rand[ARRAY_SIZE(devconfs)];
+ char *dir;
+} lo, def;
+
+static int save_and_set(int opt, FILE *fp, struct test_conf *tc) {
+ int ret;
+ int val;
+
+ /*
+ * Save
+ */
+ ret = fscanf(fp, "%d", &tc->ipv4_conf[opt]);
+ if (ret != 1) {
+ pr_perror("fscanf");
+ return -1;
+ }
+
+ ret = fseek(fp, 0, SEEK_SET);
+ if (ret) {
+ pr_perror("fseek");
+ return -1;
+ }
+
+ /*
+ * Set random value
+ */
+ val = (int)lrand48();
+
+ if (rand_limit[opt] != 0)
+ tc->ipv4_conf_rand[opt] = val % rand_limit[opt];
+ else
+ tc->ipv4_conf_rand[opt] = val;
+
+ ret = fprintf(fp, "%d", tc->ipv4_conf_rand[opt]);
+ if (ret < 0) {
+ pr_perror("fprintf");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_and_restore(int opt, FILE *fp, struct test_conf *tc) {
+ int ret;
+ int val;
+
+ /*
+ * Check opt
+ */
+ ret = fscanf(fp, "%d", &val);
+ if (ret != 1) {
+ pr_perror("fscanf");
+ return -1;
+ }
+
+ if (val != tc->ipv4_conf_rand[opt]) {
+ fail("Option \"%s/%s\" changed from %d to %d",
+ tc->dir, devconfs[opt], tc->ipv4_conf_rand[opt], val);
+ return -1;
+ }
+
+ ret = fseek(fp, 0, SEEK_SET);
+ if (ret) {
+ pr_perror("fseek");
+ return -1;
+ }
+
+ /*
+ * Restore opt
+ */
+ ret = fprintf(fp, "%d", tc->ipv4_conf[opt]);
+ if (ret < 0) {
+ pr_perror("fprintf");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int for_each_option_do(int (*f)(int opt, FILE *fp, struct test_conf *tc), struct test_conf *tc) {
+ int ret;
+ int i;
+
+ for (i = 0; devconfs[i]; i++) {
+ FILE *fp;
+ char path[PATH_MAX];
+
+ ret = snprintf(path, sizeof(path), "%s/%s", tc->dir, devconfs[i]);
+ if (ret < 0) {
+ pr_perror("snprintf");
+ return -1;
+ }
+
+ ret = access(path, W_OK);
+ if (ret < 0)
+ continue;
+
+ fp = fopen(path, "r+");
+ if (fp == NULL) {
+ pr_perror("fopen");
+ return -1;
+ }
+
+ ret = (*f)(i, fp, tc);
+ if (ret < 0)
+ return -1;
+
+ fclose(fp);
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ lo.dir = LO_CONF_DIR_PATH;
+ def.dir = DEF_CONF_DIR_PATH;
+
+ test_init(argc, argv);
+
+ ret = for_each_option_do(save_and_set, &lo);
+ if (ret < 0)
+ return -1;
+
+ ret = for_each_option_do(save_and_set, &def);
+ if (ret < 0)
+ return -1;
+
+ test_daemon();
+ test_waitsig();
+
+ ret = for_each_option_do(check_and_restore, &lo);
+ if (ret < 0)
+ return -1;
+
+ ret = for_each_option_do(check_and_restore, &def);
+ if (ret < 0)
+ return -1;
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/netns-dev.desc b/test/zdtm/static/netns-dev.desc
new file mode 100644
index 000000000..7657ba45c
--- /dev/null
+++ b/test/zdtm/static/netns-dev.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid'}
diff --git a/test/zdtm/static/netns-nf.c b/test/zdtm/static/netns-nf.c
new file mode 100644
index 000000000..393f0e732
--- /dev/null
+++ b/test/zdtm/static/netns-nf.c
@@ -0,0 +1,48 @@
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that netfilter rules (some) are kept";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char **argv)
+{
+ char cmd[128];
+
+ test_init(argc, argv);
+
+ if (system("iptables -A INPUT -t filter --protocol icmp -j DROP")) {
+ pr_perror("Can't set input rule");
+ return -1;
+ }
+
+ sprintf(cmd, "iptables -L > pre-%s", filename);
+ if (system(cmd)) {
+ pr_perror("Can't save iptables");
+ return -1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ sprintf(cmd, "iptables -L > post-%s", filename);
+ if (system(cmd)) {
+ fail("Can't get iptables");
+ return -1;
+ }
+
+ sprintf(cmd, "diff pre-%s post-%s", filename, filename);
+ if (system(cmd)) {
+ fail("Iptables differ");
+ return -1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/netns-nf.desc b/test/zdtm/static/netns-nf.desc
new file mode 100644
index 000000000..c43840580
--- /dev/null
+++ b/test/zdtm/static/netns-nf.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'deps': [ '/bin/sh', '/sbin/iptables', '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so', '/usr/bin/diff' ], 'flags': 'suid'}
diff --git a/test/zdtm/static/netns.c b/test/zdtm/static/netns.c
new file mode 100644
index 000000000..b7a75b6d9
--- /dev/null
+++ b/test/zdtm/static/netns.c
@@ -0,0 +1,55 @@
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that network environment (links, addresses and routes) are preserved";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+int main(int argc, char **argv)
+{
+ test_init(argc, argv);
+
+ if (system("ip link set lo up")) {
+ fail("Can't set lo up");
+ return -1;
+ }
+
+ if (system("ip addr add 1.2.3.4 dev lo")) {
+ fail("Can't add addr on lo");
+ return -1;
+ }
+
+ if (system("ip route add 1.2.3.5 dev lo")) {
+ fail("Can't add route via lo");
+ return -1;
+ }
+
+ if (system("ip route add 1.2.3.6 via 1.2.3.5")) {
+ fail("Can't add route via lo (2)");
+ return -1;
+ }
+
+ if (system("ip link > netns.dump.test && ip addr >> netns.dump.test && ip route >> netns.dump.test")) {
+ sleep(1000);
+ fail("Can't save net config");
+ return -1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (system("ip link > netns.rst.test && ip addr >> netns.rst.test && ip route >> netns.rst.test")) {
+ fail("Can't get net config");
+ return -1;
+ }
+
+ if (system("diff netns.rst.test netns.dump.test")) {
+ fail("Net config differs after restore");
+ return -1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/netns.desc b/test/zdtm/static/netns.desc
new file mode 100644
index 000000000..d0b2c2c1a
--- /dev/null
+++ b/test/zdtm/static/netns.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'deps': [ '/bin/sh', '/sbin/ip', '/usr/bin/diff'], 'flags': 'suid'}
diff --git a/test/zdtm/static/oom_score_adj.c b/test/zdtm/static/oom_score_adj.c
new file mode 100644
index 000000000..b4275301e
--- /dev/null
+++ b/test/zdtm/static/oom_score_adj.c
@@ -0,0 +1,94 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check for /proc/self/oom_score_adj restore";
+const char *test_author = "Dmitry Safonov <dsafonov@odin.com>";
+
+const char oom_score_adj_self[] = "/proc/self/oom_score_adj";
+const int test_value = 400;
+
+int get_oom_score_adj(const char *path, int *err)
+{
+ int fd;
+ ssize_t num;
+ char buf[11];
+
+ *err = 0;
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("Failed to open %s", path);
+ goto out;
+ }
+
+ num = read(fd, buf, 10);
+ close(fd);
+ if (num < 0) {
+ pr_perror("Unable to read %s", path);
+ goto out;
+ }
+ buf[num] = '\0';
+
+ return strtol(buf, NULL, 10);
+
+out:
+ *err = -1;
+ return 0;
+}
+
+int set_oom_score_adj(const char *path, int value)
+{
+ int fd, ret = 0;
+ char buf[11];
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ pr_perror("Failed to open %s", path);
+ return -1;
+ }
+
+ snprintf(buf, 11, "%d", value);
+
+ if (write(fd, buf, 11) < 0) {
+ pr_perror("Write %s to %s failed", buf, path);
+ ret = -1;
+ }
+
+ close(fd);
+ return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ int new_oom_score_adj;
+
+ test_init(argc, argv);
+
+ if (set_oom_score_adj(oom_score_adj_self, test_value) < 0)
+ return -1;
+
+ test_daemon();
+ test_waitsig();
+
+ new_oom_score_adj = get_oom_score_adj(oom_score_adj_self, &ret);
+ if (ret < 0)
+ return -1;
+
+ if (new_oom_score_adj != test_value) {
+ fail("OOM score value %d is different after restore: %d\n",
+ test_value, new_oom_score_adj);
+ return -1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/overmount_dev.c b/test/zdtm/static/overmount_dev.c
new file mode 100644
index 000000000..b3dd4e08b
--- /dev/null
+++ b/test/zdtm/static/overmount_dev.c
@@ -0,0 +1,92 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that we can migrate with a device special file "
+ "open in a directory which has been mounted over by "
+ "another filesystem";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char **argv)
+{
+ int fd;
+ char path[256];
+ struct stat st;
+ /* /dev/null params - sure to exist in a VPS */
+ mode_t mode = S_IFCHR | 0700;
+ dev_t dev = makedev(1, 3);
+
+ test_init(argc, argv);
+
+ if (snprintf(path, sizeof(path), "%s/foo", dirname) >= sizeof(path)) {
+ pr_perror("directory name \"%s\"is too long", dirname);
+ exit(1);
+ }
+
+ if (mkdir(dirname, 0700)) {
+ pr_perror("can't make directory %s", dirname);
+ exit(1);
+ }
+
+ if (mknod(path, mode, dev)) {
+ pr_perror("can't make device file \"%s\"", path);
+ exit(1);
+ }
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ pr_perror("can't open %s", path);
+ goto rmdir;
+ }
+
+ if (mount("rien", dirname, "tmpfs", 0, 0) < 0) {
+ pr_perror("can't mount tmpfs over %s", dirname);
+ goto cleanup;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (umount(dirname) < 0) {
+ fail("can't umount %s: %m", dirname);
+ goto cleanup;
+ }
+
+ if (close(fd) < 0) {
+ fail("can't close %s: %m", path);
+ goto unlink;
+ }
+
+ if (stat(path, &st) < 0) {
+ fail("can't stat %s: %m", path);
+ goto unlink;
+ }
+
+ if (st.st_mode != mode || st.st_rdev != dev) {
+ fail("%s is no longer the device file we had");
+ goto unlink;
+ }
+
+ if (unlink(path) < 0) {
+ fail("can't unlink %s: %m", path);
+ goto rmdir;
+ }
+
+ pass();
+ goto rmdir;
+cleanup:
+ close(fd);
+unlink:
+ unlink(path);
+rmdir:
+ rmdir(dirname);
+ return 0;
+}
diff --git a/test/zdtm/static/overmount_dev.desc b/test/zdtm/static/overmount_dev.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/overmount_dev.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/overmount_fifo.c b/test/zdtm/static/overmount_fifo.c
new file mode 100644
index 000000000..2de2a8a7e
--- /dev/null
+++ b/test/zdtm/static/overmount_fifo.c
@@ -0,0 +1,90 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that we can migrate with a named pipe "
+ "open in a directory which has been mounted over by "
+ "another filesystem";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char **argv)
+{
+ int fd;
+ char path[256];
+ struct stat st;
+ mode_t mode = S_IFIFO | 0700;
+
+ test_init(argc, argv);
+
+ if (snprintf(path, sizeof(path), "%s/foo", dirname) >= sizeof(path)) {
+ pr_perror("directory name \"%s\"is too long", dirname);
+ exit(1);
+ }
+
+ if (mkdir(dirname, 0700)) {
+ pr_perror("can't make directory %s", dirname);
+ exit(1);
+ }
+
+ if (mknod(path, mode, 0)) {
+ pr_perror("can't make fifo \"%s\"", path);
+ exit(1);
+ }
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ pr_perror("can't open %s", path);
+ goto rmdir;
+ }
+
+ if (mount("rien", dirname, "tmpfs", 0, 0) < 0) {
+ pr_perror("can't mount tmpfs over %s", dirname);
+ goto cleanup;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (umount(dirname) < 0) {
+ fail("can't umount %s: %m", dirname);
+ goto cleanup;
+ }
+
+ if (close(fd) < 0) {
+ fail("can't close %s: %m", path);
+ goto unlink;
+ }
+
+ if (stat(path, &st) < 0) {
+ fail("can't stat %s: %m", path);
+ goto unlink;
+ }
+
+ if (st.st_mode != mode) {
+ fail("%s is no longer the fifo we had");
+ goto unlink;
+ }
+
+ if (unlink(path) < 0) {
+ fail("can't unlink %s: %m", path);
+ goto rmdir;
+ }
+
+ pass();
+ goto rmdir;
+cleanup:
+ close(fd);
+unlink:
+ unlink(path);
+rmdir:
+ rmdir(dirname);
+ return 0;
+}
diff --git a/test/zdtm/static/overmount_fifo.desc b/test/zdtm/static/overmount_fifo.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/overmount_fifo.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/overmount_file.c b/test/zdtm/static/overmount_file.c
new file mode 100644
index 000000000..5c370e05a
--- /dev/null
+++ b/test/zdtm/static/overmount_file.c
@@ -0,0 +1,73 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that we can't migrate with a file open in a "
+ "directory which has been mounted over by another "
+ "filesystem";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char **argv)
+{
+ int fd;
+ char path[256];
+
+ test_init(argc, argv);
+
+ if (snprintf(path, sizeof(path), "%s/foo", dirname) >= sizeof(path)) {
+ pr_perror("directory name \"%s\"is too long", dirname);
+ exit(1);
+ }
+
+ if (mkdir(dirname, 0700)) {
+ pr_perror("can't make directory %s", dirname);
+ exit(1);
+ }
+
+ fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", path);
+ goto rmdir;
+ }
+
+ if (mount("rien", dirname, "tmpfs", 0, 0) < 0) {
+ pr_perror("can't mount tmpfs over %s", dirname);
+ goto cleanup;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (umount(dirname) < 0) {
+ fail("can't umount %s: %m", dirname);
+ goto cleanup;
+ }
+
+ if (close(fd) < 0) {
+ fail("can't close %s: %m", path);
+ goto unlink;
+ }
+
+ if (unlink(path) < 0) {
+ fail("can't unlink %s: %m", path);
+ goto rmdir;
+ }
+
+ pass();
+ goto rmdir;
+cleanup:
+ close(fd);
+unlink:
+ unlink(path);
+rmdir:
+ rmdir(dirname);
+ return 0;
+}
diff --git a/test/zdtm/static/overmount_file.desc b/test/zdtm/static/overmount_file.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/overmount_file.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/overmount_sock.c b/test/zdtm/static/overmount_sock.c
new file mode 100644
index 000000000..94e4c7e6f
--- /dev/null
+++ b/test/zdtm/static/overmount_sock.c
@@ -0,0 +1,207 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that we can migrate with a unix socket "
+ "bound in a directory which has been mounted over by"
+ " another filesystem";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+static int fill_sock_name(struct sockaddr_un *name, const char *filename)
+{
+ if (strlen(filename) >= sizeof(name->sun_path))
+ return -1;
+
+ name->sun_family = AF_LOCAL;
+ strcpy(name->sun_path, filename);
+ return 0;
+}
+
+static int setup_srv_sock(const char *filename)
+{
+ struct sockaddr_un name;
+ int sock;
+
+ if (fill_sock_name(&name, filename) < 0) {
+ pr_perror("filename \"%s\" is too long", filename);
+ return -1;
+ }
+
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0) {
+ pr_perror("can't create socket");
+ return -1;
+ }
+
+ if (bind(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0) {
+ pr_perror("can't bind to socket \"%s\"", filename);
+ goto err;
+ }
+
+ if (listen(sock, 1) < 0) {
+ pr_perror("can't listen on a socket \"%s\"", filename);
+ goto err;
+ }
+
+ return sock;
+err:
+ close(sock);
+ return -1;
+}
+
+static int setup_clnt_sock(const char *filename)
+{
+ struct sockaddr_un name;
+ int sock;
+
+ if (fill_sock_name(&name, filename) < 0)
+ return -1;
+
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -1;
+
+ if (connect(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0)
+ goto err;
+
+ return sock;
+err:
+ close(sock);
+ return -1;
+}
+
+int main(int argc, char ** argv)
+{
+ int sock, acc_sock, ret;
+ char path[256];
+ pid_t pid;
+ uint32_t crc;
+ uint8_t buf[1000];
+
+ test_init(argc, argv);
+
+ if (snprintf(path, sizeof(path), "%s/foo", dirname) >= sizeof(path)) {
+ pr_perror("directory name \"%s\"is too long", dirname);
+ exit(1);
+ }
+
+ if (mkdir(dirname, 0700)) {
+ pr_perror("can't make directory %s", dirname);
+ exit(1);
+ }
+
+ sock = setup_srv_sock(path);
+ if (sock < 0)
+ goto out;
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("can't fork");
+ goto out;
+ }
+
+ if (pid == 0) { /* child writes to the overmounted socket and returns */
+ close(sock);
+
+ sock = setup_clnt_sock(path);
+ if (sock < 0)
+ _exit(1);
+
+ test_waitsig();
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+ if (write(sock, buf, sizeof(buf)) != sizeof(buf))
+ _exit(errno);
+
+ close(sock);
+ _exit(0);
+ }
+
+ acc_sock = accept(sock, NULL, NULL);
+ if (acc_sock < 0) {
+ pr_perror("can't accept() the connection on \"%s\"", path);
+ goto out_kill;
+ }
+
+ close(sock);
+ sock = acc_sock;
+
+ if (mount("rien", dirname, "tmpfs", 0, 0) < 0) {
+ pr_perror("can't mount tmpfs over %s", dirname);
+ goto out_kill;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (kill(pid, SIGTERM)) {
+ fail("terminating the child failed: %m\n");
+ goto out;
+ }
+
+ if (wait(&ret) != pid) {
+ fail("wait() returned wrong pid %d: %m\n", pid);
+ goto out;
+ }
+
+ if (WIFEXITED(ret)) {
+ ret = WEXITSTATUS(ret);
+ if (ret) {
+ fail("child exited with nonzero code %d (%s)\n", ret,
+ strerror(ret));
+ goto out;
+ }
+ }
+ if (WIFSIGNALED(ret)) {
+ fail("child exited on unexpected signal %d\n", WTERMSIG(ret));
+ goto out;
+ }
+
+ if (read(sock, buf, sizeof(buf)) != sizeof(buf)) {
+ fail("can't read %s: %m\n", path);
+ goto out;
+ }
+
+ crc = ~0;
+ if (datachk(buf, sizeof(buf), &crc)) {
+ fail("CRC mismatch\n");
+ goto out;
+ }
+
+ if (umount(dirname) < 0) {
+ fail("can't umount %s: %m", dirname);
+ goto out;
+ }
+
+ if (close(sock) < 0) {
+ fail("can't close %s: %m", path);
+ goto out;
+ }
+
+ if (unlink(path) < 0) {
+ fail("can't unlink %s: %m", path);
+ goto out;
+ }
+
+ pass();
+
+out_kill:
+ kill(pid, SIGKILL);
+out:
+ close(sock);
+ unlink(path);
+ rmdir(dirname);
+ return 0;
+}
diff --git a/test/zdtm/static/overmount_sock.desc b/test/zdtm/static/overmount_sock.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/overmount_sock.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/packet_sock.c b/test/zdtm/static/packet_sock.c
new file mode 100644
index 000000000..66175d46b
--- /dev/null
+++ b/test/zdtm/static/packet_sock.c
@@ -0,0 +1,301 @@
+#include "zdtmtst.h"
+
+const char *test_doc = "static test for packet sockets";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+/*
+ * Description:
+ * Create and bind several packet sockets, check thet getname
+ * reports same result before and after c/r cycle. This is enough
+ * for _basic_ packet functionality only, but still.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <linux/version.h>
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+
+#define SK_RESERVE 8
+#define DEF_FANOUT 13
+
+#ifndef PACKET_FANOUT
+#define PACKET_FANOUT 18
+#endif
+
+static int test_sockaddr(int n, struct sockaddr_ll *have, struct sockaddr_ll *want)
+{
+ if (have->sll_family != want->sll_family) {
+ fail("%d Family mismatch %d/%d", n,
+ (int)have->sll_family, (int)want->sll_family);
+ return 1;
+ }
+
+ if (have->sll_protocol != want->sll_protocol) {
+ fail("%d Proto mismatch %d/%d", n,
+ (int)have->sll_protocol, (int)want->sll_protocol);
+ return 1;
+ }
+
+ if (have->sll_ifindex != want->sll_ifindex) {
+ fail("%d Index mismatch %d/%d", n,
+ have->sll_ifindex, want->sll_ifindex);
+ return 1;
+ }
+
+ /* all the others are derivatives from dev */
+ return 0;
+}
+
+#ifndef MAX_ADDR_LEN
+#define MAX_ADDR_LEN 32
+#endif
+
+struct packet_mreq_max {
+ int mr_ifindex;
+ unsigned short mr_type;
+ unsigned short mr_alen;
+ unsigned char mr_address[MAX_ADDR_LEN];
+};
+
+#define LO_ADDR_LEN 6
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
+
+struct tpacket_req3 {
+ unsigned int tp_block_size;
+ unsigned int tp_block_nr;
+ unsigned int tp_frame_size;
+ unsigned int tp_frame_nr;
+ unsigned int tp_retire_blk_tov;
+ unsigned int tp_sizeof_priv;
+ unsigned int tp_feature_req_word;
+};
+
+#endif
+
+int main(int argc, char **argv)
+{
+ int sk1, sk2;
+ struct sockaddr_ll addr, addr1, addr2;
+ socklen_t alen;
+ int ver, rsv, yes;
+ struct packet_mreq_max mreq;
+ struct tpacket_req3 ring;
+
+ test_init(argc, argv);
+
+ sk1 = socket(PF_PACKET, SOCK_RAW, 0);
+ if (sk1 < 0) {
+ pr_perror("Can't create socket 1");
+ return 1;
+ }
+
+ sk2 = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+ if (sk2 < 0) {
+ pr_perror("Can't create socket 2");
+ return 1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sll_family = AF_PACKET;
+ addr.sll_ifindex = 1; /* loopback should be 1 in all namespaces */
+ if (bind(sk2, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ pr_perror("Can't bind socket");
+ return 1;
+ }
+
+ alen = sizeof(addr1);
+ if (getsockname(sk1, (struct sockaddr *)&addr1, &alen) < 0) {
+ pr_perror("Can't get sockname 1");
+ return 1;
+ }
+
+ alen = sizeof(addr2);
+ if (getsockname(sk2, (struct sockaddr *)&addr2, &alen) < 0) {
+ pr_perror("Can't get sockname 2");
+ return 1;
+ }
+
+ ver = TPACKET_V2;
+ if (setsockopt(sk1, SOL_PACKET, PACKET_VERSION, &ver, sizeof(ver)) < 0) {
+ pr_perror("Can't set version");
+ return 1;
+ }
+
+ yes = 1;
+ if (setsockopt(sk1, SOL_PACKET, PACKET_AUXDATA, &yes, sizeof(yes)) < 0) {
+ pr_perror("Can't set auxdata");
+ return 1;
+ }
+
+ memset(&ring, 0, sizeof(ring));
+ ring.tp_block_size = PAGE_SIZE;
+ ring.tp_block_nr = 1;
+ ring.tp_frame_size = 1024;
+ ring.tp_frame_nr = (ring.tp_block_size / ring.tp_frame_size) * ring.tp_block_nr;
+ if (setsockopt(sk1, SOL_PACKET, PACKET_RX_RING, &ring, sizeof(ring)) < 0) {
+ pr_perror("Can't set rx ring");
+ return 1;
+ }
+
+ rsv = SK_RESERVE;
+ if (setsockopt(sk2, SOL_PACKET, PACKET_RESERVE, &rsv, sizeof(rsv)) < 0) {
+ pr_perror("Can't set reserve");
+ return 1;
+ }
+
+ yes = 1;
+ if (setsockopt(sk2, SOL_PACKET, PACKET_ORIGDEV, &yes, sizeof(yes)) < 0) {
+ pr_perror("Can't set origdev");
+ return 1;
+ }
+
+ yes = DEF_FANOUT;
+ if (setsockopt(sk2, SOL_PACKET, PACKET_FANOUT, &yes, sizeof(yes)) < 0) {
+ pr_perror("Can't configure fanout");
+ return 1;
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.mr_ifindex = 1;
+ mreq.mr_type = PACKET_MR_PROMISC;
+ if (setsockopt(sk1, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+ pr_perror("Can't add promisc member");
+ return 1;
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.mr_ifindex = 1;
+ mreq.mr_type = PACKET_MR_UNICAST;
+ mreq.mr_alen = LO_ADDR_LEN;
+ if (setsockopt(sk2, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+ pr_perror("Can't add ucast member");
+ return 1;
+ }
+
+ memset(&ring, 0, sizeof(ring));
+ ring.tp_block_size = PAGE_SIZE;
+ ring.tp_block_nr = 1;
+ ring.tp_frame_size = 1024;
+ ring.tp_frame_nr = (ring.tp_block_size / ring.tp_frame_size) * ring.tp_block_nr;
+ if (setsockopt(sk2, SOL_PACKET, PACKET_TX_RING, &ring, sizeof(ring)) < 0) {
+ pr_perror("Can't set tx ring");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ alen = sizeof(addr);
+ if (getsockname(sk1, (struct sockaddr *)&addr, &alen) < 0) {
+ fail("Can't get sockname 1 rst");
+ return 1;
+ }
+
+ if (test_sockaddr(1, &addr, &addr1))
+ return 1;
+
+ alen = sizeof(ver);
+ if (getsockopt(sk1, SOL_PACKET, PACKET_VERSION, &ver, &alen) < 0) {
+ fail("Can't get sockopt ver %m");
+ return 1;
+ }
+
+ if (ver != TPACKET_V2) {
+ fail("Version mismatch have %d, want %d\n", ver, TPACKET_V2);
+ return 1;
+ }
+
+ alen = sizeof(yes);
+ if (getsockopt(sk1, SOL_PACKET, PACKET_AUXDATA, &yes, &alen) < 0) {
+ fail("Can't get sockopt auxdata %m");
+ return 1;
+ }
+
+ if (yes != 1) {
+ fail("Auxdata not ON");
+ return 1;
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.mr_ifindex = 1;
+ mreq.mr_type = PACKET_MR_PROMISC;
+ if (setsockopt(sk1, SOL_PACKET, PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+ fail("Promisc member not kept");
+ return 1;
+ }
+
+ alen = sizeof(yes);
+ if (getsockopt(sk1, SOL_PACKET, PACKET_FANOUT, &yes, &alen) < 0) {
+ fail("Can't read fanout back %m");
+ return 1;
+ }
+
+ if (yes != 0) {
+ fail("Fanout screwed up to %x", yes);
+ return 1;
+ }
+
+ alen = sizeof(addr);
+ if (getsockname(sk2, (struct sockaddr *)&addr, &alen) < 0) {
+ fail("Can't get sockname 2 rst");
+ return 1;
+ }
+
+ if (test_sockaddr(2, &addr, &addr2))
+ return 1;
+
+ alen = sizeof(rsv);
+ if (getsockopt(sk2, SOL_PACKET, PACKET_RESERVE, &rsv, &alen) < 0) {
+ fail("Can't get sockopt rsv %m");
+ return 1;
+ }
+
+ alen = sizeof(yes);
+ if (getsockopt(sk2, SOL_PACKET, PACKET_ORIGDEV, &yes, &alen) < 0) {
+ fail("Can't get sockopt origdev %m");
+ return 1;
+ }
+
+ if (yes != 1) {
+ fail("OrigDev not ON");
+ return 1;
+ }
+
+ if (rsv != SK_RESERVE) {
+ fail("Reserve mismatch have %d, want %d\n", rsv, SK_RESERVE);
+ return 1;
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.mr_ifindex = 1;
+ mreq.mr_type = PACKET_MR_UNICAST;
+ mreq.mr_alen = LO_ADDR_LEN;
+ if (setsockopt(sk2, SOL_PACKET, PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+ fail("Ucast member not kept");
+ return 1;
+ }
+
+ alen = sizeof(yes);
+ if (getsockopt(sk2, SOL_PACKET, PACKET_FANOUT, &yes, &alen) < 0) {
+ fail("Can't read fanout2 back %m");
+ return 1;
+ }
+
+ if (yes != DEF_FANOUT) {
+ fail("Fanout2 screwed up to %x", yes);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/packet_sock.desc b/test/zdtm/static/packet_sock.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/packet_sock.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/packet_sock_mmap.c b/test/zdtm/static/packet_sock_mmap.c
new file mode 100644
index 000000000..99b6e2fc3
--- /dev/null
+++ b/test/zdtm/static/packet_sock_mmap.c
@@ -0,0 +1,103 @@
+#include "zdtmtst.h"
+
+const char *test_doc = "static test for packet sockets mmaps";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <linux/version.h>
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <sys/mman.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
+
+struct tpacket_req3 {
+ unsigned int tp_block_size;
+ unsigned int tp_block_nr;
+ unsigned int tp_frame_size;
+ unsigned int tp_frame_nr;
+ unsigned int tp_retire_blk_tov;
+ unsigned int tp_sizeof_priv;
+ unsigned int tp_feature_req_word;
+};
+
+#endif
+
+static void check_map_is_there(unsigned long addr, int sk)
+{
+ FILE *f;
+ char line[64];
+ struct stat ss;
+
+ fstat(sk, &ss);
+ f = fopen("/proc/self/maps", "r");
+ while (fgets(line, sizeof(line), f) != NULL) {
+ unsigned long start;
+ int maj, min, ino;
+
+ sscanf(line, "%lx-%*x %*s %*s %x:%x %d %*s", &start, &maj, &min, &ino);
+ if ((start == addr) && ss.st_dev == makedev(maj, min) && ss.st_ino == ino) {
+ pass();
+ return;
+ }
+ }
+
+ fail("No socket mapping found");
+}
+
+int main(int argc, char **argv)
+{
+ int sk;
+ struct tpacket_req3 ring;
+ void *mem;
+
+ test_init(argc, argv);
+
+ sk = socket(PF_PACKET, SOCK_RAW, 0);
+ if (sk < 0) {
+ pr_perror("Can't create socket 1");
+ return 1;
+ }
+
+ memset(&ring, 0, sizeof(ring));
+ ring.tp_block_size = PAGE_SIZE;
+ ring.tp_block_nr = 1;
+ ring.tp_frame_size = 1024;
+ ring.tp_frame_nr = (ring.tp_block_size / ring.tp_frame_size) * ring.tp_block_nr;
+ if (setsockopt(sk, SOL_PACKET, PACKET_RX_RING, &ring, sizeof(ring)) < 0) {
+ pr_perror("Can't set rx ring");
+ return 1;
+ }
+
+ memset(&ring, 0, sizeof(ring));
+ ring.tp_block_size = PAGE_SIZE;
+ ring.tp_block_nr = 1;
+ ring.tp_frame_size = 1024;
+ ring.tp_frame_nr = (ring.tp_block_size / ring.tp_frame_size) * ring.tp_block_nr;
+ if (setsockopt(sk, SOL_PACKET, PACKET_TX_RING, &ring, sizeof(ring)) < 0) {
+ pr_perror("Can't set tx ring");
+ return 1;
+ }
+
+ mem = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, sk, 0);
+ if (mem == MAP_FAILED) {
+ pr_perror("Can't mmap socket");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ check_map_is_there((unsigned long)mem, sk);
+
+ return 0;
+}
diff --git a/test/zdtm/static/packet_sock_mmap.desc b/test/zdtm/static/packet_sock_mmap.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/packet_sock_mmap.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/pdeath_sig.c b/test/zdtm/static/pdeath_sig.c
new file mode 100644
index 000000000..0f7436f63
--- /dev/null
+++ b/test/zdtm/static/pdeath_sig.c
@@ -0,0 +1,109 @@
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that pdeath sig is preserved";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+static int sigrecvd = 0;
+static void sigh(int s, siginfo_t *i, void *d)
+{
+ sigrecvd = 1;
+}
+
+#ifndef PR_SET_PDEATH_SIGNAL
+#define PR_SET_PDEATH_SIGNAL 1
+#endif
+
+int main(int argc, char **argv)
+{
+ int pid, ret, pw[2], pr[2];
+
+ test_init(argc, argv);
+
+ /*
+ * Here's what will happen here:
+ *
+ * me -(fork)-> P -(fork)-> C
+ * | |
+ * +-------------->-(pw)->-+
+ * +-<-(pr)-<--------------+
+ *
+ * We wait for C to prepare himself via pr.
+ * After C/R we kill P and close pw to wake up
+ * C. The we wait for it to report back via pr
+ * which signals has he received.
+ */
+
+ pipe(pw);
+ pipe(pr);
+
+ pid = fork();
+ if (pid == 0) {
+ pid = fork();
+ if (pid == 0) {
+ struct sigaction sa = {};
+ /* C */
+ close(pw[1]);
+ close(pr[0]);
+ sa.sa_sigaction = sigh;
+ ret = sigaction(SIGUSR1, &sa, NULL);
+ if (ret == 0)
+ ret = prctl(PR_SET_PDEATH_SIGNAL, SIGUSR1, 0, 0, 0);
+ write(pr[1], &ret, sizeof(ret));
+ read(pw[0], &ret, sizeof(ret));
+ write(pr[1], &sigrecvd, sizeof(sigrecvd));
+ } else {
+ /* P, pid == C */
+ close(pw[0]);
+ close(pw[1]);
+ close(pr[0]);
+ close(pr[1]);
+
+ /* Just hang */
+ waitpid(pid, NULL, 0);
+ }
+
+ exit(0);
+ }
+
+ /* me, pid == P */
+ close(pw[0]);
+ close(pr[1]);
+
+ ret = -1;
+ read(pr[0], &ret, sizeof(ret));
+ if (ret != 0) {
+ pr_perror("C start error");
+ goto out;
+ }
+
+ /*
+ * P didn't have time to close his pipes?
+ * That's OK, CRIU should C/R these knots.
+ */
+
+ test_daemon();
+ test_waitsig();
+
+out:
+ kill(pid, SIGKILL);
+ waitpid(pid, NULL, 0);
+ close(pw[1]);
+
+ if (ret == 0) {
+ read(pr[0], &ret, sizeof(ret));
+ if (ret != 1)
+ fail("USR1 isn't delivered");
+ else
+ pass();
+ }
+
+ return 0;
+}
diff --git a/test/zdtm/static/pid00.c b/test/zdtm/static/pid00.c
new file mode 100644
index 000000000..0b03ef596
--- /dev/null
+++ b/test/zdtm/static/pid00.c
@@ -0,0 +1,94 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that p?pid and e?[ug]id didn't change";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+int setfsuid(uid_t fsuid);
+int setfsgid(uid_t fsgid);
+
+int main(int argc, char **argv)
+{
+ int pid, s_p[2], f_p[2], r_p[3];
+ const __uid_t w_ruid = 1, w_euid = 2, w_suid = 3, w_fsuid = w_euid;
+ const __uid_t w_rgid = 5, w_egid = 6, w_sgid = 7, w_fsgid = 8;
+ __uid_t rid, eid, sid, fsid;
+ char res = 'x';
+
+ test_init(argc, argv);
+
+ pipe(s_p);
+ pipe(f_p);
+ pipe(r_p);
+
+ pid = fork();
+ if (pid == 0) {
+ close(s_p[0]);
+ close(f_p[1]);
+ close(r_p[0]);
+
+ setresgid(w_rgid, w_egid, w_sgid);
+ setfsgid(w_fsgid);
+ setresuid(w_ruid, w_euid, w_suid);
+ /* fsuid change is impossible after above */
+
+ close(s_p[1]);
+
+ read(f_p[0], &res, 1);
+ close(f_p[0]);
+
+#define CHECK_ID(__t, __w, __e) do { \
+ if (__t##id != w_##__t##__w##id) { \
+ res = __e; \
+ goto bad; \
+ } \
+ } while (0)
+
+ rid = eid = sid = fsid = 0;
+ getresuid(&rid, &eid, &sid);
+ fsid = setfsuid(w_euid);
+ CHECK_ID(r, u, '1');
+ CHECK_ID(e, u, '2');
+ CHECK_ID(s, u, '3');
+ CHECK_ID(s, u, '3');
+ CHECK_ID(fs, u, '4');
+
+ rid = eid = sid = fsid = 0;
+ getresgid(&rid, &eid, &sid);
+ fsid = setfsgid(w_fsgid);
+ CHECK_ID(r, g, '5');
+ CHECK_ID(e, g, '6');
+ CHECK_ID(s, g, '7');
+ CHECK_ID(fs, g, '8');
+
+ res = '0';
+bad:
+ write(r_p[1], &res, 1);
+ close(r_p[1]);
+ _exit(0);
+ }
+
+ close(f_p[0]);
+ close(s_p[1]);
+ close(r_p[1]);
+
+ read(s_p[0], &res, 1);
+ close(s_p[0]);
+
+ test_daemon();
+ test_waitsig();
+
+ close(f_p[1]);
+
+ read(r_p[0], &res, 1);
+ if (res == '0')
+ pass();
+ else
+ fail("Fail: %c", res);
+
+ return 0;
+}
diff --git a/test/zdtm/static/pid00.desc b/test/zdtm/static/pid00.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/pid00.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/pipe00.c b/test/zdtm/static/pipe00.c
new file mode 100644
index 000000000..dd487d091
--- /dev/null
+++ b/test/zdtm/static/pipe00.c
@@ -0,0 +1,121 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Lock inversion";
+const char *test_author = "Andrey Vagin <avagin@parallels.com>";
+
+#define TEST_STRING "Hello world"
+
+int main(int argc, char ** argv)
+{
+ int pipe1[2];
+ int pipe2[2];
+ int ret;
+ pid_t pid;
+ char buf[sizeof(TEST_STRING)];
+ task_waiter_t t;
+
+ test_init(argc, argv);
+
+ task_waiter_init(&t);
+
+ ret = pipe(pipe1);
+ if (ret)
+ return 1;
+
+ ret = pipe(pipe2);
+ if (ret)
+ return 1;
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("Can't fork");
+ exit(1);
+ } else if (pid == 0) {
+ if (dup2(pipe1[1], 11) == -1 || dup2(pipe2[0], 12) == -1) {
+ pr_perror("dup2 failed");
+ return 1;
+ }
+ } else {
+ if (dup2(pipe1[0], 12) == -1 || dup2(pipe2[1], 11) == -1) {
+ pr_perror("dup2 failed");
+ goto err;
+ }
+ }
+
+ close(pipe2[0]);
+ close(pipe2[1]);
+ close(pipe1[0]);
+ close(pipe1[1]);
+
+ if (pid > 0) {
+ int status;
+
+ task_waiter_wait4(&t, 1);
+
+ test_daemon();
+
+ test_waitsig();
+
+ ret = read(12, buf, sizeof(TEST_STRING));
+ if (ret != sizeof(TEST_STRING)) {
+ pr_perror("read failed: %d", ret);
+ goto err;
+ }
+ ret = write(11, TEST_STRING, sizeof(TEST_STRING));
+ if (ret != sizeof(TEST_STRING)) {
+ pr_perror("write failed: %d", ret);
+ goto err;
+ }
+ close(11);
+ ret = read(12, buf, sizeof(TEST_STRING));
+ if (ret != sizeof(TEST_STRING)) {
+ pr_perror("read failed: %d", ret);
+ goto err;
+ }
+ if (strcmp(TEST_STRING, buf)) {
+ pr_perror("data curruption");
+ goto err;
+ }
+
+ ret = wait(&status);
+ if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status)) {
+ kill(pid, SIGKILL);
+ goto err;
+ }
+
+ pass();
+ } else {
+ task_waiter_complete(&t, 1);
+ ret = write(11, TEST_STRING, sizeof(TEST_STRING));
+ if (ret != sizeof(TEST_STRING)) {
+ pr_perror("write failed: %d", ret);
+ return 1;
+ }
+ ret = read(12, buf, sizeof(TEST_STRING));
+ if (ret != sizeof(TEST_STRING)) {
+ pr_perror("read failed: %d", ret);
+ return 1;
+ }
+ ret = write(11, TEST_STRING, sizeof(TEST_STRING));
+ if (ret != sizeof(TEST_STRING)) {
+ pr_perror("write failed: %d", ret);
+ return 1;
+ }
+ close(11);
+ if (strcmp(TEST_STRING, buf)) {
+ pr_perror("data curruption");
+ return 1;
+ }
+ }
+
+ return 0;
+err:
+ pr_perror("FAIL");
+ return 1;
+}
diff --git a/test/zdtm/static/pipe01.c b/test/zdtm/static/pipe01.c
new file mode 100644
index 000000000..85478b6ea
--- /dev/null
+++ b/test/zdtm/static/pipe01.c
@@ -0,0 +1,132 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test that all data can be restored";
+const char *test_author = "Andrey Vagin <avagin@parallels.com>";
+
+#define TEST_STRING "Hello world"
+
+int main(int argc, char ** argv)
+{
+ int pfd[2], pfd_dup[2], pfd_rop[2];
+ char path[PATH_MAX];
+ int ret;
+ uint8_t buf[4096];
+ uint32_t crc;
+ int flags, size = 0;
+
+ test_init(argc, argv);
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+
+ ret = pipe(pfd);
+ if (ret) {
+ pr_perror("pipe() failed");
+ return 1;
+ }
+
+ pfd_dup[0] = dup(pfd[0]);
+ pfd_dup[1] = dup(pfd[1]);
+
+ snprintf(path, PATH_MAX, "/proc/self/fd/%d", pfd[0]);
+ pfd_rop[0] = open(path, O_RDONLY);
+ snprintf(path, PATH_MAX, "/proc/self/fd/%d", pfd[1]);
+ pfd_rop[1] = open(path, O_WRONLY);
+
+ if (pfd_rop[0] == -1 || pfd_rop[1] == -1 ||
+ pfd_dup[0] == -1 || pfd_dup[1] == -1) {
+ pr_perror("dup() failed");
+ return 1;
+ }
+
+ flags = fcntl(pfd[1], F_GETFL, 0);
+ if (flags == -1) {
+ pr_perror("fcntl() failed");
+ return 1;
+ }
+
+ ret = fcntl(pfd[1], F_SETFL, flags | O_NONBLOCK);
+ if (ret == -1) {
+ pr_perror("fcntl() failed");
+ return 1;
+ }
+
+ while (1) {
+ ret = write(pfd[1], buf, sizeof(buf));
+ if (ret == -1) {
+ if (errno == EAGAIN)
+ break;
+ pr_perror("write() failed");
+ goto err;
+ }
+
+ size += ret;
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ flags = fcntl(pfd[1], F_GETFL, 0);
+ if (!(flags & O_NONBLOCK)) {
+ pr_perror("O_NONBLOCK is absent");
+ goto err;
+ }
+
+ flags = fcntl(pfd_dup[1], F_GETFL, 0);
+ if (!(flags & O_NONBLOCK)) {
+ pr_perror("O_NONBLOCK is absent");
+ goto err;
+ }
+
+ flags = fcntl(pfd_rop[1], F_GETFL, 0);
+ if (flags & O_NONBLOCK) {
+ pr_perror("O_NONBLOCK appeared");
+ goto err;
+ }
+
+ if (close(pfd[1]) == -1) {
+ pr_perror("close() failed");
+ goto err;
+ }
+
+ close(pfd_dup[1]);
+ close(pfd_rop[1]);
+
+ while (1) {
+ ret = read(pfd[0], buf, sizeof(buf));
+ if (ret == 0)
+ break;
+ if (ret == -1) {
+ goto err;
+ pr_perror("read() failed");
+ }
+ size -= ret;
+
+ crc = ~0;
+ ret = datachk(buf, sizeof(buf), &crc);
+ if (ret) {
+ fail("CRC mismatch\n");
+ goto err;
+ }
+ }
+
+ if (size)
+ goto err;
+
+ pass();
+ return 0;
+err:
+ fail();
+ return 1;
+}
diff --git a/test/zdtm/static/pipe02.c b/test/zdtm/static/pipe02.c
new file mode 100644
index 000000000..2a8cca7da
--- /dev/null
+++ b/test/zdtm/static/pipe02.c
@@ -0,0 +1,61 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Create two unshared descriptor for the one end of a pipe";
+const char *test_author = "Andrey Vagin <avagin@parallels.com>";
+
+int main(int argc, char ** argv)
+{
+ int p[2], fd;
+ int ret;
+ char path[PATH_MAX];
+ int flags;
+
+ test_init(argc, argv);
+
+ ret = pipe(p);
+ if (ret)
+ return 1;
+
+ snprintf(path, sizeof(path), "/proc/self/fd/%d", p[0]);
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ pr_perror("open");
+ return 1;
+ };
+
+ if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1) {
+ pr_perror("fcntl");
+ return 1;
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if ((flags & O_NONBLOCK) == 0) {
+ fail("O_NONBLOCK are not restored for %d", fd);
+ return 1;
+ }
+
+ flags = fcntl(p[0], F_GETFL, 0);
+ if ((flags & O_NONBLOCK) != 0) {
+ fail("Unexpected O_NONBLOCK on %d", p[0]);
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/poll.c b/test/zdtm/static/poll.c
new file mode 100644
index 000000000..c0f757452
--- /dev/null
+++ b/test/zdtm/static/poll.c
@@ -0,0 +1,133 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <utime.h>
+#include <time.h>
+
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check poll() timeouts";
+const char *test_author = "Cyrill Gorcunov <gorcunov@parallels.com>";
+
+static void show_timestamp(char *prefix, unsigned long tv_sec, unsigned long tv_usec)
+{
+ test_msg("%8s: sec %20lu nsec %20lu\n", prefix, tv_sec, tv_usec);
+}
+
+static void show_pollfd(struct pollfd *fds, size_t nfds)
+{
+ size_t i;
+
+ for (i = 0; i < nfds; i++) {
+ test_msg("%2zu) fd: %2d events %2x revents %2x\n",
+ i, fds[i].fd, fds[i].events, fds[i].revents);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct timeval time1, time2;
+ struct timespec delay;
+ struct pollfd ufds[2];
+ int pipes[2], ret;
+ int delta, status;
+ task_waiter_t t;
+ pid_t pid;
+
+ test_init(argc, argv);
+ task_waiter_init(&t);
+
+ if (pipe(pipes)) {
+ pr_perror("Can't create pipes");
+ exit(1);
+ }
+
+ memset(ufds, 0, sizeof(ufds));
+ ufds[0].fd = pipes[0];
+ ufds[0].events = POLLIN;
+
+ ufds[1].fd = pipes[1];
+ ufds[1].events = POLLIN;
+
+ show_pollfd(ufds, 2);
+
+ if (gettimeofday(&time1, NULL)) {
+ pr_perror("Can't get first delta");
+ exit(1);
+ }
+ show_timestamp("Init", time1.tv_sec, time1.tv_usec);
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("Fork failed");
+ exit(1);
+ } else if (pid == 0) {
+ if (gettimeofday(&time1, NULL)) {
+ pr_perror("Can't get from times");
+ exit(1);
+ }
+
+ show_timestamp("Start", time1.tv_sec, time1.tv_usec);
+
+ task_waiter_complete(&t, 1);
+ delta = 5;
+ while (test_go()) {
+ ret = poll(ufds, 2, delta * 1000);
+ show_pollfd(ufds, 2);
+ if (ret && errno != EINTR) {
+ pr_perror("Poll-2 returned %d (events?!)", ret);
+ exit(1);
+ }
+
+ if (gettimeofday(&time2, NULL)) {
+ pr_perror("Can't get from times");
+ exit(1);
+ }
+
+ show_timestamp("Stop", time2.tv_sec, time2.tv_usec);
+ show_timestamp("Diff", time2.tv_sec - time1.tv_sec,
+ time2.tv_usec - time1.tv_usec);
+ if ((time2.tv_sec - time1.tv_sec) > delta) {
+ fail("Delta is too big %lu",
+ (unsigned long)(time2.tv_sec - time1.tv_sec));
+ exit(1);
+ }
+ }
+ exit(0);
+ }
+
+ task_waiter_wait4(&t, 1);
+
+ /* Wait to make sure we're in poll internals */
+ delay.tv_sec = 1;
+ delay.tv_nsec = 0;
+ nanosleep(&delay, NULL);
+
+ test_daemon();
+ test_waitsig();
+ kill(pid, SIGTERM);
+
+ /* Return immediately if child run or stopped(by SIGSTOP) */
+ if (waitpid(pid, &status, 0) == -1) {
+ pr_perror("Unable to wait child");
+ exit(1);
+ }
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+ fail("Child exited with error");
+ exit(1);
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/poll.desc b/test/zdtm/static/poll.desc
new file mode 100644
index 000000000..63df42aa6
--- /dev/null
+++ b/test/zdtm/static/poll.desc
@@ -0,0 +1 @@
+{'flavor': 'h'}
diff --git a/test/zdtm/static/posix_timers.c b/test/zdtm/static/posix_timers.c
new file mode 100644
index 000000000..97fd3c0a9
--- /dev/null
+++ b/test/zdtm/static/posix_timers.c
@@ -0,0 +1,443 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/time.h>
+#include <limits.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc ="Posix timers migration check";
+const char *test_author = "Kinsbursky Stanislav <skinsbursky@parallels.com>";
+
+sigset_t mask;
+
+#define WRONG_SIGNAL 1
+#define WRONG_SI_PTR 2
+#define FAIL_OVERRUN 4
+
+#define MAX_TIMER_DISPLACEMENT 10
+#define NO_PERIODIC
+
+#ifndef CLOCK_MONOTONIC_COARSE
+# define CLOCK_MONOTONIC_COARSE 6
+#endif
+
+#ifndef CLOCK_BOOTTIME
+# define CLOCK_BOOTTIME 7
+#endif
+
+#ifndef NO_PERIODIC
+static void realtime_periodic_handler(int sig, siginfo_t *si, void *uc);
+static void monotonic_periodic_handler(int sig, siginfo_t *si, void *uc);
+static void boottime_periodic_handler(int sig, siginfo_t *si, void *uc);
+#endif
+static void realtime_oneshot_handler(int sig, siginfo_t *si, void *uc);
+static void monotonic_oneshot_handler(int sig, siginfo_t *si, void *uc);
+static void boottime_oneshot_handler(int sig, siginfo_t *si, void *uc);
+
+enum {
+#ifndef NO_PERIODIC
+ REALTIME_PERIODIC_INFO,
+ MONOTONIC_PERIODIC_INFO,
+ BOOTTIME_PERIODIC_INFO,
+#endif
+ REALTIME_ONESHOT_INFO,
+ MONOTONIC_ONESHOT_INFO,
+ BOOTTIME_ONESHOT_INFO,
+};
+
+static struct posix_timers_info {
+ char clock;
+ char *name;
+ void (*handler)(int sig, siginfo_t *si, void *uc);
+ int sig;
+ int oneshot;
+ int ms_int;
+ struct sigaction sa;
+ int handler_status;
+ int handler_cnt;
+ timer_t timerid;
+ int overrun;
+ struct timespec start, end;
+} posix_timers[] = {
+#ifndef NO_PERIODIC
+ [REALTIME_PERIODIC_INFO] = {
+ .clock = CLOCK_REALTIME,
+ .name = "REALTIME (periodic)",
+ .handler = realtime_periodic_handler,
+ .sig = SIGALRM,
+ .oneshot = 0,
+ .ms_int = 1,
+ },
+ [MONOTONIC_PERIODIC_INFO] = {
+ .clock = CLOCK_MONOTONIC,
+ .name = "MONOTONIC (periodic)",
+ .handler = monotonic_periodic_handler,
+ .sig = SIGINT,
+ .oneshot = 0,
+ .ms_int = 3,
+ },
+ [BOOTTIME_PERIODIC_INFO] = {
+ .clock = CLOCK_BOOTTIME,
+ .name = "BOOTTIME (periodic)",
+ .handler = boottime_periodic_handler,
+ .sig = SIGWINCH,
+ .oneshot = 0,
+ .ms_int = 3,
+ },
+#endif
+ [REALTIME_ONESHOT_INFO] = {
+ .clock = CLOCK_REALTIME,
+ .name = "REALTIME (oneshot)",
+ .handler = realtime_oneshot_handler,
+ .sig = SIGUSR1,
+ .oneshot = 1,
+ .ms_int = INT_MAX,
+ },
+ [MONOTONIC_ONESHOT_INFO] = {
+ .clock = CLOCK_MONOTONIC,
+ .name = "MONOTONIC (oneshot)",
+ .handler = monotonic_oneshot_handler,
+ .sig = SIGUSR2,
+ .oneshot = 1,
+ .ms_int = INT_MAX,
+ },
+ [BOOTTIME_ONESHOT_INFO] = {
+ .clock = CLOCK_BOOTTIME,
+ .name = "BOOTTIME (oneshot)",
+ .handler = boottime_oneshot_handler,
+ .sig = SIGPROF,
+ .oneshot = 1,
+ .ms_int = INT_MAX,
+ },
+ { }
+};
+
+static int check_handler_status(struct posix_timers_info *info,
+ struct itimerspec *its, int ms_passed, int delta)
+{
+ int displacement;
+ int timer_ms;
+
+ if (!info->handler_cnt && !info->oneshot) {
+ fail("%s: Signal handler wasn't called\n", info->name);
+ return -EINVAL;
+ }
+
+ if (info->handler_status) {
+ if (info->handler_status & WRONG_SIGNAL)
+ fail("%s: Handler: wrong signal received\n", info->name);
+ if (info->handler_status & WRONG_SI_PTR)
+ fail("%s: Handler: wrong timer address\n", info->name);
+ if (info->handler_status & FAIL_OVERRUN)
+ fail("%s: Handler: failed to get overrun count\n", info->name);
+ return -1;
+ }
+
+ if (!info->oneshot && !its->it_value.tv_sec && !its->it_value.tv_nsec) {
+ fail("%s: timer became unset\n", info->name);
+ return -EFAULT;
+ }
+
+ if (info->oneshot && (its->it_interval.tv_sec || its->it_interval.tv_nsec)) {
+ fail("%s: timer became periodic\n", info->name);
+ return -EFAULT;
+ }
+
+ if (!info->oneshot && !its->it_interval.tv_sec && !its->it_interval.tv_nsec) {
+ fail("%s: timer became oneshot\n", info->name);
+ return -EFAULT;
+ }
+
+ if (info->oneshot) {
+ int val = its->it_value.tv_sec * 1000 + its->it_value.tv_nsec / 1000 / 1000;
+ if (info->handler_cnt) {
+ if (val != 0) {
+ fail("%s: timer continues ticking after expiration\n", info->name);
+ return -EFAULT;
+ }
+ if (info->handler_cnt > 1) {
+ fail("%s: timer expired %d times\n", info->name, info->handler_cnt);
+ return -EFAULT;
+ }
+ if (info->ms_int > ms_passed) {
+ fail("%s: timer expired too early\n", info->name);
+ return -EFAULT;
+ }
+ return 0;
+ }
+ timer_ms = info->ms_int - val;
+ } else
+ timer_ms = (info->overrun + info->handler_cnt) * info->ms_int;
+ displacement = (abs(ms_passed - timer_ms) - delta) * 100 / ms_passed;
+
+ test_msg("%20s: cpt/rst : %-8d msec\n", info->name, delta);
+ test_msg("%20s: Time passed (ms) : %-8d msec\n", info->name, ms_passed);
+ test_msg("%20s: Timer results : %-8d msec\n", info->name, timer_ms);
+ test_msg("%20s: Handler count : %d\n", info->name, info->handler_cnt);
+
+ if (displacement > MAX_TIMER_DISPLACEMENT) {
+ fail("%32s: Time displacement: %d%% (max alloved: %d%%)\n", info->name, displacement, MAX_TIMER_DISPLACEMENT);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int check_timers(int delta, struct timespec *sleep_start, struct timespec *sleep_end)
+{
+ struct posix_timers_info *info = posix_timers;
+ int ms_passed;
+ int status = 0;
+ struct itimerspec val, oldval;
+
+ if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) {
+ fail("Failed to unlock signal\n");
+ return -errno;
+ }
+
+ while (info->handler) {
+ memset(&val, 0, sizeof(val));
+ if (timer_settime(info->timerid, 0, &val, &oldval) == -1) {
+ fail("%s: failed to reset timer\n", info->name);
+ return -errno;
+ }
+
+ if (clock_gettime(info->clock, &info->end) == -1) {
+ fail("Can't get %s end time\n", info->name);
+ return -errno;
+ }
+
+ /*
+ * Adjust with @total_sleep_time if needed.
+ */
+ if (info->clock == CLOCK_BOOTTIME) {
+ info->start.tv_sec -= sleep_start->tv_sec;
+ info->start.tv_nsec -= sleep_start->tv_nsec;
+ info->end.tv_sec -= sleep_end->tv_sec;
+ info->end.tv_nsec -= sleep_end->tv_nsec;
+ }
+
+ ms_passed = (info->end.tv_sec - info->start.tv_sec) * 1000 +
+ (info->end.tv_nsec - info->start.tv_nsec) / (1000 * 1000);
+
+ if (check_handler_status(info, &oldval, ms_passed, delta))
+ status--;
+ info++;
+ }
+ return status;
+}
+
+static void generic_handler(struct posix_timers_info *info,
+ struct posix_timers_info *real, int sig)
+{
+ int overrun;
+
+ if (info == NULL)
+ info = &posix_timers[MONOTONIC_ONESHOT_INFO];
+
+ if (info != real) {
+ real->handler_status |= WRONG_SI_PTR;
+ return;
+ }
+
+ if (sig != info->sig)
+ info->handler_status |= WRONG_SIGNAL;
+
+ overrun = timer_getoverrun(info->timerid);
+ if (overrun == -1)
+ info->handler_status |= FAIL_OVERRUN;
+ else
+ info->overrun += overrun;
+ info->handler_cnt++;
+}
+
+#ifndef NO_PERIODIC
+static void monotonic_periodic_handler(int sig, siginfo_t *si, void *uc)
+{
+ generic_handler(si->si_value.sival_ptr,
+ &posix_timers[MONOTONIC_PERIODIC_INFO], sig);
+}
+
+static void boottime_periodic_handler(int sig, siginfo_t *si, void *uc)
+{
+ generic_handler(si->si_value.sival_ptr,
+ &posix_timers[BOOTTIME_PERIODIC_INFO], sig);
+}
+#endif
+
+static void monotonic_oneshot_handler(int sig, siginfo_t *si, void *uc)
+{
+ generic_handler(si->si_value.sival_ptr,
+ &posix_timers[MONOTONIC_ONESHOT_INFO], sig);
+}
+
+static void boottime_oneshot_handler(int sig, siginfo_t *si, void *uc)
+{
+ generic_handler(si->si_value.sival_ptr,
+ &posix_timers[BOOTTIME_ONESHOT_INFO], sig);
+}
+
+#ifndef NO_PERIODIC
+static void realtime_periodic_handler(int sig, siginfo_t *si, void *uc)
+{
+ generic_handler(si->si_value.sival_ptr,
+ &posix_timers[REALTIME_PERIODIC_INFO], sig);
+}
+#endif
+
+static void realtime_oneshot_handler(int sig, siginfo_t *si, void *uc)
+{
+ generic_handler(si->si_value.sival_ptr,
+ &posix_timers[REALTIME_ONESHOT_INFO], sig);
+}
+
+static int setup_timers(void)
+{
+ int i;
+ int ret;
+ struct posix_timers_info *info = posix_timers;
+ struct sigevent sev;
+ struct itimerspec its;
+
+ sigemptyset(&mask);
+ while(info->handler) {
+ sigaddset(&mask, info->sig);
+ info++;
+ }
+
+ if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) {
+ pr_perror("Failed to unlock signal");
+ return -errno;
+ }
+
+ info = posix_timers;
+ while(info->handler) {
+ /* Add and delete fake timers to test restoring 'with holes' */
+ timer_t timeridt;
+ for (i = 0; i < 10; i++) {
+ ret = timer_create(CLOCK_REALTIME, NULL, &timeridt);
+ if (ret < 0) {
+ pr_perror("Can't create temporary posix timer %lx", (long) timeridt);
+ return -errno;
+ }
+ ret = timer_delete(timeridt);
+ if (ret < 0) {
+ pr_perror("Can't remove temporaty posix timer %lx", (long) timeridt);
+ return -errno;
+ }
+ }
+
+ info->sa.sa_flags = SA_SIGINFO;
+ info->sa.sa_sigaction = info->handler;
+ sigemptyset(&info->sa.sa_mask);
+
+ if (sigaction(info->sig, &info->sa, NULL) == -1) {
+ pr_perror("Failed to set SIGALRM handler");
+ return -errno;
+ }
+
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = info->sig;
+ if (&posix_timers[MONOTONIC_ONESHOT_INFO] == info)
+ sev.sigev_value.sival_ptr = NULL;
+ else
+ sev.sigev_value.sival_ptr = info;
+
+ if (timer_create(info->clock, &sev, &info->timerid) == -1) {
+ pr_perror("Can't create timer");
+ return -errno;
+ }
+
+ its.it_value.tv_sec = info->ms_int / 1000;
+ its.it_value.tv_nsec = info->ms_int % 1000 * 1000 * 1000;
+ if (!info->oneshot) {
+ its.it_interval.tv_sec = its.it_value.tv_sec;
+ its.it_interval.tv_nsec = its.it_value.tv_nsec;
+ } else
+ its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
+
+ if (clock_gettime(info->clock, &info->start) == -1) {
+ pr_perror("Can't get %s start time", info->name);
+ return -errno;
+ }
+
+ if (timer_settime(info->timerid, 0, &its, NULL) == -1) {
+ pr_perror("Can't set timer");
+ return -errno;
+ }
+ info++;
+ }
+ return 0;
+}
+
+/*
+ * Figure out @total_sleep_time, ie time the system was in hardware
+ * suspend mode, will need this value to exclude from boottime clock
+ * testing.
+ */
+static int get_total_sleep_time(struct timespec *tv, char *type)
+{
+ struct timespec boottime_coarse;
+ struct timespec boottime;
+
+ if (clock_gettime(CLOCK_BOOTTIME, &boottime) == -1) {
+ pr_perror("Can't get CLOCK_BOOTTIME %s time", type);
+ return -errno;
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC_COARSE, &boottime_coarse) == -1) {
+ pr_perror("Can't get CLOCK_MONOTONIC_COARSE %s time", type);
+ return -errno;
+ }
+
+ tv->tv_sec = boottime.tv_sec - boottime_coarse.tv_sec;
+ tv->tv_nsec = boottime.tv_nsec - boottime_coarse.tv_nsec;
+
+ test_msg("(%6s) boottime %lu "
+ "boottime-coarse %lu "
+ "total_sleep_time %lu\n",
+ type,
+ (long)boottime.tv_sec,
+ (long)boottime_coarse.tv_sec,
+ (long)tv->tv_sec);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct timespec sleep_start, sleep_end;
+ struct timespec start, end;
+ int err;
+
+ test_init(argc, argv);
+
+ err = setup_timers();
+ if (err)
+ return err;
+
+ usleep(500 * 1000);
+
+ clock_gettime(CLOCK_REALTIME, &start);
+ err = get_total_sleep_time(&sleep_start, "start");
+ if (err)
+ return err;
+
+ test_daemon();
+ test_waitsig();
+
+ clock_gettime(CLOCK_REALTIME, &end);
+ err = get_total_sleep_time(&sleep_end, "end");
+ if (err)
+ return err;
+ err = check_timers((end.tv_sec - start.tv_sec) * 1000 +
+ (end.tv_nsec - start.tv_nsec) / 1000000,
+ &sleep_start, &sleep_end);
+ if (err)
+ return err;
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/proc-self.c b/test/zdtm/static/proc-self.c
new file mode 100644
index 000000000..54cc5f1f3
--- /dev/null
+++ b/test/zdtm/static/proc-self.c
@@ -0,0 +1,79 @@
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <utime.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check for /proc/self/ns path restore";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+const char nspath[] = "/proc/self/ns/net";
+
+int read_fd_link(int lfd, char *buf, size_t size)
+{
+ ssize_t ret;
+ char t[32];
+
+ snprintf(t, sizeof(t), "/proc/self/fd/%d", lfd);
+ ret = readlink(t, buf, size - 1);
+ if (ret < 0) {
+ pr_perror("Can't read link of fd %d", lfd);
+ return -1;
+ }
+ buf[ret] = 0;
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char path_orig[64], path_new[64];
+ int fd_self, fd_new;
+
+ test_init(argc, argv);
+
+ memset(path_orig, 0, sizeof(path_orig));
+ memset(path_new, 0, sizeof(path_new));
+
+ fd_self = open(nspath, O_RDONLY);
+ if (fd_self < 0) {
+ pr_perror("Can't open %s", nspath);
+ return -1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (read_fd_link(fd_self, path_orig, sizeof(path_orig))) {
+ pr_perror("Can't fill original path");
+ return -1;
+ }
+
+ fd_new = open(nspath, O_RDONLY);
+ if (fd_new < 0) {
+ pr_perror("Can't open %s", nspath);
+ return -1;
+ }
+
+ if (read_fd_link(fd_new, path_new, sizeof(path_new))) {
+ pr_perror("Can't fill new path");
+ return -1;
+ }
+
+ if (memcmp(path_orig, path_new, sizeof(path_orig))) {
+ fail("Paths mismatch %s %s\n", path_orig, path_new);
+ return -1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/pstree.c b/test/zdtm/static/pstree.c
new file mode 100644
index 000000000..ba94a275d
--- /dev/null
+++ b/test/zdtm/static/pstree.c
@@ -0,0 +1,87 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that environment didn't change";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+int main(int argc, char **argv)
+{
+ char x;
+ int pid, ppid;
+ int sp[2], fp[2], rp[2];
+
+ test_init(argc, argv);
+
+ if (pipe(sp) || pipe(fp) || pipe(rp)) {
+ pr_perror("pipe");
+ return 1;
+ }
+
+ pid = fork();
+ if (pid == 0) {
+ close(sp[0]);
+ close(fp[1]);
+ close(rp[0]);
+
+ pid = getpid();
+ ppid = getppid();
+
+ close(sp[1]);
+ if (read(fp[0], &x, 1)) {
+ pr_perror("read");
+ return 1;
+ }
+ close(fp[0]);
+
+ if (pid != getpid())
+ x = 'p';
+ else if (ppid != getppid())
+ x = 'P';
+ else
+ x = '0';
+
+ if (write(rp[1], &x, 1) != 1) {
+ pr_perror("write");
+ return 1;
+ }
+ close(rp[1]);
+ _exit(0);
+ }
+
+ x = 'X';
+ close(sp[1]);
+ close(fp[0]);
+ close(rp[1]);
+
+ if (read(sp[0], &x, 1)) {
+ pr_perror("read");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ close(fp[1]);
+ if (read(rp[0], &x, 1) != 1) {
+ pr_perror("read");
+ return 1;
+ }
+ close(rp[0]);
+
+ if (x == 'X')
+ fail("Sync failed");
+ else if (x == 'p')
+ fail("Pid failed");
+ else if (x == 'P')
+ fail("PPid failed");
+ else if (x != '0')
+ fail("Shit happened");
+ else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/pthread00.c b/test/zdtm/static/pthread00.c
new file mode 100644
index 000000000..2b248b234
--- /dev/null
+++ b/test/zdtm/static/pthread00.c
@@ -0,0 +1,185 @@
+/*
+ * A simple testee program with threads
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <syscall.h>
+
+#include "zdtmtst.h"
+
+#define exit_group(code) \
+ syscall(__NR_exit_group, code)
+
+const char *test_doc = "Create a few pthreads/forks and compare TLS and mmap data on restore\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org";
+
+static __thread char tls_data[10];
+
+#define TRANSITION_PASSED 4
+#define TRANSITION_FAILED 8
+
+#define MAP(map, i) (((int *)map)[i])
+
+#define SET_PASSED(map, i) MAP(map, i) = TRANSITION_PASSED
+#define SET_FAILED(map, i) MAP(map, i) = TRANSITION_FAILED
+
+#define IS_PASSED(map, i) (MAP(map, i) & TRANSITION_PASSED)
+
+#define NR_WAITERS 6
+static task_waiter_t waiter[NR_WAITERS];
+
+#define passage(index) \
+ do { \
+ task_waiter_complete(&waiter[index], 1); \
+ task_waiter_wait4(&waiter[index], 2); \
+ if (memcmp(tls_data, __tls_data, sizeof(tls_data))) \
+ SET_FAILED(map, index); \
+ else \
+ SET_PASSED(map, index); \
+ } while (0)
+
+static void *thread_subfunc_1(void *map)
+{
+ char __tls_data[10] = "1122334455";
+ pid_t pid;
+ int status;
+
+ memcpy(tls_data, __tls_data, sizeof(tls_data));
+
+ pid = test_fork();
+ if (pid < 0) {
+ exit_group(1);
+ } else if (pid == 0) {
+ passage(0);
+ exit(0);
+ }
+
+ passage(1);
+
+ test_msg("Waiting for %d\n", pid);
+ waitpid(pid, &status, 0);
+
+ return NULL;
+}
+
+static void *thread_func_1(void *map)
+{
+ char __tls_data[10] = "3122131212";
+ pthread_t th;
+ pid_t pid;
+ int status;
+
+ memcpy(tls_data, __tls_data, sizeof(tls_data));
+
+ if (pthread_create(&th, NULL, &thread_subfunc_1, map)) {
+ fail("Can't pthread_create");
+ exit_group(1);
+ }
+
+ pid = test_fork();
+ if (pid < 0) {
+ fail("Failed to test_fork()\n");
+ exit_group(1);
+ } else if (pid == 0) {
+ passage(2);
+ exit(0);
+ }
+
+ passage(3);
+
+ pthread_join(th, NULL);
+ test_msg("Waiting for %d\n", pid);
+ waitpid(pid, &status, 0);
+
+ return NULL;
+}
+
+static void *thread_func_2(void *map)
+{
+ char __tls_data[10] = "wasdfrdgdc";
+ pid_t pid;
+ int status;
+
+ memcpy(tls_data, __tls_data, sizeof(tls_data));
+
+ pid = test_fork();
+ if (pid < 0) {
+ fail("Failed to test_fork()\n");
+ exit_group(1);
+ } else if (pid == 0) {
+ passage(4);
+ exit(0);
+ }
+
+ passage(5);
+
+ test_msg("Waiting for %d\n", pid);
+ waitpid(pid, &status, 0);
+
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ pthread_t th1, th2;
+ int rc1, rc2, i;
+ void *map;
+
+ test_init(argc, argv);
+
+ for (i = 0; i < NR_WAITERS; i++)
+ task_waiter_init(&waiter[i]);
+
+ test_msg("%s pid %d\n", argv[0], getpid());
+ map = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (map == MAP_FAILED) {
+ fail("Can't map");
+ exit(1);
+ }
+
+ rc1 = pthread_create(&th1, NULL, &thread_func_1, map);
+ rc2 = pthread_create(&th2, NULL, &thread_func_2, map);
+
+ if (rc1 | rc2) {
+ fail("Can't pthread_create");
+ exit(1);
+ }
+
+ test_msg("Waiting until all threads are ready\n");
+
+ for (i = NR_WAITERS - 1; i >= 0; i--)
+ task_waiter_wait4(&waiter[i], 1);
+
+ test_daemon();
+ test_waitsig();
+
+ for (i = 0; i < NR_WAITERS; i++)
+ task_waiter_complete(&waiter[i], 2);
+
+ test_msg("Waiting while all threads are joined\n");
+ pthread_join(th1, NULL);
+ pthread_join(th2, NULL);
+
+ if (IS_PASSED(map, 0) &&
+ IS_PASSED(map, 1) &&
+ IS_PASSED(map, 2) &&
+ IS_PASSED(map, 3) &&
+ IS_PASSED(map, 4) &&
+ IS_PASSED(map, 5))
+ pass();
+ else
+ fail();
+
+ return 0;
+}
diff --git a/test/zdtm/static/pthread01.c b/test/zdtm/static/pthread01.c
new file mode 100644
index 000000000..52f849c06
--- /dev/null
+++ b/test/zdtm/static/pthread01.c
@@ -0,0 +1,209 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include "zdtmtst.h"
+
+#define gettid() pthread_self()
+
+const char *test_doc = "Create a few pthreads and test TLS + blocked signals\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org";
+
+static __thread struct tls_data_s {
+ char *rand_string[10];
+ sigset_t blk_sigset;
+} tls_data;
+
+static task_waiter_t t1;
+static task_waiter_t t2;
+
+static char *decode_signal(const sigset_t *s, char *buf)
+{
+ buf[0] = '\0';
+
+#define COLLECT(sig) \
+ do { \
+ if ((long)s->__val[0] & (long)sigmask(sig)) \
+ strcat(buf, #sig " "); \
+ } while (0)
+
+ COLLECT(SIGHUP); COLLECT(SIGINT); COLLECT(SIGQUIT); COLLECT(SIGILL); COLLECT(SIGTRAP);
+ COLLECT(SIGABRT); COLLECT(SIGIOT); COLLECT(SIGBUS); COLLECT(SIGFPE); COLLECT(SIGKILL);
+ COLLECT(SIGUSR1); COLLECT(SIGSEGV); COLLECT(SIGUSR2); COLLECT(SIGPIPE); COLLECT(SIGALRM);
+ COLLECT(SIGTERM); COLLECT(SIGSTKFLT); COLLECT(SIGCHLD); COLLECT(SIGCONT); COLLECT(SIGSTOP);
+ COLLECT(SIGTSTP); COLLECT(SIGTTIN); COLLECT(SIGTTOU); COLLECT(SIGURG); COLLECT(SIGXCPU);
+ COLLECT(SIGXFSZ); COLLECT(SIGVTALRM); COLLECT(SIGPROF); COLLECT(SIGWINCH); COLLECT(SIGIO);
+ COLLECT(SIGPOLL); COLLECT(SIGPWR); COLLECT(SIGSYS); COLLECT(SIGUNUSED);
+#undef COLLECT
+
+ return buf;
+}
+
+static void __show_sigset(int line, const sigset_t *s)
+{
+ char buf[sizeof(sigset_t) * 2 + 1] = { };
+
+ decode_signal(s, buf);
+ test_msg("sigset at %4d: %s\n", line, buf);
+}
+
+#define show_sigset(set) __show_sigset(__LINE__, set)
+
+static void *ch_thread_2(void *arg)
+{
+ char __tls_data[sizeof(tls_data.rand_string)] = "XM5o:?B*[a";
+ int *results_map = arg;
+ sigset_t blk_sigset = { };
+ sigset_t new = { };
+
+ memcpy(tls_data.rand_string, __tls_data, sizeof(tls_data.rand_string));
+
+ sigemptyset(&blk_sigset);
+ pthread_sigmask(SIG_SETMASK, NULL, &blk_sigset);
+ sigaddset(&blk_sigset, SIGFPE);
+ pthread_sigmask(SIG_SETMASK, &blk_sigset, NULL);
+ memcpy(&tls_data.blk_sigset, &blk_sigset, sizeof(tls_data.blk_sigset));
+
+ show_sigset(&blk_sigset);
+ show_sigset(&tls_data.blk_sigset);
+
+ task_waiter_complete(&t2, 1);
+ task_waiter_wait4(&t2, 2);
+
+ if (memcmp(tls_data.rand_string, __tls_data, sizeof(tls_data.rand_string))) {
+ pr_perror("Failed to restore tls_data.rand_string in thread 2");
+ results_map[2] = -1;
+ } else
+ results_map[2] = 1;
+
+ if (memcmp(&tls_data.blk_sigset, &blk_sigset, sizeof(tls_data.blk_sigset))) {
+ pr_perror("Failed to restore tls_data.blk_sigset in thread 2");
+ results_map[4] = -1;
+ } else
+ results_map[4] = 1;
+
+ pthread_sigmask(SIG_SETMASK, NULL, &new);
+ if (memcmp(&tls_data.blk_sigset, &new, sizeof(tls_data.blk_sigset))) {
+ pr_perror("Failed to restore blk_sigset in thread 2");
+ results_map[6] = -1;
+
+ show_sigset(&tls_data.blk_sigset);
+ show_sigset(&new);
+ } else
+ results_map[6] = 1;
+
+ return NULL;
+}
+
+static void *ch_thread_1(void *arg)
+{
+ char __tls_data[sizeof(tls_data.rand_string)] = "pffYQSBo?6";
+ int *results_map = arg;
+ sigset_t blk_sigset = { };
+ sigset_t new = { };
+
+ memcpy(tls_data.rand_string, __tls_data, sizeof(tls_data.rand_string));
+
+ sigemptyset(&blk_sigset);
+ pthread_sigmask(SIG_SETMASK, NULL, &blk_sigset);
+ sigaddset(&blk_sigset, SIGWINCH);
+ sigaddset(&blk_sigset, SIGALRM);
+ pthread_sigmask(SIG_SETMASK, &blk_sigset, NULL);
+ memcpy(&tls_data.blk_sigset, &blk_sigset, sizeof(tls_data.blk_sigset));
+
+ show_sigset(&blk_sigset);
+ show_sigset(&tls_data.blk_sigset);
+
+ task_waiter_complete(&t1, 1);
+ task_waiter_wait4(&t1, 2);
+
+ if (memcmp(tls_data.rand_string, __tls_data, sizeof(tls_data.rand_string))) {
+ pr_perror("Failed to restore tls_data.rand_string in thread 1");
+ results_map[1] = -1;
+ } else
+ results_map[1] = 1;
+
+ if (memcmp(&tls_data.blk_sigset, &blk_sigset, sizeof(tls_data.blk_sigset))) {
+ pr_perror("Failed to restore tls_data.blk_sigset in thread 1");
+ results_map[3] = -1;
+ } else
+ results_map[3] = 1;
+
+ sigemptyset(&new);
+ pthread_sigmask(SIG_SETMASK, NULL, &new);
+ if (memcmp(&tls_data.blk_sigset, &new, sizeof(tls_data.blk_sigset))) {
+ pr_perror("Failed to restore blk_sigset in thread 1");
+ results_map[5] = -1;
+
+ show_sigset(&tls_data.blk_sigset);
+ show_sigset(&new);
+ } else
+ results_map[5] = 1;
+
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ pthread_t thread_1, thread_2;
+ int *results_map;
+ int rc1, rc2;
+
+ test_init(argc, argv);
+
+ task_waiter_init(&t1);
+ task_waiter_init(&t2);
+
+ test_msg("%s pid %d\n", argv[0], getpid());
+
+ results_map = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if ((void *)results_map == MAP_FAILED) {
+ fail("Can't map");
+ exit(1);
+ }
+
+ rc1 = pthread_create(&thread_1, NULL, &ch_thread_1, results_map);
+ rc2 = pthread_create(&thread_2, NULL, &ch_thread_2, results_map);
+
+ if (rc1 | rc2) {
+ fail("Can't pthread_create");
+ exit(1);
+ }
+
+ test_msg("Waiting until all threads are created\n");
+
+ task_waiter_wait4(&t1, 1);
+ task_waiter_wait4(&t2, 1);
+
+ test_daemon();
+ test_waitsig();
+
+ task_waiter_complete(&t1, 2);
+ task_waiter_complete(&t2, 2);
+
+ test_msg("Waiting while all threads are joined\n");
+ pthread_join(thread_1, NULL);
+ pthread_join(thread_2, NULL);
+
+ if (results_map[1] == 1 &&
+ results_map[2] == 1 &&
+ results_map[3] == 1 &&
+ results_map[4] == 1 &&
+ results_map[5] == 1 &&
+ results_map[6] == 1)
+ pass();
+ else
+ fail();
+
+ return 0;
+}
diff --git a/test/zdtm/static/pthread02.c b/test/zdtm/static/pthread02.c
new file mode 100644
index 000000000..186271b74
--- /dev/null
+++ b/test/zdtm/static/pthread02.c
@@ -0,0 +1,43 @@
+/*
+ * A simple testee program with threads
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <pthread.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Create a thread with a dead leader\n";
+const char *test_author = "Andrew Vagin <avagin@openvz.org";
+
+
+static void *thread_func(void *args)
+{
+ test_waitsig();
+ pass();
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ pthread_t th1;
+ int ret;
+
+ test_init(argc, argv);
+
+
+ ret = pthread_create(&th1, NULL, &thread_func, NULL);
+
+ if (ret) {
+ fail("Can't pthread_create");
+ exit(1);
+ }
+
+ test_daemon();
+
+ pthread_exit(NULL);
+ return 0;
+}
diff --git a/test/zdtm/static/pthread02.desc b/test/zdtm/static/pthread02.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/pthread02.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/ptrace_sig.c b/test/zdtm/static/ptrace_sig.c
new file mode 100644
index 000000000..a8e1dc501
--- /dev/null
+++ b/test/zdtm/static/ptrace_sig.c
@@ -0,0 +1,189 @@
+#include <sys/ptrace.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check ptrace, if the child process's stopped by signal";
+const char *test_author = "Andrey Vagin <avagin@parallels.com>";
+
+typedef void (*sighandler_t)(int);
+
+int child_fd;
+int child_exit = 0;
+void sig_handler(int signo, siginfo_t *siginfo, void *data)
+{
+ int pid, ret;
+ test_msg("receive signal sig=%d from pid=%d\n", signo, siginfo->si_pid);
+ pid = siginfo->si_pid;
+ ret = write(child_fd, &pid, sizeof(pid));
+ if (ret != sizeof(pid))
+ pr_perror("write");
+ child_exit = 1;
+}
+
+void sig_chld_handler(int signo)
+{
+ test_msg("Receive signal %d\n", signo);
+}
+
+int child(int fd)
+{
+ int ret = 0;
+ struct sigaction act = {
+ .sa_sigaction = sig_handler,
+ .sa_flags = SA_SIGINFO,
+ }, old_act;
+
+ sigemptyset(&act.sa_mask);
+
+ child_fd = fd;
+
+ ret = sigaction(SIGUSR2, &act, &old_act);
+ if (ret < 0) {
+ pr_perror("signal failed");
+ return 1;
+ }
+
+ ret = ptrace(PTRACE_TRACEME, 0, 0, 0);
+ if (ret < 0) {
+ pr_perror("ptrace failed");
+ return 1;
+ }
+ ret = write(child_fd, &ret, sizeof(ret));
+ while (!child_exit)
+ ret = sleep(1);
+ close(child_fd);
+ return 0;
+}
+
+int main(int argc, char ** argv)
+{
+ int ret, status = 0;
+ pid_t pid, spid, cpid;
+ sighandler_t sh;
+ int signal_pipe[2];
+ int child_pipe[2];
+
+ test_init(argc, argv);
+
+ ret = pipe(child_pipe);
+ if (ret < 0) {
+ pr_perror("pipe failed");
+ return 1;
+ }
+
+ cpid = test_fork();
+ if (cpid < 0) {
+ pr_perror("fork failed");
+ return 1;
+ }
+ else if (cpid == 0) {
+ close(child_pipe[0]);
+ return child(child_pipe[1]);
+ }
+
+ close(child_pipe[1]);
+ ret = pipe(signal_pipe);
+ if (ret < 0) {
+ pr_perror("pipe failed");
+ return 1;
+ }
+
+ spid = test_fork();
+ if (spid < 0) {
+ pr_perror("Can't fork signal process");
+ return 1;
+ } else if (spid == 0) {
+ close(signal_pipe[1]);
+ ret = read(signal_pipe[0], &status, sizeof(status));
+ if (ret != sizeof(status)) {
+ pr_perror("read");
+ return 1;
+ }
+ test_msg("send signal to %d\n", cpid);
+ ret = kill(cpid, SIGUSR2);
+ if (ret < 0) {
+ pr_perror("kill failed");
+ }
+ return 0;
+ }
+ close(signal_pipe[0]);
+
+ sh = signal(SIGCHLD, sig_chld_handler);
+ if (sh == SIG_ERR) {
+ pr_perror("signal failed");
+ return 1;
+ }
+
+ test_msg("wait while child initialized");
+ ret = read(child_pipe[0], &status, sizeof(status));
+ if (ret != sizeof(status)) {
+ pr_perror("read from child process failed");
+ return 1;
+ }
+
+ ret = write(signal_pipe[1], &status, sizeof(status));
+ if (ret != sizeof(status)) {
+ pr_perror("write to signal process failed");
+ }
+ close(signal_pipe[1]);
+
+ test_daemon();
+ test_waitsig();
+
+ while (1) {
+ test_msg("waiting...\n");
+ pid = wait(&status);
+ if (pid < 0) {
+ if (errno != ECHILD)
+ pr_perror("wait");
+ break;
+ }
+
+ if (WIFSTOPPED(status)) {
+ siginfo_t siginfo;
+
+ test_msg("pid=%d stopsig=%d\n", pid, WSTOPSIG(status));
+
+ ret = ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo);
+ if (ret < 0) {
+ pr_perror("ptrace failed");
+ return 1;
+ } else
+ test_msg("pid=%d sends signal\n", siginfo.si_pid);
+
+ ret = ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
+ if (ret < 0)
+ pr_perror("ptrace failed");
+
+ ret = read(child_pipe[0], &status, sizeof(status));
+ if (ret != sizeof(status)) {
+ pr_perror("read");
+ return 1;
+ }
+
+ if (spid != siginfo.si_pid)
+ fail("%d!=%d", cpid, siginfo.si_pid);
+ else if (status == siginfo.si_pid)
+ pass();
+ else {
+ fail("%d!=%d", status, siginfo.si_pid);
+ return 1;
+ }
+ }
+ if (WIFEXITED(status)) {
+ test_msg("pid = %d status = %d\n", pid, WEXITSTATUS(status));
+ if (WEXITSTATUS(status))
+ return 1;
+ }
+ if (WIFSTOPPED(status)) {
+ test_msg("pid = %d signal = %d\n", pid, WTERMSIG(status));
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/test/zdtm/static/ptrace_sig.desc b/test/zdtm/static/ptrace_sig.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/ptrace_sig.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/pty00.c b/test/zdtm/static/pty00.c
new file mode 100644
index 000000000..7aa9c8a07
--- /dev/null
+++ b/test/zdtm/static/pty00.c
@@ -0,0 +1,135 @@
+#define _XOPEN_SOURCE
+#include <stdlib.h>
+#include "zdtmtst.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <signal.h>
+
+const char *test_doc = "Check, that pseudoterminals are restored";
+const char *test_author = "Andrey Vagin <avagin@openvz.org>";
+
+static unsigned int nr_sighups;
+
+static void signal_handler_sighup(int signum)
+{
+ nr_sighups++;
+}
+
+int main(int argc, char ** argv)
+{
+ int fdm, fds, ret, tty;
+ char *slavename;
+ char buf[10];
+ const char teststr[] = "hello\n";
+
+ struct sigaction sa = {
+ .sa_handler = signal_handler_sighup,
+ .sa_flags = 0,
+ };
+
+ test_init(argc, argv);
+
+ /*
+ * On closing control terminal we're expecting to
+ * receive SIGHUP, so make sure it's delivered.
+ */
+ if (sigaction(SIGHUP, &sa, 0)) {
+ fail("sigaction failed\n");
+ return 1;
+ }
+
+ fdm = open("/dev/ptmx", O_RDWR);
+ if (fdm == -1) {
+ pr_perror("open(%s) failed", "/dev/ptmx");
+ return 1;
+ }
+ grantpt(fdm);
+ unlockpt(fdm);
+ slavename = ptsname(fdm);
+ fds = open(slavename, O_RDWR);
+ if (fds == -1) {
+ pr_perror("open(%s) failed", slavename);
+ return 1;
+ }
+
+ tty = open("/dev/tty", O_RDWR);
+ if (tty < 0) {
+ pr_perror("open(%s) failed", "/dev/tty");
+ return 1;
+ }
+
+ /* Try to reproduce a deadlock */
+ if (dup2(fdm, 101) != 101) {
+ pr_perror("dup( , 101) failed");
+ return 1;
+ }
+ close(fdm);
+ fdm = 101;
+
+ if (dup2(fds, 100) != 100) {
+ pr_perror("dup( , 100) failed");
+ return 1;
+ }
+ close(fds);
+ fds = 100;
+
+ test_daemon();
+
+ test_waitsig();
+
+ /* Check connectivity */
+ ret = write(fdm, teststr, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("write(fdm) failed");
+ return 1;
+ }
+
+ ret = read(fds, buf, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("read(fds) failed");
+ return 1;
+ }
+
+ if (strncmp(teststr, buf, sizeof(teststr) - 1)) {
+ fail("data mismatch");
+ return 1;
+ }
+
+ ret = write(fdm, teststr, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("write(fdm) failed");
+ return 1;
+ }
+
+ ret = read(tty, buf, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("read(tty) failed");
+ return 1;
+ }
+
+ if (strncmp(teststr, buf, sizeof(teststr) - 1)) {
+ fail("data mismatch");
+ return 1;
+ }
+
+ if (nr_sighups != 0) {
+ fail("Expected 0 SIGHUP before closing control terminal but got %d", nr_sighups);
+ return 1;
+ }
+
+ close(fdm);
+ close(fds);
+ close(tty);
+
+ if (nr_sighups != 1) {
+ fail("Expected 1 SIGHUP after closing control terminal but got %d", nr_sighups);
+ return 1;
+ } else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/pty01.c b/test/zdtm/static/pty01.c
new file mode 100644
index 000000000..46710e280
--- /dev/null
+++ b/test/zdtm/static/pty01.c
@@ -0,0 +1,93 @@
+#define _XOPEN_SOURCE
+#include <stdlib.h>
+#include "zdtmtst.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <signal.h>
+
+const char *test_doc = "Check two pts on ptmx";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+static const char teststr[] = "ping\n";
+
+int main(int argc, char *argv[])
+{
+ char buf[sizeof(teststr)];
+ int master, slave1, slave2, ret;
+ char *slavename;
+
+ test_init(argc, argv);
+
+ master = open("/dev/ptmx", O_RDWR);
+ if (master == -1) {
+ pr_perror("open(%s) failed", "/dev/ptmx");
+ return 1;
+ }
+
+ grantpt(master);
+ unlockpt(master);
+
+ slavename = ptsname(master);
+ slave1 = open(slavename, O_RDWR);
+ if (slave1 == -1) {
+ pr_perror("open(%s) failed", slavename);
+ return 1;
+ }
+
+ slave2 = open(slavename, O_RDWR);
+ if (slave2 == -1) {
+ pr_perror("open(%s) failed", slavename);
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ signal(SIGHUP, SIG_IGN);
+
+ ret = write(master, teststr, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("write(master) failed");
+ return 1;
+ }
+
+ ret = read(slave1, buf, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("read(slave1) failed");
+ return 1;
+ }
+
+ if (strncmp(teststr, buf, sizeof(teststr) - 1)) {
+ fail("data mismatch");
+ return 1;
+ }
+
+ ret = write(master, teststr, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("write(master) failed");
+ return 1;
+ }
+
+ ret = read(slave2, buf, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("read(slave1) failed");
+ return 1;
+ }
+
+ if (strncmp(teststr, buf, sizeof(teststr) - 1)) {
+ fail("data mismatch");
+ return 1;
+ }
+
+ close(master);
+ close(slave1);
+ close(slave2);
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/pty02.c b/test/zdtm/static/pty02.c
new file mode 100644
index 000000000..0074bbd8d
--- /dev/null
+++ b/test/zdtm/static/pty02.c
@@ -0,0 +1,103 @@
+#define _XOPEN_SOURCE
+#include <stdlib.h>
+#include "zdtmtst.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <signal.h>
+
+const char *test_doc = "Check forked master ptmx";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+static const char teststr[] = "ping\n";
+
+#define exit_shot(pid, code) \
+ do { kill(pid, SIGKILL); exit(code); } while (0)
+
+#define exit_shot_parent(code) \
+ exit_shot(getppid(), 1)
+
+int main(int argc, char *argv[])
+{
+ char buf[sizeof(teststr)];
+ int master, slave, ret;
+ char *slavename;
+ task_waiter_t t;
+ pid_t pid;
+
+ test_init(argc, argv);
+
+ master = open("/dev/ptmx", O_RDWR);
+ if (master == -1) {
+ pr_perror("open(%s) failed", "/dev/ptmx");
+ return 1;
+ }
+
+ grantpt(master);
+ unlockpt(master);
+
+ slavename = ptsname(master);
+ slave = open(slavename, O_RDWR);
+ if (slave == -1) {
+ pr_perror("open(%s) failed", slavename);
+ return 1;
+ }
+
+ task_waiter_init(&t);
+
+ pid = test_fork();
+ if (pid == 0) {
+ int new_master, ret;
+
+ new_master = dup(master);
+ if (new_master < 0) {
+ pr_perror("can't dup master");
+ exit_shot_parent(1);
+ }
+
+ task_waiter_complete_current(&t);
+
+ ret = write(new_master, teststr, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("write(new_master) failed (ret = %d)", ret);
+ exit_shot_parent(1);
+ }
+
+ task_waiter_wait4(&t, 1);
+
+ close(new_master);
+ exit(0);
+ } else if (pid < 0) {
+ pr_perror("test_fork failed");
+ exit(1);
+ }
+
+ task_waiter_wait4(&t, pid);
+ close(master);
+
+ test_daemon();
+ test_waitsig();
+
+ signal(SIGHUP, SIG_IGN);
+
+ ret = read(slave, buf, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("read(slave) failed (ret = %d)", ret);
+ return 1;
+ }
+
+ if (strncmp(teststr, buf, sizeof(teststr) - 1)) {
+ fail("data mismatch");
+ return 1;
+ }
+
+ task_waiter_complete(&t, 1);
+ close(slave);
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/pty02.desc b/test/zdtm/static/pty02.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/pty02.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/pty03.c b/test/zdtm/static/pty03.c
new file mode 100644
index 000000000..f60d30d4b
--- /dev/null
+++ b/test/zdtm/static/pty03.c
@@ -0,0 +1,83 @@
+#define _XOPEN_SOURCE
+#include <stdlib.h>
+#include "zdtmtst.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+
+const char *test_doc = "Check a non-opened control terminal";
+const char *test_author = "Andrey Vagin <avagin@openvz.org>";
+
+static const char teststr[] = "ping\n";
+
+int main(int argc, char *argv[])
+{
+ char buf[sizeof(teststr)];
+ int master, slave, ret;
+ char *slavename;
+
+ test_init(argc, argv);
+
+ master = open("/dev/ptmx", O_RDWR);
+ if (master == -1) {
+ pr_perror("open(%s) failed", "/dev/ptmx");
+ return 1;
+ }
+
+ grantpt(master);
+ unlockpt(master);
+
+ slavename = ptsname(master);
+ slave = open(slavename, O_RDWR);
+ if (slave == -1) {
+ pr_perror("open(%s) failed", slavename);
+ return 1;
+ }
+
+ if (ioctl(slave, TIOCSCTTY, 1)) {
+ pr_perror("Can't set a controll terminal");
+ return 1;
+ }
+
+ close(slave);
+
+ test_daemon();
+ test_waitsig();
+
+ slave = open("/dev/tty", O_RDWR);
+ if (slave == -1) {
+ pr_perror("Can't open the controll terminal");
+ return -1;
+ }
+
+ signal(SIGHUP, SIG_IGN);
+
+ ret = write(master, teststr, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("write(master) failed");
+ return 1;
+ }
+
+ ret = read(slave, buf, sizeof(teststr) - 1);
+ if (ret != sizeof(teststr) - 1) {
+ pr_perror("read(slave1) failed");
+ return 1;
+ }
+
+ if (strncmp(teststr, buf, sizeof(teststr) - 1)) {
+ fail("data mismatch");
+ return 1;
+ }
+
+ close(master);
+ close(slave);
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/pty03.desc b/test/zdtm/static/pty03.desc
new file mode 100644
index 000000000..63df42aa6
--- /dev/null
+++ b/test/zdtm/static/pty03.desc
@@ -0,0 +1 @@
+{'flavor': 'h'}
diff --git a/test/zdtm/static/pty04.c b/test/zdtm/static/pty04.c
new file mode 100644
index 000000000..199d2c758
--- /dev/null
+++ b/test/zdtm/static/pty04.c
@@ -0,0 +1,64 @@
+#define _XOPEN_SOURCE
+#include <stdlib.h>
+#include "zdtmtst.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+
+const char *test_doc = "Check two pts with a fake ptmx";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+int main(int argc, char *argv[])
+{
+ int master, slave1, slave2;
+ char *slavename;
+
+ test_init(argc, argv);
+
+ master = open("/dev/ptmx", O_RDWR);
+ if (master == -1) {
+ pr_perror("open(%s) failed", "/dev/ptmx");
+ return 1;
+ }
+
+ grantpt(master);
+ unlockpt(master);
+
+ slavename = ptsname(master);
+
+ slave1 = open(slavename, O_RDWR);
+ if (slave1 == -1) {
+ pr_perror("open(%s) failed", slavename);
+ return 1;
+ }
+
+ slave2 = open(slavename, O_RDWR);
+ if (slave2 == -1) {
+ pr_perror("open(%s) failed", slavename);
+ return 1;
+ }
+
+ if (ioctl(slave1, TIOCSCTTY, 1)) {
+ pr_perror("Can't set a controll terminal");
+ return 1;
+ }
+
+ test_msg("Closing master\n");
+ signal(SIGHUP, SIG_IGN);
+ close(master);
+
+ test_daemon();
+ test_waitsig();
+
+ close(slave1);
+ close(slave2);
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/remap_dead_pid.c b/test/zdtm/static/remap_dead_pid.c
new file mode 100644
index 000000000..00fe3fd16
--- /dev/null
+++ b/test/zdtm/static/remap_dead_pid.c
@@ -0,0 +1,77 @@
+#define _GNU_SOURCE
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+#ifndef CLONE_NEWNS
+#define CLONE_NEWNS 0x00020000
+#endif
+
+const char *test_doc = "Check that dead pid's /proc entries are remapped correctly";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+int main(int argc, char **argv)
+{
+ pid_t pid;
+
+ test_init(argc, argv);
+
+ pid = fork();
+ if (pid < 0) {
+ fail("fork() failed");
+ return -1;
+ }
+
+ if (pid == 0) {
+ test_msg("child is %d\n", pid);
+ /* Child process just sleeps until it is killed. All we need
+ * here is a process to open the mountinfo of. */
+ while(1)
+ sleep(10);
+ } else {
+ int fd, ret;
+ char path[PATH_MAX];
+ pid_t result;
+
+ sprintf(path, "/proc/%d/mountinfo", pid);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fail("failed to open fd");
+ return -1;
+ }
+
+ /* no matter what, we should kill the child */
+ kill(pid, SIGKILL);
+ result = waitpid(pid, NULL, 0);
+ if (result < 0) {
+ fail("failed waitpid()");
+ return -1;
+ }
+
+ if (fd < 0) {
+ fail("failed opening %s", path);
+ return -1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = fcntl(fd, F_GETFD);
+ close(fd);
+
+ if (ret) {
+ fail("bad fd after restore");
+ return -1;
+ }
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/remap_dead_pid.desc b/test/zdtm/static/remap_dead_pid.desc
new file mode 100644
index 000000000..63df42aa6
--- /dev/null
+++ b/test/zdtm/static/remap_dead_pid.desc
@@ -0,0 +1 @@
+{'flavor': 'h'}
diff --git a/test/zdtm/static/remap_dead_pid_root.c b/test/zdtm/static/remap_dead_pid_root.c
new file mode 100644
index 000000000..2a3141216
--- /dev/null
+++ b/test/zdtm/static/remap_dead_pid_root.c
@@ -0,0 +1,72 @@
+#define _GNU_SOURCE
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+#ifndef CLONE_NEWNS
+#define CLONE_NEWNS 0x00020000
+#endif
+
+const char *test_doc = "Check that dead pid's /proc entries are remapped correctly";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+int main(int argc, char **argv)
+{
+ pid_t pid;
+
+ test_init(argc, argv);
+
+ pid = fork();
+ if (pid < 0) {
+ fail("fork() failed");
+ return -1;
+ }
+
+ if (pid == 0) {
+ test_msg("child is %d\n", pid);
+ /* Child process just sleeps until it is killed. All we need
+ * here is a process to open the mountinfo of. */
+ while(1)
+ sleep(10);
+ } else {
+ int fd, ret;
+ char path[PATH_MAX];
+ pid_t result;
+
+ sprintf(path, "/proc/%d", pid);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fail("failed to open fd");
+ return -1;
+ }
+
+ /* no matter what, we should kill the child */
+ kill(pid, SIGKILL);
+ result = waitpid(pid, NULL, 0);
+ if (result < 0) {
+ fail("failed waitpid()");
+ return -1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = fcntl(fd, F_GETFD);
+ close(fd);
+
+ if (ret) {
+ fail("bad fd after restore");
+ return -1;
+ }
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/remap_dead_pid_root.desc b/test/zdtm/static/remap_dead_pid_root.desc
new file mode 100644
index 000000000..63df42aa6
--- /dev/null
+++ b/test/zdtm/static/remap_dead_pid_root.desc
@@ -0,0 +1 @@
+{'flavor': 'h'}
diff --git a/test/zdtm/static/rlimits00.c b/test/zdtm/static/rlimits00.c
new file mode 100644
index 000000000..17ea8daea
--- /dev/null
+++ b/test/zdtm/static/rlimits00.c
@@ -0,0 +1,66 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that rlimits are saved";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+int main(int argc, char **argv)
+{
+ int r, changed = 0;
+ struct rlimit rlims[RLIM_NLIMITS], trlim;
+
+ test_init(argc, argv);
+
+ for (r = 0; r < RLIM_NLIMITS; r++) {
+ if (getrlimit(r, &rlims[r])) {
+ pr_perror("Can't get rlimit");
+ goto out;
+ }
+
+ if (rlims[r].rlim_cur > 1 &&
+ rlims[r].rlim_cur != RLIM_INFINITY) {
+ rlims[r].rlim_cur--;
+
+ if (setrlimit(r, &rlims[r])) {
+ pr_perror("Can't set rlimit");
+ goto out;
+ }
+
+ changed = 1;
+ }
+ }
+
+ if (!changed) {
+ pr_perror("Can't change any rlimir");
+ goto out;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ for (r = 0; r < RLIM_NLIMITS; r++) {
+ if (getrlimit(r, &trlim)) {
+ fail("Can't get rlimit after rst");
+ goto out;
+ }
+
+ if (rlims[r].rlim_cur != trlim.rlim_cur) {
+ fail("Cur changed");
+ goto out;
+ }
+
+ if (rlims[r].rlim_max != trlim.rlim_max) {
+ fail("Max changed");
+ goto out;
+ }
+ }
+
+ pass();
+out:
+ return 0;
+}
+
diff --git a/test/zdtm/static/rmdir_open.c b/test/zdtm/static/rmdir_open.c
new file mode 100644
index 000000000..a4fb5b0f2
--- /dev/null
+++ b/test/zdtm/static/rmdir_open.c
@@ -0,0 +1,70 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that opened removed dir works";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char **argv)
+{
+ int fd;
+ struct stat st;
+
+ test_init(argc, argv);
+
+ if (mkdir(dirname, 0700)) {
+ pr_perror("Can't make dir");
+ goto out;
+ }
+
+ fd = open(dirname, O_DIRECTORY);
+ if (fd < 0) {
+ pr_perror("Can't open dir");
+ goto outr;
+ }
+
+ if (rmdir(dirname)) {
+ pr_perror("Can't remove dir");
+ goto outr;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ /*
+ * We can't compare anything with previous, since
+ * inode _will_ change, so can the device. The only
+ * reasonable thing we can do is check that the fd
+ * still points to some removed directory.
+ */
+ if (fstat(fd, &st)) {
+ fail("Can't stat fd\n");
+ goto out;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ fail("Fd is no longer directory\n");
+ goto out;
+ }
+
+ if (st.st_nlink != 0) {
+ fail("Directory is not removed\n");
+ goto out;
+ }
+
+ pass();
+ return 0;
+
+outr:
+ rmdir(dirname);
+out:
+ return 1;
+}
diff --git a/test/zdtm/static/route_rules b/test/zdtm/static/route_rules
new file mode 100755
index 000000000..ea44537c7
--- /dev/null
+++ b/test/zdtm/static/route_rules
@@ -0,0 +1,74 @@
+#!/bin/bash
+# $Id: route_rules,v 1.1 2007/06/04 12:11:30 agladkov Exp $
+#
+# Copyright (c) 2007 by SWsoft.
+# All rights reserved.
+#
+# Description:
+# check that routes saved after migration
+
+export PATH=$PATH:${0%/*}/../../lib
+
+die()
+{
+ echo "$0:${BASH_LINENO[0]}: $*" >&2
+ exit 1
+}
+
+fail()
+{
+ echo "FAIL: $0:${BASH_LINENO[0]}: $*" > "$outfile"
+ exit 1
+}
+
+do_or_fail()
+{
+ local failmsg="$1" output
+ shift
+ output="$(eval $@ 2>&1)" ||
+ fail "$failmsg: $output"
+}
+
+do_start()
+{
+ [ -f "$statefile" ] && die "state file $statefile aleady exists"
+
+ # Get default route
+ dev_name=`ip route list match 0.0.0.0/0 | sed 's/.*dev \([^ ]*\).*/\1/'`
+ [ -n "$dev_name" ] || fail "dev_name is zero: " \
+ "\$dev_name=\`ip route list match 0.0.0.0/0 | " \
+ "sed 's/.*dev \([^ ]*\).*/\1/'"
+ do_or_fail "can't add routes" \
+ ip r a 1.2.3.4/32 dev $dev_name && ip r a 1.2.0.0/16 via 1.2.3.4
+
+ do_or_fail "can't list created routes" \
+ ip r \| grep "1.2.3.4" \> "$statefile"
+}
+
+do_stop()
+{
+ do_or_fail "can't compare the routes" \
+ ip r \| grep "1.2.3.4" \| diff -u "$statefile" -
+
+ rm -f "$statefile"
+ IFS="
+ ";
+ for line in `ip r | grep "1.2.3.4"`; do
+ eval ip r del $line
+ done
+
+ echo "PASS" > $outfile
+}
+
+
+tmpargs="$(parseargs.sh --name=$0 \
+ --flags-req=statefile,outfile \
+ -- "$@")" ||
+ die "can't parse command line"
+eval "$tmpargs"
+
+[ -f "$outfile" ] && die "out file $outfile aleady exists"
+
+# expect "start" or "stop"
+action=${1:?Specify action$(die 'Specify action')}
+do_$action
diff --git a/test/zdtm/static/rtc.c b/test/zdtm/static/rtc.c
new file mode 100644
index 000000000..e082edc31
--- /dev/null
+++ b/test/zdtm/static/rtc.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <linux/rtc.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+#define TEST_HZ 4
+#define NR_FAILS 10
+
+int main(int argc, char **argv)
+{
+ unsigned long data, delta;
+ int fd, fail = NR_FAILS, to_pass = NR_FAILS;
+ struct timeval start, end;
+
+ test_init(argc, argv);
+
+ fd = open("/dev/rtc", O_RDWR);
+ if (fd < 0) {
+ pr_perror("open");
+ return 1;
+ }
+
+ if (ioctl(fd, RTC_IRQP_SET, TEST_HZ) == -1) {
+ pr_perror("RTC_IRQP_SET");
+ return 1;
+ }
+
+ if (ioctl(fd, RTC_PIE_ON, 0) == -1) {
+ pr_perror("RTC_PIE_ON");
+ return 1;
+ }
+
+ test_daemon();
+
+ gettimeofday(&start, NULL);
+ start.tv_usec += start.tv_sec * 1000000;
+ while (test_go() || to_pass--) {
+ if (read(fd, &data, sizeof(unsigned long)) == -1)
+ return 1;
+ gettimeofday(&end, NULL);
+ end.tv_usec += end.tv_sec * 1000000;
+ delta = end.tv_usec - start.tv_usec;
+ if (labs(delta - 1000000 / TEST_HZ ) > 100000) {
+ pr_perror("delta = %ld", delta);
+ fail--;
+ if (fail == 0)
+ return 1;
+ }
+ start = end;
+ }
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/rtc.desc b/test/zdtm/static/rtc.desc
new file mode 100644
index 000000000..80094be09
--- /dev/null
+++ b/test/zdtm/static/rtc.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'flags': 'suid crlib'}
diff --git a/test/zdtm/static/sched_policy00.c b/test/zdtm/static/sched_policy00.c
new file mode 100644
index 000000000..9fe89a1a1
--- /dev/null
+++ b/test/zdtm/static/sched_policy00.c
@@ -0,0 +1,63 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <sched.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check sched policy to be preserved";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+static const int parm = 3;
+
+static int do_nothing(void)
+{
+ while (1)
+ sleep(10);
+
+ return -1;
+}
+
+int main(int argc, char ** argv)
+{
+ int pid, ret, err = 0;
+ struct sched_param p;
+
+ test_init(argc, argv);
+
+ pid = fork();
+ if (!pid)
+ return do_nothing();
+
+ p.sched_priority = parm;
+ if (sched_setscheduler(pid, SCHED_RR, &p)) {
+ pr_perror("Can't set policy");
+ kill(pid, SIGKILL);
+ return -1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = sched_getscheduler(pid);
+ if (ret != SCHED_RR) {
+ fail("Broken/No policy");
+ err++;
+ }
+
+ ret = sched_getparam(pid, &p);
+ if (ret < 0 || p.sched_priority != parm) {
+ fail("Broken prio");
+ err++;
+ }
+
+ if (!err)
+ pass();
+
+ kill(pid, SIGKILL);
+ return err;
+}
diff --git a/test/zdtm/static/sched_policy00.desc b/test/zdtm/static/sched_policy00.desc
new file mode 100644
index 000000000..d969725f6
--- /dev/null
+++ b/test/zdtm/static/sched_policy00.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/sched_prio00.c b/test/zdtm/static/sched_prio00.c
new file mode 100644
index 000000000..ea4a2ee13
--- /dev/null
+++ b/test/zdtm/static/sched_prio00.c
@@ -0,0 +1,79 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <signal.h>
+#include <string.h>
+#include <sched.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check sched prios to be preserved";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+#define NRTASKS 3
+
+static int do_nothing(void)
+{
+ while (1)
+ sleep(10);
+
+ return -1;
+}
+
+static void kill_all(int *pid, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ kill(pid[i], SIGKILL);
+}
+
+int main(int argc, char ** argv)
+{
+ int pid[NRTASKS], i, parm[NRTASKS], ret;
+
+ test_init(argc, argv);
+
+ parm[0] = -20;
+ parm[1] = 19;
+ parm[2] = 1;
+
+ for (i = 0; i < NRTASKS; i++) {
+ pid[i] = fork();
+ if (!pid[i])
+ return do_nothing();
+
+ if (setpriority(PRIO_PROCESS, pid[i], parm[i])) {
+ pr_perror("Can't set prio %d", i);
+ kill_all(pid, i);
+ return -1;
+ }
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ for (i = 0; i < NRTASKS; i++) {
+ errno = 0;
+ ret = getpriority(PRIO_PROCESS, pid[i]);
+ if (errno) {
+ fail("No prio for task %d", i);
+ break;
+ }
+
+ if (ret != parm[i]) {
+ fail("Broken nice for %d", i);
+ break;
+ }
+ }
+
+ if (i == NRTASKS)
+ pass();
+
+ kill_all(pid, NRTASKS);
+ return 0;
+}
diff --git a/test/zdtm/static/sched_prio00.desc b/test/zdtm/static/sched_prio00.desc
new file mode 100644
index 000000000..d969725f6
--- /dev/null
+++ b/test/zdtm/static/sched_prio00.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/seccomp_filter.c b/test/zdtm/static/seccomp_filter.c
new file mode 100644
index 000000000..1a1f34302
--- /dev/null
+++ b/test/zdtm/static/seccomp_filter.c
@@ -0,0 +1,187 @@
+#include <unistd.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <stddef.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <linux/seccomp.h>
+#include <linux/filter.h>
+#include <linux/limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that SECCOMP_MODE_FILTER is restored";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+int get_seccomp_mode(pid_t pid)
+{
+ FILE *f;
+ char buf[PATH_MAX];
+
+ sprintf(buf, "/proc/%d/status", pid);
+ f = fopen(buf, "r+");
+ if (!f) {
+ pr_perror("fopen failed");
+ return -1;
+ }
+
+ while (NULL != fgets(buf, sizeof(buf), f)) {
+ int mode;
+
+ if (sscanf(buf, "Seccomp:\t%d", &mode) != 1)
+ continue;
+
+ fclose(f);
+ return mode;
+ }
+ fclose(f);
+
+ return -1;
+}
+
+int filter_syscall(int syscall_nr)
+{
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+ };
+
+ struct sock_fprog bpf_prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bpf_prog) < 0) {
+ pr_perror("prctl failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char ** argv)
+{
+ pid_t pid;
+ int mode, status;
+ int sk_pair[2], sk, ret;
+ char c = 'K';
+
+ test_init(argc, argv);
+
+ if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair)) {
+ pr_perror("socketpair");
+ return -1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork");
+ return -1;
+ }
+
+ if (pid == 0) {
+
+ sk = sk_pair[1];
+ close(sk_pair[0]);
+
+ /*
+ * Let's install a few filters separately to make sure the
+ * chaining actually works.
+ */
+ if (filter_syscall(__NR_ptrace) < 0)
+ _exit(1);
+
+ /*
+ * The idea is to have a syscall that is used in restore_creds,
+ * so we can make sure seccomp is actually suspended when that
+ * is called.
+ */
+ if (filter_syscall(__NR_setresuid) < 0)
+ _exit(1);
+
+ setuid(1000);
+
+ zdtm_seccomp = 1;
+ test_msg("SECCOMP_MODE_FILTER is enabled\n");
+
+ if (write(sk, &c, 1) != 1) {
+ pr_perror("write");
+ _exit(1);
+ }
+
+ if (read(sk, &c, 1) != 1) {
+ pr_perror("read");
+ _exit(1);
+ }
+
+ prctl(PR_SET_DUMPABLE, 1);
+
+ if (write(sk, &c, 1) != 1) {
+ pr_perror("write");
+ _exit(1);
+ }
+
+ if (read(sk, &c, 1) != 1) {
+ pr_perror("read");
+ _exit(1);
+ }
+
+ /* We expect to be killed by our policy above. */
+ ptrace(PTRACE_TRACEME);
+
+ syscall(__NR_exit, 0);
+ }
+
+ sk = sk_pair[0];
+ close(sk_pair[1]);
+
+ if ((ret = read(sk, &c, 1)) != 1) {
+ pr_perror("read %d", ret);
+ goto err;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (write(sk, &c, 1) != 1) {
+ pr_perror("write");
+ goto err;
+ }
+ if ((ret = read(sk, &c, 1)) != 1) {
+ pr_perror("read %d", ret);
+ goto err;
+ }
+
+ mode = get_seccomp_mode(pid);
+ if (write(sk, &c, 1) != 1) {
+ pr_perror("write");
+ goto err;
+ }
+ if (waitpid(pid, &status, 0) != pid) {
+ pr_perror("waitpid");
+ exit(1);
+ }
+
+ if (WTERMSIG(status) != SIGSYS) {
+ pr_perror("expected SIGSYS, got %d\n", WTERMSIG(status));
+ exit(1);
+ }
+
+ if (mode != SECCOMP_MODE_FILTER) {
+ fail("seccomp mode mismatch %d\n", mode);
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+err:
+ kill(pid, SIGKILL);
+ return 1;
+}
diff --git a/test/zdtm/static/seccomp_filter.desc b/test/zdtm/static/seccomp_filter.desc
new file mode 100644
index 000000000..14dd96184
--- /dev/null
+++ b/test/zdtm/static/seccomp_filter.desc
@@ -0,0 +1 @@
+{'flags': 'suid', 'feature': 'seccomp_filters'}
diff --git a/test/zdtm/static/seccomp_filter_inheritance.c b/test/zdtm/static/seccomp_filter_inheritance.c
new file mode 100644
index 000000000..ff0c46a7d
--- /dev/null
+++ b/test/zdtm/static/seccomp_filter_inheritance.c
@@ -0,0 +1,176 @@
+#include <unistd.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <stddef.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <linux/seccomp.h>
+#include <linux/filter.h>
+#include <linux/limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that SECCOMP_MODE_FILTER is restored";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+int get_seccomp_mode(pid_t pid)
+{
+ FILE *f;
+ char buf[PATH_MAX];
+
+ sprintf(buf, "/proc/%d/status", pid);
+ f = fopen(buf, "r+");
+ if (!f) {
+ pr_perror("fopen failed");
+ return -1;
+ }
+
+ while (NULL != fgets(buf, sizeof(buf), f)) {
+ int mode;
+
+ if (sscanf(buf, "Seccomp:\t%d", &mode) != 1)
+ continue;
+
+ fclose(f);
+ return mode;
+ }
+ fclose(f);
+
+ return -1;
+}
+
+int filter_syscall(int syscall_nr)
+{
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+ };
+
+ struct sock_fprog bpf_prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bpf_prog) < 0) {
+ pr_perror("prctl failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char ** argv)
+{
+ pid_t pid;
+ int mode, status;
+ int sk_pair[2], sk, ret;
+ char c = 'K';
+
+ test_init(argc, argv);
+
+ if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair)) {
+ pr_perror("socketpair");
+ return -1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork");
+ return -1;
+ }
+
+ if (pid == 0) {
+
+ pid_t pid2;
+
+ sk = sk_pair[1];
+ close(sk_pair[0]);
+
+ if (filter_syscall(__NR_ptrace) < 0)
+ _exit(1);
+
+ if (filter_syscall(__NR_fstat) < 0)
+ _exit(1);
+
+ zdtm_seccomp = 1;
+ test_msg("SECCOMP_MODE_FILTER is enabled\n");
+
+ pid2 = fork();
+ if (pid2 < 0)
+ _exit(1);
+
+ if (!pid2) {
+
+ if (write(sk, &c, 1) != 1) {
+ pr_perror("write");
+ _exit(1);
+ }
+
+ if (read(sk, &c, 1) != 1) {
+ pr_perror("read");
+ _exit(1);
+ }
+
+ /* We expect to be killed by our policy above. */
+ ptrace(PTRACE_TRACEME);
+ _exit(1);
+ }
+
+ if (waitpid(pid2, &status, 0) != pid2) {
+ pr_perror("waitpid");
+ _exit(1);
+ }
+
+ if (WTERMSIG(status) != SIGSYS) {
+ pr_perror("expected SIGSYS, got %d\n", WTERMSIG(status));
+ _exit(1);
+ }
+
+ _exit(0);
+ }
+
+ sk = sk_pair[0];
+ close(sk_pair[1]);
+
+ if ((ret = read(sk, &c, 1)) != 1) {
+ pr_perror("read %d", ret);
+ goto err;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ mode = get_seccomp_mode(pid);
+ if (write(sk, &c, 1) != 1) {
+ pr_perror("write");
+ goto err;
+ }
+
+ if (mode != SECCOMP_MODE_FILTER) {
+ fail("seccomp mode mismatch %d\n", mode);
+ return 1;
+ }
+
+ if (waitpid(pid, &status, 0) != pid) {
+ pr_perror("waitpid");
+ _exit(1);
+ }
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ fail("bad exit status");
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+err:
+ kill(pid, SIGKILL);
+ return 1;
+}
diff --git a/test/zdtm/static/seccomp_filter_inheritance.desc b/test/zdtm/static/seccomp_filter_inheritance.desc
new file mode 100644
index 000000000..14dd96184
--- /dev/null
+++ b/test/zdtm/static/seccomp_filter_inheritance.desc
@@ -0,0 +1 @@
+{'flags': 'suid', 'feature': 'seccomp_filters'}
diff --git a/test/zdtm/static/seccomp_filter_tsync.c b/test/zdtm/static/seccomp_filter_tsync.c
new file mode 100644
index 000000000..a5835498f
--- /dev/null
+++ b/test/zdtm/static/seccomp_filter_tsync.c
@@ -0,0 +1,202 @@
+#include <unistd.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <stddef.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <linux/seccomp.h>
+#include <linux/filter.h>
+#include <linux/limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include <pthread.h>
+#include "zdtmtst.h"
+
+#ifndef SECCOMP_SET_MODE_FILTER
+#define SECCOMP_SET_MODE_FILTER 1
+#endif
+
+#ifndef SECCOMP_FILTER_FLAG_TSYNC
+#define SECCOMP_FILTER_FLAG_TSYNC 1
+#endif
+
+const char *test_doc = "Check that SECCOMP_FILTER_FLAG_TSYNC works correctly after restore";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+pthread_mutex_t getpid_wait;
+
+int get_seccomp_mode(pid_t pid)
+{
+ FILE *f;
+ char buf[PATH_MAX];
+
+ sprintf(buf, "/proc/%d/status", pid);
+ f = fopen(buf, "r+");
+ if (!f) {
+ pr_perror("fopen failed");
+ return -1;
+ }
+
+ while (NULL != fgets(buf, sizeof(buf), f)) {
+ int mode;
+
+ if (sscanf(buf, "Seccomp:\t%d", &mode) != 1)
+ continue;
+
+ fclose(f);
+ return mode;
+ }
+ fclose(f);
+
+ return -1;
+}
+
+int filter_syscall(int syscall_nr, unsigned int flags)
+{
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+ };
+
+ struct sock_fprog bpf_prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, flags, &bpf_prog) < 0) {
+ pr_perror("seccomp failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+void *wait_and_getpid(void *arg)
+{
+ pthread_mutex_lock(&getpid_wait);
+ pthread_mutex_unlock(&getpid_wait);
+ pthread_mutex_destroy(&getpid_wait);
+
+ /* we expect the tg to get killed by the seccomp filter that was
+ * installed via TSYNC */
+ ptrace(PTRACE_TRACEME);
+ pthread_exit((void *)1);
+}
+
+int main(int argc, char ** argv)
+{
+ pid_t pid;
+ int mode, status;
+ int sk_pair[2], sk, ret;
+ char c = 'K';
+
+ test_init(argc, argv);
+
+ if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair)) {
+ pr_perror("socketpair");
+ return -1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork");
+ return -1;
+ }
+
+ if (pid == 0) {
+ pthread_t th;
+ void *p = NULL;
+
+ if (pthread_mutex_init(&getpid_wait, NULL)) {
+ pr_perror("pthread_mutex_init");
+ _exit(1);
+ }
+
+ sk = sk_pair[1];
+ close(sk_pair[0]);
+
+ if (filter_syscall(__NR_getpid, 0) < 0)
+ _exit(1);
+
+ zdtm_seccomp = 1;
+
+ pthread_mutex_lock(&getpid_wait);
+ pthread_create(&th, NULL, wait_and_getpid, NULL);
+
+ test_msg("SECCOMP_MODE_FILTER is enabled\n");
+
+ if (write(sk, &c, 1) != 1) {
+ pr_perror("write");
+ _exit(1);
+ }
+
+ if (read(sk, &c, 1) != 1) {
+ pr_perror("read");
+ _exit(1);
+ }
+
+ /* Now we have c/r'd with a shared filter, so let's install
+ * another filter with TSYNC and make sure that it is
+ * inherited.
+ */
+ if (filter_syscall(__NR_ptrace, SECCOMP_FILTER_FLAG_TSYNC) < 0)
+ _exit(1);
+
+ pthread_mutex_unlock(&getpid_wait);
+ if (pthread_join(th, &p) != 0) {
+ pr_perror("pthread_join");
+ exit(1);
+ }
+
+ /* Here we're abusing pthread exit slightly: if the thread gets
+ * to call pthread_exit, the value of p is one, but if it gets
+ * killed pthread_join doesn't set a value since the thread
+ * didn't, so the value is null; we exit 0 to indicate success
+ * as usual.
+ */
+ syscall(__NR_exit, p);
+ }
+
+ sk = sk_pair[0];
+ close(sk_pair[1]);
+
+ if ((ret = read(sk, &c, 1)) != 1) {
+ pr_perror("read %d", ret);
+ goto err;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ mode = get_seccomp_mode(pid);
+ if (write(sk, &c, 1) != 1) {
+ pr_perror("write");
+ goto err;
+ }
+ if (waitpid(pid, &status, 0) != pid) {
+ pr_perror("waitpid");
+ exit(1);
+ }
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ pr_perror("expected 0 exit, got %d\n", WEXITSTATUS(status));
+ exit(1);
+ }
+
+ if (mode != SECCOMP_MODE_FILTER) {
+ fail("seccomp mode mismatch %d\n", mode);
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+err:
+ kill(pid, SIGKILL);
+ return 1;
+}
diff --git a/test/zdtm/static/seccomp_filter_tsync.desc b/test/zdtm/static/seccomp_filter_tsync.desc
new file mode 100644
index 000000000..14dd96184
--- /dev/null
+++ b/test/zdtm/static/seccomp_filter_tsync.desc
@@ -0,0 +1 @@
+{'flags': 'suid', 'feature': 'seccomp_filters'}
diff --git a/test/zdtm/static/seccomp_strict.c b/test/zdtm/static/seccomp_strict.c
new file mode 100644
index 000000000..8b1a37ac5
--- /dev/null
+++ b/test/zdtm/static/seccomp_strict.c
@@ -0,0 +1,122 @@
+#include <unistd.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <linux/seccomp.h>
+#include <linux/limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that SECCOMP_MODE_STRICT is restored";
+const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
+
+int get_seccomp_mode(pid_t pid)
+{
+ FILE *f;
+ char buf[PATH_MAX];
+
+ sprintf(buf, "/proc/%d/status", pid);
+ f = fopen(buf, "r+");
+ if (!f) {
+ pr_perror("fopen failed");
+ return -1;
+ }
+
+ while (NULL != fgets(buf, sizeof(buf), f)) {
+ int mode;
+
+ if (sscanf(buf, "Seccomp:\t%d", &mode) != 1)
+ continue;
+
+ fclose(f);
+ return mode;
+ }
+ fclose(f);
+
+ return -1;
+}
+
+int main(int argc, char ** argv)
+{
+ pid_t pid;
+ int mode, status;
+ int sk_pair[2], sk;
+ char c = 'K';
+
+ test_init(argc, argv);
+
+ if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair)) {
+ pr_perror("socketpair");
+ return -1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork");
+ return -1;
+ }
+
+ if (pid == 0) {
+ sk = sk_pair[1];
+ close(sk_pair[0]);
+ zdtm_seccomp = 1;
+
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT) < 0) {
+ pr_perror("prctl failed");
+ return -1;
+ }
+ test_msg("SECCOMP_MODE_STRICT is enabled\n");
+
+ if (write(sk, &c, 1) != 1) {
+ pr_perror("write");
+ return -1;
+ }
+ if (read(sk, &c, 1) != 1) {
+ _exit(1);
+ pr_perror("read");
+ return -1;
+ }
+
+ syscall(__NR_exit, 0);
+ }
+
+ sk = sk_pair[0];
+ close(sk_pair[1]);
+
+ if (read(sk, &c, 1) != 1) {
+ pr_perror("read");
+ goto err;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ mode = get_seccomp_mode(pid);
+ if (write(sk, &c, 1) != 1) {
+ pr_perror("write");
+ goto err;
+ }
+ if (waitpid(pid, &status, 0) != pid) {
+ pr_perror("waitpid");
+ exit(1);
+ }
+ if (status != 0) {
+ pr_perror("The child exited with an unexpected code %d", status);
+ exit(1);
+ }
+ if (mode != SECCOMP_MODE_STRICT) {
+ fail("seccomp mode mismatch %d\n", mode);
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+err:
+ kill(pid, SIGKILL);
+ return 1;
+}
diff --git a/test/zdtm/static/seccomp_strict.desc b/test/zdtm/static/seccomp_strict.desc
new file mode 100644
index 000000000..d73e109d8
--- /dev/null
+++ b/test/zdtm/static/seccomp_strict.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'flags': 'suid', 'feature': 'seccomp_suspend'}
diff --git a/test/zdtm/static/selfexe00.c b/test/zdtm/static/selfexe00.c
new file mode 100644
index 000000000..bc6117248
--- /dev/null
+++ b/test/zdtm/static/selfexe00.c
@@ -0,0 +1,60 @@
+/*
+ * A simple testee program with threads
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "zdtmtst.h"
+
+#define gettid() pthread_self()
+
+const char *test_doc = "Check if /proc/self/exe points to same location after restore\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org";
+
+int main(int argc, char *argv[])
+{
+ char path_before[PATH_MAX];
+ char path_after[PATH_MAX];
+ int ret;
+
+ test_init(argc, argv);
+
+ test_msg("%s pid %d\n", argv[0], getpid());
+ ret = readlink("/proc/self/exe", path_before, sizeof(path_before) - 1);
+ if (ret < 0) {
+ pr_perror("Can't read selflink");
+ fail();
+ exit(1);
+ }
+ path_before[ret] = 0;
+ test_msg("%s\n", path_before);
+
+ test_daemon();
+ test_waitsig();
+
+ ret = readlink("/proc/self/exe", path_after, sizeof(path_after) - 1);
+ if (ret < 0) {
+ pr_perror("Can't read selflink");
+ fail();
+ exit(1);
+ }
+ path_after[ret] = 0;
+ test_msg("%s\n", path_after);
+
+ if (!strcmp(path_before, path_after))
+ pass();
+ else
+ fail();
+
+ return 0;
+}
diff --git a/test/zdtm/static/sem.c b/test/zdtm/static/sem.c
new file mode 100644
index 000000000..a06649de2
--- /dev/null
+++ b/test/zdtm/static/sem.c
@@ -0,0 +1,181 @@
+#define _GNU_SOURCE
+#include <sched.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc="Tests IPC semaphores migrates fine";
+const char *test_author="Stanislav Kinsbursky <skinsbursky@parallels.com>";
+
+static int sem_test(int id,
+ struct sembuf *lock, struct sembuf *unlock,
+ int lock_ops, int unlock_ops)
+{
+ if (semop(id, lock, lock_ops) == -1) {
+ fail("Failed to lock semaphore");
+ return -errno;
+ }
+ if (semop(id, unlock, unlock_ops) == -1) {
+ fail("Failed to unlock semaphore");
+ return -errno;
+ }
+ return 0;
+}
+
+#define NSEMS 10
+
+static int check_sem_by_key(int key, int num)
+{
+ int id;
+ struct sembuf lock[2] = {
+ {
+ .sem_num = num,
+ .sem_op = 0,
+ .sem_flg = 0,
+ },
+ {
+ .sem_num = num,
+ .sem_op = 1,
+ .sem_flg = 0,
+ },
+ };
+ struct sembuf unlock[1] = {
+ {
+ .sem_num = num,
+ .sem_op = -1,
+ .sem_flg = 0,
+ }
+ };
+ int val;
+
+ id = semget(key, NSEMS, 0777);
+ if (id == -1) {
+ fail("Can't get sem");
+ return -errno;
+ }
+
+ val = semctl(id, num, GETVAL);
+ if (val < 0) {
+ fail("Failed to get sem value");
+ return -errno;
+ }
+
+ return sem_test(id, lock, unlock,
+ sizeof(lock)/sizeof(struct sembuf),
+ sizeof(unlock)/sizeof(struct sembuf));
+}
+
+static int check_sem_by_id(int id, int num, int val)
+{
+ int curr;
+ struct sembuf lock[] = {
+ {
+ .sem_num = num,
+ .sem_op = val,
+ .sem_flg = 0,
+ },
+ };
+ struct sembuf unlock[] = {
+ {
+ .sem_num = num,
+ .sem_op = - val * 2,
+ .sem_flg = 0,
+ }
+ };
+
+ curr = semctl(id, num, GETVAL);
+ if (curr < 0) {
+ fail("Failed to get sem value");
+ return -errno;
+ }
+ if (curr != val) {
+ fail("Sem has wrong value: %d instead of %d\n", curr, val);
+ return -EFAULT;
+ }
+ return sem_test(id, lock, unlock,
+ sizeof(lock)/sizeof(struct sembuf),
+ sizeof(unlock)/sizeof(struct sembuf));
+}
+
+int main(int argc, char **argv)
+{
+ int id, key;
+ int i;
+ int val[NSEMS];
+ int ret, fail_count = 0;
+
+ test_init(argc, argv);
+
+ key = ftok(argv[0], 89063453);
+ if (key == -1) {
+ pr_perror("Can't make key");
+ return -1;
+ }
+
+ id = semget(key, NSEMS, 0777 | IPC_CREAT | IPC_EXCL);
+ if (id == -1) {
+ fail_count++;
+ pr_perror("Can't get sem array");
+ goto out;
+ }
+
+ for (i = 0; i < NSEMS; i++) {
+ val[i] = lrand48() & 0x7;
+
+ if (semctl(id, i, SETVAL, val[i]) == -1) {
+ fail_count++;
+ pr_perror("Can't init sem %d", i);
+ goto out_destroy;
+ }
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ for (i = 0; i < NSEMS; i++) {
+ ret = check_sem_by_id(id, i, val[i]);
+ if (ret < 0) {
+ fail_count++;
+ fail("Check sem %d by id failed", i);
+ goto out_destroy;
+ }
+
+ if (check_sem_by_key(key, i) < 0) {
+ fail("Check sem %d by key failed", i);
+ fail_count++;
+ goto out_destroy;
+ }
+
+ val[i] = semctl(id, 0, GETVAL);
+ if (val[i] < 0) {
+ fail("Failed to get sem %d value", i);
+ fail_count++;
+ goto out_destroy;
+ }
+ if (val[i] != 0) {
+ fail("Non-zero sem %d value: %d", val);
+ fail_count++;
+ }
+ }
+
+out_destroy:
+ ret = semctl(id, 0, IPC_RMID);
+ if (ret < 0) {
+ fail("Destroy sem array failed");
+ fail_count++;
+ }
+out:
+ if (fail_count == 0)
+ pass();
+ return fail_count;
+}
diff --git a/test/zdtm/static/sem.desc b/test/zdtm/static/sem.desc
new file mode 100644
index 000000000..6c4afe5f0
--- /dev/null
+++ b/test/zdtm/static/sem.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns'}
diff --git a/test/zdtm/static/session00.c b/test/zdtm/static/session00.c
new file mode 100644
index 000000000..bc8f00940
--- /dev/null
+++ b/test/zdtm/static/session00.c
@@ -0,0 +1,237 @@
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test that sid, pgid are restored";
+const char *test_author = "Andrey Vagin <avagin@openvz.org>";
+
+#define DETACH 1
+#define NEWSID 2
+#define CHANGESID 4
+#define DOUBLE_CHANGESID 8
+
+struct testcase {
+ int flags;
+ pid_t pid;
+ pid_t sid;
+};
+
+static struct testcase testcases[] = {
+ {DETACH, },
+ {NEWSID, },
+ {0, },
+ {DETACH|NEWSID, },
+ {CHANGESID, },
+ {DOUBLE_CHANGESID | CHANGESID, }
+ };
+/*
+ 2 2 session00
+ 4 4 \_ session00 # {NEWSID, },
+ 2 5 \_ session00 # {0, },
+ 8 8 \_ session00
+ 2 9 | \_ session00 # {CHANGESID, }
+ 10 10 \_ session00
+ 11 11 \_ session00
+ 2 12 \_ session00 # {DOUBLE_CHANGESID | CHANGESID, }
+ 2 3 session00 # {DETACH, },
+ 6 7 session00 # {DETACH|NEWSID, },
+*/
+
+#define NUM_CASES (sizeof(testcases) / sizeof(struct testcase))
+
+static int fork_child(int i)
+{
+ int p[2];
+ int status, ret;
+ pid_t pid, sid;
+
+ ret = pipe(p);
+ if (ret) {
+ pr_perror("pipe() failed");
+ return 1;
+ }
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("Can't fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ if (testcases[i].flags & NEWSID) {
+ sid = setsid();
+ if (sid == -1) {
+ pr_perror("setsid failed");
+ write(p[1], &sid, sizeof(sid));
+ exit(1);
+ }
+ }
+
+ if (testcases[i].flags & (DETACH | CHANGESID)) {
+ pid = test_fork();
+ if (pid < 0) {
+ write(p[1], &pid, sizeof(pid));
+ exit(1);
+ }
+ }
+
+ if (pid != 0) {
+ if (!(testcases[i].flags & CHANGESID))
+ exit(0);
+
+ sid = setsid();
+ if (sid == -1) {
+ pr_perror("setsid failed");
+ write(p[1], &sid, sizeof(sid));
+ exit(1);
+ }
+
+ close(p[1]);
+ wait(NULL);
+ if (getsid(getpid()) != sid) {
+ fail("The process %d (%x) has SID=%d (expected %d)",
+ pid, testcases[i].flags, sid, testcases[i].sid);
+ exit(1);
+ }
+ exit(0);
+ }
+
+ if (testcases[i].flags & DOUBLE_CHANGESID) {
+ pid = fork();
+ if (pid < 0) {
+ write(p[1], &pid, sizeof(pid));
+ exit(1);
+ }
+
+ if (pid == 0)
+ goto child;
+
+ sid = setsid();
+ if (sid == -1) {
+ pr_perror("setsid failed");
+ write(p[1], &sid, sizeof(sid));
+ exit(1);
+ }
+
+ close(p[1]);
+ wait(NULL);
+ if (getsid(getpid()) != sid) {
+ fail("The process %d (%x) has SID=%d (expected %d)",
+ pid, testcases[i].flags, sid, testcases[i].sid);
+ exit(1);
+ }
+ exit(0);
+ }
+
+child:
+ pid = getpid();
+ write(p[1], &pid, sizeof(pid));
+ close(p[1]);
+
+ test_waitsig();
+ pass();
+ exit(0);
+ }
+
+ close(p[1]);
+
+ if (testcases[i].flags & DETACH) {
+ pid_t ret;
+ ret = wait(&status);
+ if (ret != pid) {
+ pr_perror("wait return %d instead of %d", ret, pid);
+ kill(pid, SIGKILL);
+ return 1;
+ }
+ }
+
+ ret = read(p[0], &testcases[i].pid, sizeof(pid));
+ if (ret != sizeof(ret)) {
+ pr_perror("read failed");
+ return 1;
+ }
+ /* wait when a child closes fd */
+ ret = read(p[0], &testcases[i].pid, sizeof(pid));
+ if (ret != 0) {
+ pr_perror("read failed");
+ return 1;
+ }
+
+ close(p[0]);
+
+ if (testcases[i].pid < 0) {
+ pr_perror("child failed");
+ return 1;
+ }
+
+ testcases[i].sid = getsid(testcases[i].pid);
+
+ return 0;
+}
+
+int main(int argc, char ** argv)
+{
+ int i, ret, err = 0, status;
+ pid_t pid;
+
+ test_init(argc, argv);
+
+ for (i = 0; i < NUM_CASES; i++)
+ if (fork_child(i))
+ break;
+
+ if (i != NUM_CASES) {
+ int j;
+ for (j = 0; j < i; j++)
+ kill(testcases[j].pid, SIGTERM);
+ return 1;
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ for (i = 0; i < NUM_CASES; i++) {
+ pid_t pid = testcases[i].pid;
+ pid_t sid = getsid(pid);
+
+ if (sid != testcases[i].sid) {
+ fail("The process %d (%x) has SID=%d (expected %d)",
+ pid, testcases[i].flags, sid, testcases[i].sid);
+ err++;
+ }
+
+ ret = kill(pid, SIGKILL);
+ if (ret == -1) {
+ pr_perror("kill failed");
+ err++;
+ }
+ waitpid(pid, NULL, 0);
+
+ if (testcases[i].flags & CHANGESID) {
+ pid = wait(&status);
+ if (pid == -1) {
+ pr_perror("wait() failed");
+ err++;
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+ fail("The process with pid %d returns %d\n", pid, status);
+ err++;
+ }
+ }
+ }
+
+ pid = wait(&status);
+ if (pid != -1 || errno != ECHILD) {
+ pr_perror("%d isn't waited");
+ err++;
+ }
+
+ if (!err)
+ pass();
+
+ return err > 0;
+}
diff --git a/test/zdtm/static/session00.desc b/test/zdtm/static/session00.desc
new file mode 100644
index 000000000..6c4afe5f0
--- /dev/null
+++ b/test/zdtm/static/session00.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns'}
diff --git a/test/zdtm/static/session01.c b/test/zdtm/static/session01.c
new file mode 100644
index 000000000..9bea83d92
--- /dev/null
+++ b/test/zdtm/static/session01.c
@@ -0,0 +1,338 @@
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+
+#include "zdtmtst.h"
+#include "lock.h"
+
+const char *test_doc = "Test that sid, pgid are restored";
+const char *test_author = "Andrey Vagin <avagin@openvz.org>";
+
+struct master {
+ pid_t pid;
+ pid_t ppid;
+ pid_t sid;
+ pid_t pgid;
+};
+
+struct testcase {
+ pid_t pid;
+ pid_t ppid;
+ pid_t sid;
+ pid_t born_sid;
+ pid_t pgid;
+ int alive;
+ struct master master;
+ futex_t futex;
+};
+
+enum {
+ TEST_FORK,
+ TEST_PGID,
+ TEST_WAIT,
+ TEST_MASTER,
+ TEST_CHECK,
+ TEST_EXIT,
+};
+
+static struct testcase *testcases;
+static futex_t *fstate;
+static struct testcase __testcases[] = {
+ { 2, 1, 2, 1, 2, 1 }, /* session00 */
+ { 4, 2, 4, 2, 4, 1 }, /* |\_session00 */
+ {15, 4, 4, 4, 15, 1 }, /* | |\_session00 */
+ {16, 4, 4, 4, 15, 1 }, /* | \_session00 */
+ {17, 4, 4, 4, 17, 0 }, /* | |\_session00 */
+ {18, 4, 4, 4, 17, 1 }, /* | \_session00 */
+ { 5, 2, 2, 2, 2, 1 }, /* |\_session00 */
+ { 8, 2, 8, 2, 8, 1 }, /* |\_session00 */
+ { 9, 8, 2, 2, 2, 1 }, /* | \_session00 */
+ {10, 2, 10, 2, 10, 1 }, /* |\_session00 */
+ {11, 10, 11, 2, 11, 1 }, /* | \_session00 */
+ {12, 11, 2, 2, 2, 1 }, /* | \_session00 */
+ {13, 2, 2, 2, 2, 0 }, /* \_session00 */
+ { 3, 13, 2, 2, 2, 1 }, /* session00 */
+ { 6, 2, 6, 2, 6, 0 }, /* \_session00 */
+ {14, 6, 6, 6, 6, 1 }, /* session00 */
+};
+
+#define TESTS (sizeof(__testcases) / sizeof(struct testcase))
+
+#define check(n, a, b) do { if ((a) != (b)) { pr_perror("%s mismatch %d != %d", n, a, b); goto err; } } while (0)
+
+static int child(const int c);
+static int fork_children(struct testcase *t, int leader)
+{
+ int i;
+ pid_t cid;
+
+ for (i = 0; i < TESTS; i++) {
+ if (t->pid != testcases[i].ppid)
+ continue;
+
+ if (leader ^ (t->pid == testcases[i].born_sid))
+ continue;
+
+ cid = test_fork_id(i);
+ if (cid < 0)
+ goto err;
+ if (cid == 0) {
+ test_msg("I'm %d with pid %d\n", i, getpid());
+ child(i);
+ exit(0);
+ }
+
+ testcases[i].master.pid = cid;
+ }
+ return 0;
+err:
+ return -1;
+}
+
+static int child(const int c)
+{
+ int i;
+ struct testcase *t = &testcases[c];
+
+ t->master.pid = getpid();
+
+ if (fork_children(t, 0))
+ goto err;
+
+ if (t->pid == t->sid) {
+ if (getpid() != getsid(getpid()))
+ if (setsid() < 0)
+ goto err;
+ if (fork_children(t, 1))
+ goto err;
+ }
+ if (t->pid == t->pgid) {
+ if (getpid() != getpgid(getpid()))
+ if (setpgid(getpid(), getpid()) < 0) {
+ pr_perror("setpgid() failed");
+ goto err;
+ }
+ t->master.pgid = t->master.pid;
+ }
+
+ futex_set_and_wake(&t->futex, c);
+
+ if (c == 0)
+ goto out;
+
+ futex_wait_until(fstate, TEST_PGID);
+
+ for (i = 0; i < TESTS; i++) {
+ if (c == 0)
+ break;
+ if (t->pgid != testcases[i].pid)
+ continue;
+ if (getpgid(getpid()) != testcases[i].master.pid)
+ if (setpgid(getpid(), testcases[i].master.pid) < 0) {
+ pr_perror("setpgid() failed (%d) (%d)", c, i);
+ goto err;
+ }
+
+ t->master.pgid = testcases[i].master.pid;
+ break;
+ }
+
+ futex_set_and_wake(&t->futex, c);
+
+ futex_wait_until(fstate, TEST_WAIT);
+
+ for (i = 0; i < TESTS; i++) {
+ if (t->pid != testcases[i].ppid)
+ continue;
+ if (testcases[i].alive)
+ continue;
+ test_msg("Wait porcess %d (pid %d)\n", i, testcases[i].master.pid);
+ waitpid(testcases[i].master.pid, NULL, 0);
+ }
+
+ if (!t->alive)
+ goto out;
+
+ futex_set_and_wake(&t->futex, c);
+
+ futex_wait_until(fstate, TEST_MASTER);
+
+ /* Save the master copy */
+ t->master.ppid = getppid();
+ t->master.sid = getsid(getpid());
+
+ futex_set_and_wake(&t->futex, c);
+
+ futex_wait_until(fstate, TEST_CHECK);
+
+ check("pid", t->master.pid, getpid());
+ check("ppid", t->master.ppid, getppid());
+ check("sid", t->master.sid, getsid(getpid()));
+ check("pgid", t->master.pgid, getpgid(getpid()));
+
+ futex_set_and_wake(&t->futex, c);
+
+ /* Wait while all test cases check results */
+ futex_wait_until(fstate, TEST_EXIT);
+out:
+ return 0;
+err:
+ futex_set_and_wake(&t->futex, -1);
+ return 1;
+}
+
+int main(int argc, char ** argv)
+{
+ int i, err, ret;
+ void *ptr;
+
+ BUG_ON(sizeof(*fstate) + sizeof(__testcases) > 4096);
+
+ ptr = mmap(NULL, 4096, PROT_WRITE | PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (ptr == MAP_FAILED)
+ return 1;
+
+ fstate = ptr;
+ futex_set(fstate, TEST_FORK);
+ testcases = ptr + sizeof(*fstate);
+
+ memcpy(testcases, &__testcases, sizeof(__testcases));
+
+ test_init(argc, argv);
+
+ testcases[0].master.pid = getpid();;
+ if (child(0))
+ goto err;
+
+ for (i = 1; i < TESTS; i++) {
+ ret = futex_wait_while(&testcases[i].futex, 0);
+ if (ret < 0)
+ return 1;
+ futex_set(&testcases[i].futex, 0);
+ }
+
+ test_msg("TEST_PGID\n");
+ futex_set_and_wake(fstate, TEST_PGID);
+ for (i = 1; i < TESTS; i++) {
+ ret = futex_wait_while(&testcases[i].futex, 0);
+ if (ret < 0)
+ goto err;
+ futex_set(&testcases[i].futex, 0);
+ }
+
+ test_msg("TEST_WAIT\n");
+ futex_set_and_wake(fstate, TEST_WAIT);
+ for (i = 1; i < TESTS; i++) {
+ if (!testcases[i].alive)
+ continue;
+ ret = futex_wait_while(&testcases[i].futex, 0);
+ if (ret < 0)
+ goto err;
+ futex_set(&testcases[i].futex, 0);
+ }
+
+ for (i = 0; i < TESTS; i++) {
+ if (testcases[0].pid != testcases[i].ppid)
+ continue;
+ if (testcases[i].alive)
+ continue;
+ test_msg("Wait porcess %d (pid %d)\n",
+ i, testcases[i].master.pid);
+ waitpid(testcases[i].master.pid, NULL, 0);
+ }
+
+ test_msg("TEST_MASTER\n");
+ futex_set_and_wake(fstate, TEST_MASTER);
+ for (i = 1; i < TESTS; i++) {
+ if (!testcases[i].alive)
+ continue;
+ ret = futex_wait_while(&testcases[i].futex, 0);
+ if (ret < 0)
+ goto err;
+ futex_set(&testcases[i].futex, 0);
+ test_msg("The process %d initilized\n", ret);
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ err = 0;
+ for (i = 1; i < TESTS; i++) {
+ int j;
+ struct testcase *t = testcases + i;
+ pid_t sid, pgid;
+
+ if (!t->alive)
+ continue;
+
+ for (j = 0; j < TESTS; j++) {
+ struct testcase *p = testcases + j;
+ /* sanity check */
+ if (p->pid == t->sid && t->master.sid != p->master.pid) {
+ pr_perror("session mismatch (%d) %d != (%d) %d",
+ i, t->master.sid, j, p->master.pid);
+ err++;
+ }
+ if (p->pid == t->pgid && t->master.pgid != p->master.pid) {
+ pr_perror("pgid mismatch (%d) %d != (%d) %d",
+ i, t->master.pgid, j, p->master.pid);
+ err++;
+ }
+ }
+
+ sid = getsid(t->master.pid);
+ if (t->master.sid != sid) {
+ pr_perror("%d: session mismatch %d (expected %d)",
+ i, sid, t->master.sid);
+ err++;
+ }
+
+ pgid = getpgid(t->master.pid);
+ if (t->master.pgid != pgid) {
+ pr_perror("%d: pgid mismatch %d (expected %d)",
+ i, t->master.pgid, pgid);
+ err++;
+ }
+ }
+
+ test_msg("TEST_CHECK\n");
+ futex_set_and_wake(fstate, TEST_CHECK);
+
+ for (i = 1; i < TESTS; i++) {
+ if (!testcases[i].alive)
+ continue;
+
+ ret = futex_wait_while(&testcases[i].futex, 0);
+ if (ret < 0)
+ goto err;
+ futex_set(&testcases[i].futex, 0);
+
+ if (ret < 0) {
+ fail("Someone failed");
+ err++;
+ continue;
+ }
+ test_msg("The process %u is restored correctly\n", (unsigned)ret);
+ }
+
+ test_msg("TEST_EXIT\n");
+ futex_set_and_wake(fstate, TEST_EXIT);
+
+ if (!err)
+ pass();
+
+ return 0;
+err:
+ for (i = 1; i < TESTS; i++) {
+ pid_t pid = testcases[i].master.pid;
+ if (pid > 0) {
+ ret = kill(pid, SIGKILL);
+ test_msg("kill %d %s\n", pid, strerror(ret == -1 ? errno : 0));
+ }
+ }
+ return 1;
+}
diff --git a/test/zdtm/static/session01.desc b/test/zdtm/static/session01.desc
new file mode 100644
index 000000000..6c4afe5f0
--- /dev/null
+++ b/test/zdtm/static/session01.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns'}
diff --git a/test/zdtm/static/session02.c b/test/zdtm/static/session02.c
new file mode 100644
index 000000000..6c5e77121
--- /dev/null
+++ b/test/zdtm/static/session02.c
@@ -0,0 +1,328 @@
+#define _GNU_SOURCE
+#include <sys/mman.h>
+#include <sched.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <sys/user.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Create a crazy process tree";
+const char *test_author = "Andrew Vagin <avagin@parallels.com>";
+
+struct process
+{
+ pid_t pid;
+ pid_t sid;
+ int sks[2];
+ int dead;
+};
+
+struct process *processes;
+int nr_processes = 20;
+int current = 0;
+
+static void cleanup()
+{
+ int i;
+
+ for (i = 0; i < nr_processes; i++) {
+ if (processes[i].dead)
+ continue;
+ if (processes[i].pid <= 0)
+ continue;
+
+ kill(processes[i].pid, SIGKILL);
+ }
+}
+
+enum commands
+{
+ TEST_FORK,
+ TEST_WAIT,
+ TEST_SUBREAPER,
+ TEST_SETSID,
+ TEST_DIE
+};
+
+struct command
+{
+ enum commands cmd;
+ int arg1;
+ int arg2;
+};
+
+static void handle_command();
+
+static void mainloop()
+{
+ while (1)
+ handle_command();
+}
+
+#define CLONE_STACK_SIZE 4096
+/* All arguments should be above stack, because it grows down */
+struct clone_args {
+ char stack[CLONE_STACK_SIZE] __stack_aligned__;
+ char stack_ptr[0];
+ int id;
+};
+
+static int clone_func(void *_arg)
+{
+ struct clone_args *args = (struct clone_args *) _arg;
+
+ current = args->id;
+
+ test_msg("%3d: Hello. My pid is %d\n", args->id, getpid());
+ mainloop();
+ exit(0);
+}
+
+static int make_child(int id, int flags)
+{
+ struct clone_args args;
+ pid_t cid;
+
+ args.id = id;
+
+ cid = clone(clone_func, args.stack_ptr,
+ flags | SIGCHLD, &args);
+
+ if (cid < 0)
+ pr_perror("clone(%d, %d)", id, flags);
+
+ processes[id].pid = cid;
+
+ return cid;
+}
+
+static void handle_command()
+{
+ int sk = processes[current].sks[0], ret, status = 0;
+ struct command cmd;
+
+ ret = read(sk, &cmd, sizeof(cmd));
+ if (ret != sizeof(cmd)) {
+ pr_perror("Unable to get command");
+ goto err;
+ }
+
+ switch (cmd.cmd) {
+ case TEST_FORK:
+ {
+ pid_t pid;
+
+ pid = make_child(cmd.arg1, cmd.arg2);
+ if (pid == -1) {
+ status = -1;
+ goto err;
+ }
+
+ test_msg("%3d: fork(%d, %x) = %d\n",
+ current, cmd.arg1, cmd.arg2, pid);
+ processes[cmd.arg1].pid = pid;
+ }
+ break;
+ case TEST_WAIT:
+ test_msg("%3d: wait(%d) = %d\n", current,
+ cmd.arg1, processes[cmd.arg1].pid);
+
+ if (waitpid(processes[cmd.arg1].pid, NULL, 0) == -1) {
+ pr_perror("waitpid(%d)", processes[cmd.arg1].pid);
+ status = -1;
+ }
+ break;
+ case TEST_SUBREAPER:
+ test_msg("%3d: subreaper(%d)\n", current, cmd.arg1);
+ if (prctl(PR_SET_CHILD_SUBREAPER, cmd.arg1, 0, 0, 0) == -1) {
+ pr_perror("PR_SET_CHILD_SUBREAPER");
+ status = -1;
+ }
+ break;
+ case TEST_SETSID:
+ test_msg("%3d: setsid()\n", current);
+ if(setsid() == -1) {
+ pr_perror("setsid");
+ status = -1;
+ }
+ break;
+ case TEST_DIE:
+ test_msg("%3d: die()\n", current);
+ processes[current].dead = 1;
+ shutdown(sk, SHUT_RDWR);
+ exit(0);
+ }
+
+ ret = write(sk, &status, sizeof(status));
+ if (ret != sizeof(status)) {
+ pr_perror("Unable to answer");
+ goto err;
+ }
+
+ if (status < 0)
+ goto err;
+
+ return;
+err:
+ shutdown(sk, SHUT_RDWR);
+ exit(1);
+}
+
+static int send_command(int id, enum commands op, int arg1, int arg2)
+{
+ int sk = processes[id].sks[1], ret, status;
+ struct command cmd = {op, arg1, arg2};
+
+ if (op == TEST_FORK) {
+ if (processes[arg1].pid) {
+ pr_perror("%d is busy", arg1);
+ return -1;
+ }
+ }
+
+ ret = write(sk, &cmd, sizeof(cmd));
+ if (ret != sizeof(cmd)) {
+ pr_perror("Unable to send command");
+ goto err;
+ }
+
+ status = 0;
+ ret = read(sk, &status, sizeof(status));
+ if (ret != sizeof(status) && !(status == 0 && op == TEST_DIE)) {
+ pr_perror("Unable to get answer");
+ goto err;
+ }
+
+ if (status) {
+ pr_perror("The command(%d, %d, %d) failed");
+ goto err;
+ }
+
+ return 0;
+err:
+ cleanup();
+ exit(1);
+}
+
+int main(int argc, char ** argv)
+{
+ int pid, i;
+ int fail_cnt = 0;
+
+ test_init(argc, argv);
+
+ processes = mmap(NULL, PAGE_SIZE, PROT_WRITE | PROT_READ,
+ MAP_SHARED | MAP_ANONYMOUS, 0, 0);
+ if (processes == NULL) {
+ pr_perror("Unable to map share memory");
+ return 1;
+ }
+
+ for (i = 0; i < nr_processes; i++) {
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, processes[i].sks) == -1) {
+ pr_perror("socketpair");
+ return 1;
+ }
+ }
+
+ pid = make_child(0, 0);
+ if (pid < 0)
+ return -1;
+
+ /*
+ * 5 5 \_ session02 ( 0)
+ * 6 6 \_ session02 ( 1)
+ * 8 7 | \_ session02 ( 3)
+ * 15 12 | \_ session02 (10)
+ * 10 10 \_ session02 ( 5)
+ * 11 7 \_ session02 ( 6)
+ * 13 12 \_ session02 ( 8)
+ */
+
+ send_command(0, TEST_SUBREAPER, 1, 0);
+ send_command(0, TEST_SETSID, 0, 0);
+
+ send_command(0, TEST_FORK, 1, 0);
+ send_command(1, TEST_FORK, 2, 0);
+
+ send_command(2, TEST_SETSID, 0, 0);
+ send_command(2, TEST_FORK, 3, CLONE_PARENT);
+ send_command(2, TEST_DIE, 0, 0);
+ send_command(1, TEST_WAIT, 2, 0);
+
+ send_command(3, TEST_FORK, 4, 0);
+ send_command(4, TEST_FORK, 5, 0);
+ send_command(5, TEST_FORK, 6, 0);
+
+ send_command(5, TEST_FORK, 7, 0);
+ send_command(7, TEST_SETSID, 0, 0);
+ send_command(7, TEST_FORK, 8, CLONE_PARENT);
+ send_command(7, TEST_FORK, 9, CLONE_PARENT);
+ send_command(7, TEST_DIE, 0, 0);
+ send_command(5, TEST_WAIT, 7, 0);
+
+ send_command(9, TEST_FORK, 10, 0);
+ send_command(1, TEST_SUBREAPER, 1, 0);
+ send_command(9, TEST_DIE, 0, 0);
+ send_command(5, TEST_WAIT, 9, 0);
+ send_command(1, TEST_SUBREAPER, 0, 0);
+
+ send_command(4, TEST_DIE, 0, 0);
+ send_command(3, TEST_WAIT, 4, 0);
+
+ send_command(1, TEST_SETSID, 0, 0);
+ send_command(5, TEST_SETSID, 0, 0);
+
+ for (i = 0; i < nr_processes; i++) {
+ if (processes[i].dead)
+ continue;
+ if (processes[i].pid == 0)
+ continue;
+
+ processes[i].sid = getsid(processes[i].pid);
+ if (processes[i].sid == -1) {
+ pr_perror("getsid(%d)", i);
+ goto err;
+ }
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ for (i = 0; i < nr_processes; i++) {
+ pid_t sid;
+
+ if (processes[i].dead)
+ continue;
+ if (processes[i].pid == 0)
+ continue;
+
+ sid = getsid(processes[i].pid);
+ if (sid == -1) {
+ pr_perror("getsid(%d)", i);
+ goto err;
+ }
+
+ if (sid != processes[i].sid) {
+ fail("%d, %d: wrong sid %d (expected %d)",
+ i, processes[i].pid, sid, processes[i].sid);
+ fail_cnt++;
+ }
+ }
+
+ if (fail_cnt)
+ goto err;
+
+ pass();
+
+ return 0;
+err:
+ cleanup();
+ return 1;
+}
diff --git a/test/zdtm/static/session02.desc b/test/zdtm/static/session02.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/session02.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/session03.c b/test/zdtm/static/session03.c
new file mode 100644
index 000000000..ef268e552
--- /dev/null
+++ b/test/zdtm/static/session03.c
@@ -0,0 +1,377 @@
+#define _GNU_SOURCE
+#include <sys/mman.h>
+#include <sched.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <sys/user.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Create a crazy process tree";
+const char *test_author = "Andrew Vagin <avagin@parallels.com>";
+
+struct process
+{
+ pid_t pid;
+ pid_t sid;
+ int sks[2];
+ int dead;
+ int wait;
+};
+
+#define MEM_SIZE (2 * PAGE_SIZE)
+#define PR_MAX (MEM_SIZE / sizeof(struct process))
+
+struct process *processes;
+int nr_processes = 0;
+int current = 0;
+
+static void sigchld_handler(int signal, siginfo_t *siginfo, void *data)
+{
+ pid_t pid = siginfo->si_pid;
+ if (siginfo->si_status == 2)
+ waitpid(pid, NULL, WNOHANG);
+}
+
+static void cleanup()
+{
+ int i, ret;
+
+ for (i = 0; i < nr_processes; i++) {
+ if (processes[i].dead)
+ continue;
+ if (processes[i].pid <= 0)
+ continue;
+
+ kill(processes[i].pid, SIGKILL);
+ }
+
+ while (1) {
+ ret = wait(NULL);
+ if (ret == -1) {
+ if (errno == ECHILD)
+ break;
+ pr_perror("wait");
+ exit(1);
+ }
+ }
+}
+
+enum commands
+{
+ TEST_FORK,
+ TEST_DIE_WAIT,
+ TEST_DIE,
+ TEST_SUBREAPER,
+ TEST_SETSID,
+ TEST_MAX
+};
+
+int cmd_weght[TEST_MAX] = {10, 3, 1, 10, 7};
+int sum_weight = 0;
+static int get_rnd_op()
+{
+ int i, m;
+ if (sum_weight == 0) {
+ for (i = 0; i < TEST_MAX; i++)
+ sum_weight += cmd_weght[i];
+ }
+ m = lrand48() % sum_weight;
+ for (i = 0; i < TEST_MAX; i++) {
+ if (m > cmd_weght[i]) {
+ m -= cmd_weght[i];
+ continue;
+ }
+ return i;
+ }
+ return -1;
+}
+
+struct command
+{
+ enum commands cmd;
+ int arg1;
+ int arg2;
+};
+
+static void handle_command();
+
+static void mainloop()
+{
+ while (1)
+ handle_command();
+}
+
+#define CLONE_STACK_SIZE 4096
+/* All arguments should be above stack, because it grows down */
+struct clone_args {
+ char stack[CLONE_STACK_SIZE] __stack_aligned__;
+ char stack_ptr[0];
+ int id;
+};
+
+static int clone_func(void *_arg)
+{
+ struct clone_args *args = (struct clone_args *) _arg;
+
+ current = args->id;
+
+ test_msg("%3d: Hello. My pid is %d\n", args->id, getpid());
+ mainloop();
+ exit(0);
+}
+
+static int make_child(int id, int flags)
+{
+ struct clone_args args;
+ pid_t cid;
+
+ args.id = id;
+
+ cid = clone(clone_func, args.stack_ptr,
+ flags | SIGCHLD, &args);
+
+ if (cid < 0)
+ pr_perror("clone(%d, %d)", id, flags);
+
+ processes[id].pid = cid;
+
+ return cid;
+}
+
+static void handle_command()
+{
+ int sk = processes[current].sks[0], ret, status = 0;
+ struct command cmd;
+
+ ret = read(sk, &cmd, sizeof(cmd));
+ if (ret != sizeof(cmd)) {
+ pr_perror("Unable to get command");
+ goto err;
+ }
+
+ switch (cmd.cmd) {
+ case TEST_FORK:
+ {
+ pid_t pid;
+
+ pid = make_child(cmd.arg1, cmd.arg2 ? CLONE_PARENT : 0);
+ if (pid < 0) {
+ status = -1;
+ goto err;
+ }
+
+ test_msg("%3d: fork(%d, %x) = %d\n",
+ current, cmd.arg1, cmd.arg2, pid);
+ processes[cmd.arg1].pid = pid;
+ }
+ break;
+ case TEST_SUBREAPER:
+ test_msg("%3d: subreaper(%d)\n", current, cmd.arg1);
+ if (prctl(PR_SET_CHILD_SUBREAPER, cmd.arg1, 0, 0, 0) == -1) {
+ pr_perror("PR_SET_CHILD_SUBREAPER");
+ status = -1;
+ }
+ break;
+ case TEST_SETSID:
+ if (getsid(getpid()) == getpid())
+ break;
+ test_msg("%3d: setsid()\n", current);
+ if(setsid() == -1) {
+ pr_perror("setsid");
+ status = -1;
+ }
+ break;
+ case TEST_DIE_WAIT:
+ test_msg("%3d: wait()\n", current);
+ case TEST_DIE:
+ test_msg("%3d: die()\n", current);
+ processes[current].dead = 1;
+ shutdown(sk, SHUT_RDWR);
+ if (cmd.cmd == TEST_DIE_WAIT)
+ exit(2);
+ exit(0);
+ default:
+ pr_perror("Unknown operation %d", cmd.cmd);
+ status = -1;
+ break;
+ }
+
+ ret = write(sk, &status, sizeof(status));
+ if (ret != sizeof(status)) {
+ pr_perror("Unable to answer");
+ goto err;
+ }
+
+ if (status < 0)
+ goto err;
+
+ return;
+err:
+ shutdown(sk, SHUT_RDWR);
+ exit(1);
+}
+
+static int send_command(int id, enum commands op, int arg)
+{
+ int sk = processes[id].sks[1], ret, status;
+ struct command cmd = {op, arg};
+
+ if (op == TEST_FORK) {
+ cmd.arg1 = nr_processes;
+ nr_processes++;
+ if (nr_processes > PR_MAX)
+ return -1;
+ cmd.arg2 = arg;
+ }
+
+ ret = write(sk, &cmd, sizeof(cmd));
+ if (ret != sizeof(cmd)) {
+ pr_perror("Unable to send command");
+ goto err;
+ }
+
+ status = 0;
+ ret = read(sk, &status, sizeof(status));
+ if (ret != sizeof(status) &&
+ !(status == 0 && (op == TEST_DIE || op == TEST_DIE_WAIT))) {
+ pr_perror("Unable to get answer");
+ goto err;
+ }
+
+ if (status) {
+ pr_perror("The command(%d, %d, %d) failed");
+ goto err;
+ }
+
+ return 0;
+err:
+ cleanup();
+ exit(1);
+}
+
+int main(int argc, char ** argv)
+{
+ struct sigaction act;
+ int pid, i, ret;
+ int fail_cnt = 0;
+
+ test_init(argc, argv);
+
+ if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) {
+ pr_perror("PR_SET_CHILD_SUBREAPER");
+ return -1;
+ }
+
+ ret = sigaction(SIGCHLD, NULL, &act);
+ if (ret < 0) {
+ pr_perror("sigaction() failed");
+ return -1;
+ }
+
+ act.sa_flags |= SA_NOCLDSTOP | SA_SIGINFO | SA_RESTART;
+ act.sa_sigaction = sigchld_handler;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGCHLD);
+
+ ret = sigaction(SIGCHLD, &act, NULL);
+ if (ret < 0) {
+ pr_perror("sigaction() failed");
+ return -1;
+ }
+
+ processes = mmap(NULL, MEM_SIZE, PROT_WRITE | PROT_READ,
+ MAP_SHARED | MAP_ANONYMOUS, 0, 0);
+ if (processes == NULL) {
+ pr_perror("Unable to map share memory");
+ return 1;
+ }
+
+ for (i = 0; i < PR_MAX; i++) {
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, processes[i].sks) == -1) {
+ pr_perror("socketpair");
+ return 1;
+ }
+ }
+
+ nr_processes++;
+ pid = make_child(0, 0);
+ if (pid < 0)
+ return -1;
+
+ while(nr_processes < PR_MAX) {
+ int op, id;
+ int flags = lrand48() % 2;
+
+ op = get_rnd_op();
+ if (op == TEST_DIE || op == TEST_DIE_WAIT || op == TEST_SUBREAPER) {
+ if (nr_processes == 1)
+ continue;
+ else
+ id = lrand48() % (nr_processes - 1) + 1;
+ } else if (op == TEST_FORK) {
+ id = nr_processes * 9 / 10 + lrand48() % nr_processes / 10;
+ while (processes[id].dead != 0)
+ id--;
+ } else
+ id = lrand48() % nr_processes;
+
+ if (processes[id].dead)
+ continue;
+
+ send_command(id, op, flags);
+ }
+
+ for (i = 0; i < nr_processes; i++) {
+ if (processes[i].dead)
+ continue;
+ if (processes[i].pid == 0)
+ continue;
+
+ processes[i].sid = getsid(processes[i].pid);
+ if (processes[i].sid == -1) {
+ pr_perror("getsid(%d)", i);
+ goto err;
+ }
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ for (i = 0; i < nr_processes; i++) {
+ pid_t sid;
+
+ if (processes[i].dead)
+ continue;
+ if (processes[i].pid == 0)
+ continue;
+
+ sid = getsid(processes[i].pid);
+ if (sid == -1) {
+ pr_perror("getsid(%d)", i);
+ goto err;
+ }
+
+ if (sid != processes[i].sid) {
+ fail("%d, %d: wrong sid %d (expected %d)",
+ i, processes[i].pid, sid, processes[i].sid);
+ fail_cnt++;
+ }
+ }
+
+ if (fail_cnt)
+ goto err;
+
+ pass();
+
+ cleanup();
+ return 0;
+err:
+ cleanup();
+ return 1;
+}
diff --git a/test/zdtm/static/session03.desc b/test/zdtm/static/session03.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/session03.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/shm.c b/test/zdtm/static/shm.c
new file mode 100644
index 000000000..fc90a9955
--- /dev/null
+++ b/test/zdtm/static/shm.c
@@ -0,0 +1,194 @@
+#define _GNU_SOURCE
+#include <sched.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc="Tests detached shmems migrate fine";
+const char *test_author="Stanislav Kinsbursky <skinsbursky@parallels.com>";
+
+#define DEF_MEM_SIZE (40960)
+unsigned int shmem_size = DEF_MEM_SIZE;
+TEST_OPTION(shmem_size, uint, "Size of shared memory segment", 0);
+
+#define INIT_CRC (~0)
+
+static int fill_shm_seg(int id, size_t size)
+{
+ uint8_t *mem;
+ uint32_t crc = INIT_CRC;
+
+ mem = shmat(id, NULL, 0);
+ if (mem == (void *)-1) {
+ pr_perror("Can't attach shm: %d", -errno);
+ return -1;
+ }
+
+ datagen(mem, size, &crc);
+
+ if (shmdt(mem) < 0) {
+ pr_perror("Can't detach shm: %d", -errno);
+ return -1;
+ }
+ return 0;
+}
+
+static int get_shm_seg(int key, size_t size, unsigned int flags)
+{
+ int id;
+
+ id = shmget(key, size, 0777 | flags);
+ if (id == -1) {
+ pr_perror("Can't get shm: %d", -errno);
+ return -1;
+ }
+ return id;
+}
+
+static int prepare_shm(int key, size_t size)
+{
+ int id;
+
+ id = get_shm_seg(key, shmem_size, IPC_CREAT | IPC_EXCL);
+ if (id == -1) {
+ return -1;
+ }
+ if (fill_shm_seg(id, shmem_size) < 0)
+ return -1;
+ return id;
+}
+
+static int check_shm_id(int id, size_t size)
+{
+ uint8_t *mem;
+ uint32_t crc = INIT_CRC;
+
+ mem = shmat(id, NULL, 0);
+ if (mem == (void *)-1) {
+ pr_perror("Can't attach shm: %d", -errno);
+ return -1;
+ }
+ crc = INIT_CRC;
+ if (datachk(mem, size, &crc)) {
+ fail("shmem data are corrupted");
+ return -1;
+ }
+ if (shmdt(mem) < 0) {
+ pr_perror("Can't detach shm: %d", -errno);
+ return -1;
+ }
+ return 0;
+}
+
+static int check_shm_key(int key, size_t size)
+{
+ int id;
+
+ id = get_shm_seg(key, size, 0);
+ if (id < 0)
+ return -1;
+ return check_shm_id(id, size);
+}
+
+int main(int argc, char **argv)
+{
+ key_t key;
+ int shm;
+ int fail_count = 0;
+ int ret = -1;
+
+ void *mem;
+ uint32_t crc = INIT_CRC;
+
+ test_init(argc, argv);
+
+ key = ftok(argv[0], 822155666);
+ if (key == -1) {
+ pr_perror("Can't make key");
+ goto out;
+ }
+
+ shm = prepare_shm(key, shmem_size);
+ if (shm == -1) {
+ pr_perror("Can't prepare shm (1)");
+ goto out;
+ }
+
+ mem = shmat(shm, NULL, 0);
+ if (mem == (void *)-1) {
+ pr_perror("Can't shmat");
+ goto out;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = check_shm_id(shm, shmem_size);
+ if (ret < 0) {
+ fail("ID check (1) failed\n");
+ fail_count++;
+ goto out_shm;
+ }
+
+ ret = check_shm_key(key, shmem_size);
+ if (ret < 0) {
+ fail("KEY check failed\n");
+ fail_count++;
+ goto out_shm;
+ }
+
+ if (datachk(mem, shmem_size, &crc)) {
+ fail("shmem data is corrupted");
+ return -1;
+ }
+
+ if (shmdt(mem) < 0) {
+ pr_perror("Can't detach shm");
+ return -1;
+ }
+
+ ret = shmctl(shm, IPC_RMID, NULL);
+ if (ret < 0) {
+ fail("Failed (1) to destroy segment: %d\n", -errno);
+ fail_count++;
+ goto out_shm;
+ }
+ /*
+ * Code below checks that it's still possible to create new IPC SHM
+ * segments
+ */
+ shm = prepare_shm(key, shmem_size);
+ if (shm == -1) {
+ fail("Can't prepare shm (2)");
+ fail_count++;
+ goto out;
+ }
+
+ ret = check_shm_id(shm, shmem_size);
+ if (ret < 0) {
+ fail("ID check (2) failed\n");
+ fail_count++;
+ goto out_shm;
+ }
+
+out_shm:
+ ret = shmctl(shm, IPC_RMID, NULL);
+ if (ret < 0) {
+ fail("Failed (2) to destroy segment: %d\n", -errno);
+ fail_count++;
+ }
+ if (fail_count == 0)
+ pass();
+out:
+ return ret;
+}
diff --git a/test/zdtm/static/shm.desc b/test/zdtm/static/shm.desc
new file mode 100644
index 000000000..6c4afe5f0
--- /dev/null
+++ b/test/zdtm/static/shm.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns'}
diff --git a/test/zdtm/static/sigaltstack.c b/test/zdtm/static/sigaltstack.c
new file mode 100644
index 000000000..be5ddf792
--- /dev/null
+++ b/test/zdtm/static/sigaltstack.c
@@ -0,0 +1,168 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <pthread.h>
+#include <syscall.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check for alternate signal stack";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+static char stack_thread[SIGSTKSZ + TEST_MSG_BUFFER_SIZE] __stack_aligned__;
+static char stack_main[SIGSTKSZ + TEST_MSG_BUFFER_SIZE] __stack_aligned__;
+
+enum {
+ SAS_MAIN_OLD,
+ SAS_MAIN_NEW,
+ SAS_THRD_OLD,
+ SAS_THRD_NEW,
+
+ SAS_MAX
+};
+static stack_t sas_state[SAS_MAX];
+
+static task_waiter_t t;
+
+#define exit_group(code) syscall(__NR_exit_group, code)
+#define gettid() syscall(__NR_gettid)
+
+static int sascmp(stack_t *old, stack_t *new)
+{
+ return old->ss_size != new->ss_size ||
+ old->ss_sp != new->ss_sp ||
+ old->ss_flags != new->ss_flags;
+}
+
+static void show_ss(char *prefix, stack_t *s)
+{
+ test_msg("%20s: at %p (size %#8x flags %#2x)\n",
+ prefix, s->ss_sp, s->ss_size, s->ss_flags);
+}
+
+void thread_sigaction(int signo, siginfo_t *info, void *context)
+{
+ if (sigaltstack(NULL, &sas_state[SAS_THRD_NEW]))
+ pr_perror("thread sigaltstack");
+
+ show_ss("thread in sas", &sas_state[SAS_THRD_NEW]);
+
+ task_waiter_complete(&t, 2);
+
+ test_msg("Waiting in thread SAS\n");
+ task_waiter_wait4(&t, 3);
+ test_msg("Leaving thread SAS\n");
+}
+
+static void *thread_func(void *arg)
+{
+ sas_state[SAS_THRD_OLD] = (stack_t) {
+ .ss_size = SIGSTKSZ,
+ .ss_sp = stack_thread,
+ .ss_flags = SS_ONSTACK,
+ };
+
+ struct sigaction sa = {
+ .sa_sigaction = thread_sigaction,
+ .sa_flags = SA_RESTART | SA_ONSTACK,
+ };
+
+ sigemptyset(&sa.sa_mask);
+
+ if (sigaction(SIGUSR2, &sa, NULL)) {
+ pr_perror("Can't set SIGUSR2 handler");
+ exit_group(-1);
+ }
+
+ task_waiter_wait4(&t, 1);
+
+ if (sigaltstack(&sas_state[SAS_THRD_OLD], NULL)) {
+ pr_perror("thread sigaltstack");
+ exit_group(-1);
+ }
+
+ syscall(__NR_tkill, gettid(), SIGUSR2);
+
+ return NULL;
+}
+
+void leader_sigaction(int signo, siginfo_t *info, void *context)
+{
+ if (sigaltstack(NULL, &sas_state[SAS_MAIN_NEW]))
+ pr_perror("leader sigaltstack");
+
+ show_ss("leader in sas", &sas_state[SAS_MAIN_NEW]);
+}
+
+int main(int argc, char *argv[])
+{
+ pthread_t thread;
+
+ sas_state[SAS_MAIN_OLD] = (stack_t) {
+ .ss_size = SIGSTKSZ,
+ .ss_sp = stack_main,
+ .ss_flags = SS_ONSTACK,
+ };
+
+ struct sigaction sa = {
+ .sa_sigaction = leader_sigaction,
+ .sa_flags = SA_RESTART | SA_ONSTACK,
+ };
+
+ sigemptyset(&sa.sa_mask);
+
+ test_init(argc, argv);
+ task_waiter_init(&t);
+
+ if (sigaction(SIGUSR1, &sa, NULL)) {
+ pr_perror("Can't set SIGUSR1 handler");
+ exit(-1);
+ }
+
+ if (pthread_create(&thread, NULL, &thread_func, NULL)) {
+ pr_perror("Can't create thread");
+ exit(-1);
+ }
+
+ if (sigaltstack(&sas_state[SAS_MAIN_OLD], NULL)) {
+ pr_perror("sigaltstack");
+ exit(-1);
+ }
+
+ task_waiter_complete(&t, 1);
+ task_waiter_wait4(&t, 2);
+
+ test_daemon();
+ test_waitsig();
+
+ test_msg("Thread may leave SAS\n");
+ task_waiter_complete(&t, 3);
+
+ syscall(__NR_tkill, gettid(), SIGUSR1);
+
+ if (pthread_join(thread, NULL)) {
+ fail("Error joining thread");
+ exit(-1);
+ }
+ task_waiter_fini(&t);
+
+ show_ss("main old", &sas_state[SAS_MAIN_OLD]);
+ show_ss("main new", &sas_state[SAS_MAIN_NEW]);
+ show_ss("thrd old", &sas_state[SAS_THRD_OLD]);
+ show_ss("thrd new", &sas_state[SAS_THRD_NEW]);
+
+ if (sascmp(&sas_state[SAS_MAIN_OLD], &sas_state[SAS_MAIN_NEW]) ||
+ sascmp(&sas_state[SAS_THRD_OLD], &sas_state[SAS_THRD_NEW])) {
+ fail("sas not restored");
+ } else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/signalfd00.c b/test/zdtm/static/signalfd00.c
new file mode 100644
index 000000000..5a1527382
--- /dev/null
+++ b/test/zdtm/static/signalfd00.c
@@ -0,0 +1,72 @@
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/signalfd.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check for signalfd without signals";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+int main(int argc, char *argv[])
+{
+ int fd, ret;
+ sigset_t mask;
+ siginfo_t info;
+
+ test_init(argc, argv);
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ fd = signalfd(-1, &mask, SFD_NONBLOCK);
+ if (fd < 0) {
+ fail("Can't create signalfd");
+ exit(1);
+ }
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ sigaddset(&mask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ test_daemon();
+ test_waitsig();
+
+ kill(getpid(), SIGUSR2);
+
+ ret = read(fd, &info, sizeof(info));
+ if (ret >= 0) {
+ fail("ghost signal");
+ exit(1);
+ }
+
+ kill(getpid(), SIGUSR1);
+
+ ret = read(fd, &info, sizeof(info));
+ if (ret != sizeof(info)) {
+ fail("no signal");
+ exit(1);
+ }
+
+ if (info.si_signo != SIGUSR1) {
+ fail("wrong signal");
+ exit(1);
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/sigpending.c b/test/zdtm/static/sigpending.c
new file mode 100644
index 000000000..2a4abfba0
--- /dev/null
+++ b/test/zdtm/static/sigpending.c
@@ -0,0 +1,274 @@
+#define _GNU_SOURCE
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <pthread.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check pending signals";
+const char *test_author = "Andrew Vagin <avagin@parallels.com>";
+
+static pid_t child;
+static int numsig;
+
+#define TESTSIG (SIGRTMAX)
+#define THREADSIG (SIGRTMIN)
+static siginfo_t share_infos[2];
+static siginfo_t self_infos[64]; /* self */
+static siginfo_t thread_infos[3]; /* thread */
+static int share_nr;
+static int self_nr;
+static int thread_nr;
+
+#ifndef offsetof
+# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+static pthread_mutex_t exit_lock;
+static pthread_mutex_t init_lock;
+
+static void sig_handler(int signal, siginfo_t *info, void *data)
+{
+ uint32_t crc;
+
+ test_msg("signo=%d si_code=%x\n", signal, info->si_code);
+
+ if (test_go()) {
+ pr_perror("The signal is received before unlocking");
+ return;
+ }
+
+ switch (signal) {
+ case SIGCHLD:
+ if ((info->si_code & CLD_EXITED) &&
+ (info->si_pid == child) &&
+ (info->si_status == 5))
+ numsig++;
+ else {
+ fail("Wrong siginfo");
+ exit(1);
+ }
+ return;
+ }
+
+ if (TESTSIG == signal || THREADSIG == signal) {
+ siginfo_t *src;
+
+ if (signal == TESTSIG) {
+ src = &share_infos[share_nr];
+ share_nr++;
+ } else if (getpid() == syscall(SYS_gettid)) {
+ src = &self_infos[self_nr];
+ self_nr++;
+ } else {
+ src = &thread_infos[thread_nr];
+ thread_nr++;
+ }
+
+ crc = ~0;
+ if (datachk((uint8_t *) &info->_sifields,
+ sizeof(siginfo_t) - offsetof(siginfo_t, _sifields), &crc)) {
+ fail("CRC mismatch\n");
+ return;
+ }
+
+ if (memcmp(info, src, sizeof(siginfo_t))) {
+ fail("Source and received info are differ\n");
+ return;
+ }
+
+ numsig++;
+ return;
+ }
+
+ pr_perror("Unexpected signal");
+ exit(1);
+}
+
+static int thread_id;
+
+static void *thread_fn(void *args)
+{
+ sigset_t blockmask, oldset, newset;
+ struct sigaction act;
+
+ memset(&oldset, 0, sizeof(oldset));
+ memset(&newset, 0, sizeof(oldset));
+
+ sigfillset(&blockmask);
+ sigdelset(&blockmask, SIGTERM);
+
+ if (sigprocmask(SIG_BLOCK, &blockmask, NULL) == -1) {
+ pr_perror("sigprocmask");
+ return NULL;
+ }
+
+ if (sigprocmask(SIG_SETMASK, NULL, &oldset) == -1) {
+ pr_perror("sigprocmask");
+ return NULL;
+ }
+
+ thread_id = syscall(SYS_gettid);
+
+ act.sa_flags = SA_SIGINFO | SA_RESTART;
+ act.sa_sigaction = sig_handler;
+ sigemptyset(&act.sa_mask);
+
+ sigaddset(&act.sa_mask, TESTSIG);
+ sigaddset(&act.sa_mask, THREADSIG);
+ if (sigaction(TESTSIG, &act, NULL)) {
+ pr_perror("sigaction() failed");
+ return NULL;
+ }
+
+ pthread_mutex_unlock(&init_lock);
+ pthread_mutex_lock(&exit_lock);
+
+ if (sigprocmask(SIG_UNBLOCK, &blockmask, &newset) == -1) {
+ pr_perror("sigprocmask");
+ return NULL;
+ }
+
+ sigdelset(&oldset, SIGTRAP);
+ sigdelset(&newset, SIGTRAP);
+ if (memcmp(&newset, &oldset, sizeof(newset))) {
+ fail("The signal blocking mask was changed");
+ numsig = INT_MAX;
+ }
+
+ return NULL;
+}
+
+static int sent_sigs;
+
+int send_siginfo(int signo, pid_t pid, pid_t tid, int group, siginfo_t *info)
+{
+ static int si_code = -10;
+ uint32_t crc = ~0;
+
+ info->si_code = si_code;
+ si_code--;
+ info->si_signo = signo;
+ datagen((uint8_t *) &info->_sifields,
+ sizeof(siginfo_t) - offsetof(siginfo_t, _sifields), &crc);
+
+ sent_sigs++;
+
+ if (group)
+ return syscall(SYS_rt_sigqueueinfo, pid, signo, info);
+ else
+ return syscall(SYS_rt_tgsigqueueinfo, pid, tid, signo, info);
+}
+
+int main(int argc, char ** argv)
+{
+ sigset_t blockmask, oldset, newset;
+ struct sigaction act;
+ pthread_t pthrd;
+ int i;
+
+ memset(&oldset, 0, sizeof(oldset));
+ memset(&newset, 0, sizeof(oldset));
+
+ test_init(argc, argv);
+ pthread_mutex_init(&exit_lock, NULL);
+ pthread_mutex_lock(&exit_lock);
+ pthread_mutex_init(&init_lock, NULL);
+ pthread_mutex_lock(&init_lock);
+
+ if (pthread_create(&pthrd, NULL, thread_fn, NULL)) {
+ pr_perror("Can't create a thread");
+ return 1;
+ }
+
+ pthread_mutex_lock(&init_lock);
+
+ sigfillset(&blockmask);
+ sigdelset(&blockmask, SIGTERM);
+
+ if (sigprocmask(SIG_BLOCK, &blockmask, NULL) == -1) {
+ pr_perror("sigprocmask");
+ return -1;
+ }
+
+ if (sigprocmask(SIG_BLOCK, NULL, &oldset) == -1) {
+ pr_perror("sigprocmask");
+ return -1;
+ }
+
+ child = fork();
+ if (child == -1) {
+ pr_perror("fork");
+ return -1;
+ }
+
+ if(child == 0)
+ return 5; /* SIGCHLD */
+ if (waitid(P_PID, child, NULL, WNOWAIT | WEXITED)) {
+ pr_perror("waitid");
+ return 1;
+ }
+
+ sent_sigs++;
+
+ for (i = 0; i < sizeof(share_infos) / sizeof(siginfo_t); i++) {
+ send_siginfo(TESTSIG, getpid(), -1, 1, share_infos + i);
+ }
+
+ for (i = 0; i < sizeof(self_infos) / sizeof(siginfo_t); i++) {
+ send_siginfo(THREADSIG, getpid(), getpid(), 0, self_infos + i);
+ }
+
+ for (i = 0; i < sizeof(thread_infos) / sizeof(siginfo_t); i++) {
+ send_siginfo(THREADSIG, getpid(), thread_id, 0, thread_infos + i);
+ }
+
+ act.sa_flags = SA_SIGINFO | SA_RESTART;
+ act.sa_sigaction = sig_handler;
+ sigemptyset(&act.sa_mask);
+
+ if (sigaction(SIGCHLD, &act, NULL)) {
+ pr_perror("sigaction() failed");
+ return -1;
+ }
+
+ sigaddset(&act.sa_mask, TESTSIG);
+ sigaddset(&act.sa_mask, THREADSIG);
+ if (sigaction(TESTSIG, &act, NULL)) {
+ pr_perror("sigaction() failed");
+ return -1;
+ }
+
+ if (sigaction(THREADSIG, &act, NULL)) {
+ pr_perror("sigaction() failed");
+ return -1;
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ if (sigprocmask(SIG_UNBLOCK, &blockmask, &newset) == -1) {
+ pr_perror("sigprocmask");
+ return -1;
+ }
+ pthread_mutex_unlock(&exit_lock);
+ pthread_join(pthrd, NULL);
+
+ sigdelset(&oldset, SIGTRAP);
+ sigdelset(&newset, SIGTRAP);
+ if (memcmp(&newset, &oldset, sizeof(newset))) {
+ fail("The signal blocking mask was changed");
+ return 1;
+ }
+
+ if (numsig == sent_sigs)
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/sk-freebind-false.c b/test/zdtm/static/sk-freebind-false.c
new file mode 120000
index 000000000..de243ce6e
--- /dev/null
+++ b/test/zdtm/static/sk-freebind-false.c
@@ -0,0 +1 @@
+sk-freebind.c \ No newline at end of file
diff --git a/test/zdtm/static/sk-freebind.c b/test/zdtm/static/sk-freebind.c
new file mode 100644
index 000000000..de30329c9
--- /dev/null
+++ b/test/zdtm/static/sk-freebind.c
@@ -0,0 +1,77 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <arpa/inet.h> /* for sockaddr_in and inet_ntoa() */
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that IP_FREEBIND is restored";
+const char *test_author = "Andrew Vagin <avagin@virtuozzo.com>";
+
+union sockaddr_inet {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+};
+
+#ifdef ZDTM_FREEBIND_FALSE
+static const int fb_keep = 0;
+static const int port = 56789;
+#else
+static const int fb_keep = 1;
+static const int port = 56787;
+#endif
+
+int main(int argc, char **argv)
+{
+ union sockaddr_inet addr;
+ socklen_t len;
+ int val, sock;
+
+ test_init(argc, argv);
+
+ addr.v6.sin6_family = AF_INET6;
+ inet_pton(AF_INET6, "2001:db8::ff00:42:8329", &(addr.v6.sin6_addr));
+ addr.v6.sin6_port = htons(port);
+
+ sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock == -1) {
+ pr_perror("socket() failed");
+ return -1;
+ }
+ val = 1;
+ if (setsockopt(sock, SOL_IP, IP_FREEBIND, &val, sizeof(int)) == -1 ) {
+ pr_perror("setsockopt() error");
+ return -1;
+ }
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr))) {
+ pr_perror("bind()");
+ return -1;
+ }
+
+ if (!fb_keep) {
+ val = 0;
+ if (setsockopt(sock, SOL_IP, IP_FREEBIND, &val, sizeof(int)) == -1 ) {
+ pr_perror("setsockopt() error");
+ return -1;
+ }
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ len = sizeof(int);
+ if (getsockopt(sock, SOL_IP, IP_FREEBIND, &val, &len) == -1 ) {
+ pr_perror("setsockopt() error");
+ return -1;
+ }
+
+ if (val != fb_keep) {
+ fail("Unexpected value: %d", val);
+ return -1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/sk-netlink.c b/test/zdtm/static/sk-netlink.c
new file mode 100644
index 000000000..f334246e2
--- /dev/null
+++ b/test/zdtm/static/sk-netlink.c
@@ -0,0 +1,160 @@
+#include <unistd.h>
+#include <linux/netlink.h>
+#include <sys/socket.h>
+#include <linux/socket.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+#define UDEV_MONITOR_TEST 32
+
+const char *test_doc = "Support of netlink sockets";
+const char *test_author = "Andrew Vagin <avagin@parallels.com>";
+
+int main(int argc, char ** argv)
+{
+ int ssk, bsk, csk, dsk;
+ struct sockaddr_nl addr;
+ struct msghdr msg;
+ struct {
+ struct nlmsghdr hdr;
+ } req;
+ struct iovec iov;
+ char buf[4096];
+
+ test_init(argc, argv);
+
+ ssk = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
+ if (ssk < 0) {
+ pr_perror("Can't create sock diag socket");
+ return -1;
+ }
+ bsk = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
+ if (bsk < 0) {
+ pr_perror("Can't create sock diag socket");
+ return -1;
+ }
+#if 0
+ int on, bbsk;
+
+ bbsk = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
+ if (bbsk < 0) {
+ pr_perror("Can't create sock diag socket");
+ return -1;
+ }
+
+ on = UDEV_MONITOR_TEST;
+ setsockopt(bbsk, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &on, sizeof(on));
+#endif
+ csk = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
+ if (csk < 0) {
+ pr_perror("Can't create sock diag socket");
+ return -1;
+ }
+ dsk = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
+ if (dsk < 0) {
+ pr_perror("Can't create sock diag socket");
+ return -1;
+ }
+
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = 0;
+ addr.nl_pid = getpid();
+ if (bind(ssk, (struct sockaddr *) &addr, sizeof(struct sockaddr_nl))) {
+ pr_perror("bind");
+ return 1;
+ }
+
+ addr.nl_groups = 1 << (UDEV_MONITOR_TEST - 1);
+ addr.nl_pid = 0;
+ if (bind(bsk, (struct sockaddr *) &addr, sizeof(struct sockaddr_nl))) {
+ pr_perror("bind");
+ return 1;
+ }
+
+ addr.nl_pid = getpid();;
+ addr.nl_groups = 1 << (UDEV_MONITOR_TEST - 1);
+ if (connect(csk, (struct sockaddr *) &addr, sizeof(struct sockaddr_nl))) {
+ pr_perror("connect");
+ return 1;
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ req.hdr.nlmsg_len = sizeof(req);
+ req.hdr.nlmsg_type = 0x1234;
+ req.hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+ req.hdr.nlmsg_seq = 0xabcd;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ iov.iov_base = (void *) &req;
+ iov.iov_len = sizeof(req);;
+
+ if (sendmsg(csk, &msg, 0) < 0) {
+ pr_perror("Can't send request message");
+ return 1;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+
+ if (recvmsg(ssk, &msg, 0) < 0) {
+ pr_perror("Can't recv request message");
+ return 1;
+ }
+
+ if (recvmsg(bsk, &msg, 0) < 0) {
+ pr_perror("Can't recv request message");
+ return 1;
+ }
+
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = 0;
+ addr.nl_pid = getpid();
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_namelen = sizeof(addr);
+ msg.msg_name = &addr;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ iov.iov_base = (void *) &req;
+ iov.iov_len = sizeof(req);;
+
+ if (sendmsg(dsk, &msg, 0) < 0) {
+ pr_perror("Can't send request message");
+ return 1;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+
+ if (recvmsg(ssk, &msg, 0) < 0) {
+ pr_perror("Can't recv request message");
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/sk-netlink.desc b/test/zdtm/static/sk-netlink.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/sk-netlink.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/sk-unix-rel.c b/test/zdtm/static/sk-unix-rel.c
new file mode 100644
index 000000000..d341ce9af
--- /dev/null
+++ b/test/zdtm/static/sk-unix-rel.c
@@ -0,0 +1,110 @@
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test unix stream sockets with relative name\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org";
+
+#define SK_DATA "packet"
+
+char *filename;
+TEST_OPTION(filename, string, "socket file name", 1);
+
+#define TEST_MODE 0640
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_un addr;
+ unsigned int addrlen;
+ int sock[2];
+
+ char path[PATH_MAX];
+ char buf[64];
+ char *cwd;
+ int ret;
+
+ test_init(argc, argv);
+
+ cwd = get_current_dir_name();
+ if (!cwd) {
+ fail("getcwd\n");
+ exit(1);
+ }
+
+ snprintf(path, sizeof(path), "%s/%s", cwd, filename);
+ unlink(path);
+
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, filename, sizeof(addr.sun_path));
+ addrlen = sizeof(addr.sun_family) + strlen(filename);
+
+ sock[0] = socket(AF_UNIX, SOCK_STREAM, 0);
+ sock[1] = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock[0] < 0 || sock[1] < 0) {
+ fail("socket\n");
+ exit(1);
+ }
+
+ if (setsockopt(sock[0], SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0 ||
+ setsockopt(sock[1], SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0) {
+ fail("setsockopt\n");
+ exit(1);
+ }
+
+ ret = bind(sock[0], &addr, addrlen);
+ if (ret) {
+ fail("bind\n");
+ exit(1);
+ }
+
+ ret = listen(sock[0], 16);
+ if (ret) {
+ fail("bind\n");
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (connect(sock[1], &addr, addrlen)) {
+ fail("connect\n");
+ exit(1);
+ }
+
+ ret = accept(sock[0], NULL, NULL);
+ if (ret < 0) {
+ fail("accept");
+ exit(1);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ write(sock[1], SK_DATA, sizeof(SK_DATA));
+ read(ret, &buf, sizeof(buf));
+
+ if (strcmp(buf, SK_DATA)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("stream : '%s'\n", buf);
+ close(sock[0]);
+ close(sock[1]);
+ unlink(path);
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/sk-unix-unconn.c b/test/zdtm/static/sk-unix-unconn.c
new file mode 100644
index 000000000..8e600130d
--- /dev/null
+++ b/test/zdtm/static/sk-unix-unconn.c
@@ -0,0 +1,64 @@
+#include <unistd.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check unconnected unix sockets";
+const char *test_author = "Vagin Andrew <avagin@parallels.com>";
+
+int main(int argc, char ** argv)
+{
+ int sk, skc;
+ int ret;
+ char path[PATH_MAX];
+ struct sockaddr_un addr;
+ socklen_t addrlen;
+
+ test_init(argc, argv);
+
+ sk = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sk == -1) {
+ pr_perror("socket");
+ return 1;
+ }
+
+ skc = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (skc == -1) {
+ pr_perror("socket");
+ return 1;
+ }
+
+ snprintf(path, sizeof(path), "X/zdtm-%s-%d", argv[0], getpid());
+
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+ addrlen = sizeof(addr.sun_family) + strlen(path);
+ addr.sun_path[0] = 0;
+
+ ret = bind(sk, (struct sockaddr *) &addr, addrlen);
+ if (ret) {
+ fail("bind\n");
+ return 1;
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ if (listen(sk, 1) == -1) {
+ pr_perror("listen");
+ return 1;
+ }
+
+ if (connect(skc, (struct sockaddr *) &addr, addrlen) == -1) {
+ fail("Unable to connect");
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/sleeping00.c b/test/zdtm/static/sleeping00.c
new file mode 100644
index 000000000..f59ffaf1f
--- /dev/null
+++ b/test/zdtm/static/sleeping00.c
@@ -0,0 +1,18 @@
+#include <unistd.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Suspend while migrating";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+int main(int argc, char ** argv)
+{
+ test_init(argc, argv);
+
+ test_daemon();
+ test_waitsig();
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/sock_filter.c b/test/zdtm/static/sock_filter.c
new file mode 100644
index 000000000..e2475cd89
--- /dev/null
+++ b/test/zdtm/static/sock_filter.c
@@ -0,0 +1,115 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/filter.h>
+#include <linux/in.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check socket filter";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+#ifndef SO_GET_FILTER
+#define SO_GET_FILTER SO_ATTACH_FILTER
+#endif
+
+#define SFLEN 14
+
+int main(int argc, char **argv)
+{
+ int sk;
+ struct sock_fprog p;
+ struct sock_filter f[SFLEN] = {
+ { 0x28, 0, 0, 0x0000000c },
+ { 0x15, 0, 4, 0x00000800 },
+ { 0x20, 0, 0, 0x0000001a },
+ { 0x15, 8, 0, 0x7f000001 },
+ { 0x20, 0, 0, 0x0000001e },
+ { 0x15, 6, 7, 0x7f000001 },
+ { 0x15, 1, 0, 0x00000806 },
+ { 0x15, 0, 5, 0x00008035 },
+ { 0x20, 0, 0, 0x0000001c },
+ { 0x15, 2, 0, 0x7f000001 },
+ { 0x20, 0, 0, 0x00000026 },
+ { 0x15, 0, 1, 0x7f000001 },
+ { 0x6, 0, 0, 0x0000ffff },
+ { 0x6, 0, 0, 0x00000000 },
+ };
+ struct sock_filter f2[SFLEN], f3[SFLEN];
+ socklen_t len;
+
+ test_init(argc, argv);
+
+ sk = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sk < 0) {
+ pr_perror("No socket");
+ return 1;
+ }
+
+ p.len = SFLEN;
+ p.filter = f;
+
+ if (setsockopt(sk, SOL_SOCKET, SO_ATTACH_FILTER, &p, sizeof(p))) {
+ pr_perror("No filter");
+ return 1;
+ }
+
+ len = 0;
+ if (getsockopt(sk, SOL_SOCKET, SO_GET_FILTER, NULL, &len)) {
+ pr_perror("No len");
+ return 1;
+ }
+
+ if (len != SFLEN) {
+ pr_perror("Len mismatch");
+ return 1;
+ }
+
+ memset(f2, 0, sizeof(f2));
+ if (getsockopt(sk, SOL_SOCKET, SO_GET_FILTER, f2, &len)) {
+ perror("No filter");
+ return 1;
+ }
+
+ if (len != SFLEN) {
+ pr_perror("Len mismatch2");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ len = 0;
+ if (getsockopt(sk, SOL_SOCKET, SO_GET_FILTER, NULL, &len)) {
+ fail("No len");
+ return 1;
+ }
+
+ if (len != SFLEN) {
+ fail("Len mismatch");
+ return 1;
+ }
+
+ memset(f3, 0, sizeof(f3));
+ if (getsockopt(sk, SOL_SOCKET, SO_GET_FILTER, f3, &len)) {
+ fail("No filter");
+ return 1;
+ }
+
+ if (len != SFLEN) {
+ fail("Len mismatch2");
+ return 1;
+ }
+
+ if (memcmp(f2, f3, sizeof(f2))) {
+ fail("Filters mismatch");
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
+
diff --git a/test/zdtm/static/sock_opts00.c b/test/zdtm/static/sock_opts00.c
new file mode 100644
index 000000000..08fc1d357
--- /dev/null
+++ b/test/zdtm/static/sock_opts00.c
@@ -0,0 +1,91 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check various socket options to work";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+#define TEST_PORT 59687
+#define TEST_ADDR INADDR_ANY
+
+#define NOPTS 7
+
+int main(int argc, char ** argv)
+{
+ int sock, ret = 0, vname[NOPTS], val[NOPTS], rval, i;
+ socklen_t len = sizeof(int);
+
+ vname[0] = SO_PRIORITY;
+ vname[1] = SO_RCVLOWAT;
+ vname[2] = SO_MARK;
+ vname[3] = SO_PASSCRED;
+ vname[4] = SO_PASSSEC;
+ vname[5] = SO_DONTROUTE;
+ vname[6] = SO_NO_CHECK;
+
+ test_init(argc, argv);
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ pr_perror("can't create socket");
+ return 1;
+ }
+
+ for (i = 0; i < NOPTS; i++) {
+ ret = getsockopt(sock, SOL_SOCKET, vname[i], &val[i], &len);
+ if (ret) {
+ pr_perror("can't get option %d", i);
+ return 1;
+ }
+
+ val[i]++;
+
+ ret = setsockopt(sock, SOL_SOCKET, vname[i], &val[i], len);
+ if (ret) {
+ pr_perror("can't set option %d", i);
+ return 1;
+ }
+
+ ret = getsockopt(sock, SOL_SOCKET, vname[i], &rval, &len);
+ if (ret) {
+ pr_perror("can't get option %d 2", i);
+ return 1;
+ }
+
+ if (rval != val[i]) {
+ if (rval + 1 == val[i]) {
+ pr_perror("can't reset option %d want %d have %d", i,
+ val[i], rval);
+ return 1;
+ }
+
+ /* kernel tuned things up on set */
+ val[i] = rval;
+ }
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ for (i = 0; i < NOPTS; i++) {
+ ret = getsockopt(sock, SOL_SOCKET, vname[i], &rval, &len);
+ if (ret) {
+ pr_perror("can't get option %d again", i);
+ return 1;
+ }
+
+ if (val[i] != rval) {
+ fail("option %d changed", i);
+ return 1;
+ }
+ }
+
+ pass();
+ close(sock);
+
+ return 0;
+}
diff --git a/test/zdtm/static/sock_opts00.desc b/test/zdtm/static/sock_opts00.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/sock_opts00.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/sock_opts01.c b/test/zdtm/static/sock_opts01.c
new file mode 100644
index 000000000..7dfce2257
--- /dev/null
+++ b/test/zdtm/static/sock_opts01.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that SO_BINDTODEVICE option works";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+int main(int argc, char ** argv)
+{
+ int sock, ret;
+ char dev[IFNAMSIZ], dev2[IFNAMSIZ];
+ socklen_t len, len2;
+
+ test_init(argc, argv);
+
+ sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0) {
+ pr_perror("can't create socket");
+ return 1;
+ }
+
+ ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5);
+ if (ret < 0)
+ ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3);
+ if (ret < 0) {
+ pr_perror("can't bind to eth0");
+ return 1;
+ }
+
+ len = sizeof(dev);
+ ret = getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &dev, &len);
+ if (ret < 0) {
+ pr_perror("can't get dev binding");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ len2 = sizeof(dev);
+ ret = getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &dev2, &len2);
+ if (ret < 0) {
+ fail("can't get dev binding2");
+ return 1;
+ }
+
+ if ((len != len2) || strncmp(dev, dev2, len))
+ fail("wrong bound device");
+ else
+ pass();
+
+ close(sock);
+
+ return 0;
+}
diff --git a/test/zdtm/static/sock_opts01.desc b/test/zdtm/static/sock_opts01.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/sock_opts01.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/socket-closed-tcp.c b/test/zdtm/static/socket-closed-tcp.c
new file mode 100644
index 000000000..58fc7d7b0
--- /dev/null
+++ b/test/zdtm/static/socket-closed-tcp.c
@@ -0,0 +1,58 @@
+#include "zdtmtst.h"
+
+#ifdef ZDTM_IPV6
+#define ZDTM_FAMILY AF_INET6
+#else
+#define ZDTM_FAMILY AF_INET
+#endif
+
+const char *test_doc = "Check, that a TCP socket in the TCP_CLOSE state can be restored\n";
+const char *test_author = "Andrey Vagin <avagin@openvz.org";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <netinet/tcp.h>
+
+static int port = 8880;
+
+int main(int argc, char **argv)
+{
+ int fd, fd_s, clt;
+
+ test_init(argc, argv);
+
+ if ((fd_s = tcp_init_server(ZDTM_FAMILY, &port)) < 0) {
+ pr_err("initializing server failed\n");
+ return 1;
+ }
+
+ clt = tcp_init_client(ZDTM_FAMILY, "localhost", port);
+ if (clt < 0)
+ return 1;
+
+ /*
+ * parent is server of TCP connection
+ */
+ fd = tcp_accept_server(fd_s);
+ if (fd < 0) {
+ pr_err("can't accept client connection\n");
+ return 1;
+ }
+ close(fd_s);
+
+ shutdown(fd, SHUT_WR);
+ shutdown(clt, SHUT_WR);
+ close(fd);
+
+ test_daemon();
+ test_waitsig();
+
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket-closed-tcp.desc b/test/zdtm/static/socket-closed-tcp.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/socket-closed-tcp.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/socket-ext.c b/test/zdtm/static/socket-ext.c
new file mode 100644
index 000000000..bfc2925e2
--- /dev/null
+++ b/test/zdtm/static/socket-ext.c
@@ -0,0 +1,101 @@
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test external sockets\n";
+const char *test_author = "Andrey Vagin <avagin@openvz.org";
+
+#define SK_DATA "packet"
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_un addr;
+ unsigned int addrlen;
+ task_waiter_t lock;
+
+ char path[PATH_MAX] = "/tmp/zdtm.unix.sock.XXXXXX";
+ pid_t pid;
+ int ret, sk;
+
+ mktemp(path);
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+ addrlen = sizeof(addr.sun_family) + strlen(path);
+
+ task_waiter_init(&lock);
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork() failed");
+ return 1;
+ } else if (pid == 0) {
+ char c;
+ test_ext_init(argc, argv);
+
+ sk = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (sk < 0) {
+ pr_perror("Can't create socket");
+ return 1;
+ }
+ ret = bind(sk, &addr, addrlen);
+ if (ret < 0) {
+ pr_perror("Can't bind socket to %s", path);
+ return 1;
+ }
+ chmod(path, 0777);
+ test_msg("The external socket %s\n", path);
+ task_waiter_complete(&lock, 1);
+ task_waiter_fini(&lock);
+
+ recv(sk, &c, sizeof(c), 0);
+
+ return 0;
+ }
+
+ test_init(argc, argv);
+
+ task_waiter_wait4(&lock, 1);
+ task_waiter_fini(&lock);
+
+ sk = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (sk < 0) {
+ pr_perror("Can't create socket");
+ return 1;
+ }
+
+ ret = connect(sk, &addr, addrlen);
+ if (ret < 0) {
+ pr_perror("Can't connect socket");
+ return 1;
+ }
+
+
+ test_daemon();
+ test_waitsig();
+
+ unlink(path);
+
+ ret = send(sk, "H", 1, 0);
+ if (ret != 1) {
+ pr_perror("Can't send a symbol");
+ fail();
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket-ext.desc b/test/zdtm/static/socket-ext.desc
new file mode 100644
index 000000000..5ecad7b47
--- /dev/null
+++ b/test/zdtm/static/socket-ext.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'opts': '--ext-unix-sk'}
diff --git a/test/zdtm/static/socket-ext.opts b/test/zdtm/static/socket-ext.opts
new file mode 100644
index 000000000..de3680778
--- /dev/null
+++ b/test/zdtm/static/socket-ext.opts
@@ -0,0 +1 @@
+--ext-unix-sk
diff --git a/test/zdtm/static/socket-tcp-local.c b/test/zdtm/static/socket-tcp-local.c
new file mode 120000
index 000000000..8cb60dd03
--- /dev/null
+++ b/test/zdtm/static/socket-tcp-local.c
@@ -0,0 +1 @@
+socket-tcp.c \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcp-local.desc b/test/zdtm/static/socket-tcp-local.desc
new file mode 100644
index 000000000..e02979edf
--- /dev/null
+++ b/test/zdtm/static/socket-tcp-local.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns uns', 'opts': '--tcp-established', 'flags': 'nouser'}
diff --git a/test/zdtm/static/socket-tcp-local.opts b/test/zdtm/static/socket-tcp-local.opts
new file mode 120000
index 000000000..5f40ff841
--- /dev/null
+++ b/test/zdtm/static/socket-tcp-local.opts
@@ -0,0 +1 @@
+socket-tcp.opts \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcp-nfconntrack.c b/test/zdtm/static/socket-tcp-nfconntrack.c
new file mode 120000
index 000000000..8cb60dd03
--- /dev/null
+++ b/test/zdtm/static/socket-tcp-nfconntrack.c
@@ -0,0 +1 @@
+socket-tcp.c \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcp-nfconntrack.desc b/test/zdtm/static/socket-tcp-nfconntrack.desc
new file mode 100644
index 000000000..add2513f8
--- /dev/null
+++ b/test/zdtm/static/socket-tcp-nfconntrack.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'opts': '--tcp-established', 'flags': 'suid'}
diff --git a/test/zdtm/static/socket-tcp.c b/test/zdtm/static/socket-tcp.c
new file mode 100644
index 000000000..3eca8de26
--- /dev/null
+++ b/test/zdtm/static/socket-tcp.c
@@ -0,0 +1,213 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "zdtmtst.h"
+
+#ifdef ZDTM_IPV6
+#define ZDTM_FAMILY AF_INET6
+#else
+#define ZDTM_FAMILY AF_INET
+#endif
+
+const char *test_doc = "Check, that a TCP connection can be restored\n";
+const char *test_author = "Andrey Vagin <avagin@parallels.com";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sched.h>
+#include <netinet/tcp.h>
+
+static int port = 8880;
+
+#define BUF_SIZE 4096
+
+int read_data(int fd, unsigned char *buf, int size)
+{
+ int cur = 0;
+ int ret;
+ while (cur != size) {
+ ret = read(fd, buf + cur, size - cur);
+ if (ret <= 0)
+ return -1;
+ cur += ret;
+ }
+
+ return 0;
+}
+
+int write_data(int fd, const unsigned char *buf, int size)
+{
+ int cur = 0;
+ int ret;
+
+ while (cur != size) {
+ ret = write(fd, buf + cur, size - cur);
+ if (ret <= 0)
+ return -1;
+ cur += ret;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ unsigned char buf[BUF_SIZE];
+ int fd, fd_s;
+ pid_t extpid;
+ uint32_t crc;
+ int pfd[2];
+ int val;
+ socklen_t optlen;
+
+#ifdef ZDTM_CONNTRACK
+ unshare(CLONE_NEWNET);
+ if (system("ip link set up dev lo"))
+ return 1;
+ if (system("iptables -A INPUT -i lo -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT"))
+ return 1;
+ system("iptables -A INPUT -j DROP");
+#endif
+
+#ifdef ZDTM_TCP_LOCAL
+ test_init(argc, argv);
+#endif
+
+ if (pipe(pfd)) {
+ pr_perror("pipe() failed");
+ return 1;
+ }
+
+ extpid = fork();
+ if (extpid < 0) {
+ pr_perror("fork() failed");
+ return 1;
+ } else if (extpid == 0) {
+#ifndef ZDTM_TCP_LOCAL
+ test_ext_init(argc, argv);
+#endif
+
+ close(pfd[1]);
+ if (read(pfd[0], &port, sizeof(port)) != sizeof(port)) {
+ pr_perror("Can't read port");
+ return 1;
+ }
+
+ fd = tcp_init_client(ZDTM_FAMILY, "localhost", port);
+ if (fd < 0)
+ return 1;
+
+#ifdef STREAM
+ while (1) {
+ if (read_data(fd, buf, BUF_SIZE)) {
+ pr_perror("read less then have to");
+ return 1;
+ }
+ if (datachk(buf, BUF_SIZE, &crc))
+ return 2;
+
+ datagen(buf, BUF_SIZE, &crc);
+ if (write_data(fd, buf, BUF_SIZE)) {
+ pr_perror("can't write");
+ return 1;
+ }
+ }
+#else
+ if (read_data(fd, buf, BUF_SIZE)) {
+ pr_perror("read less then have to");
+ return 1;
+ }
+ if (datachk(buf, BUF_SIZE, &crc))
+ return 2;
+
+ datagen(buf, BUF_SIZE, &crc);
+ if (write_data(fd, buf, BUF_SIZE)) {
+ pr_perror("can't write");
+ return 1;
+ }
+#endif
+ return 0;
+ }
+
+#ifndef ZDTM_TCP_LOCAL
+ test_init(argc, argv);
+#endif
+
+ if ((fd_s = tcp_init_server(ZDTM_FAMILY, &port)) < 0) {
+ pr_err("initializing server failed\n");
+ return 1;
+ }
+
+ close(pfd[0]);
+ if (write(pfd[1], &port, sizeof(port)) != sizeof(port)) {
+ pr_perror("Can't send port");
+ return 1;
+ }
+ close(pfd[1]);
+
+ /*
+ * parent is server of TCP connection
+ */
+ fd = tcp_accept_server(fd_s);
+ if (fd < 0) {
+ pr_err("can't accept client connection\n");
+ return 1;
+ }
+
+ val = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) {
+ pr_perror("setsockopt");
+ return 1;
+ }
+
+ test_daemon();
+#ifdef STREAM
+ while (test_go()) {
+ datagen(buf, BUF_SIZE, &crc);
+ if (write_data(fd, buf, BUF_SIZE)) {
+ pr_perror("can't write");
+ return 1;
+ }
+
+ if (read_data(fd, buf, BUF_SIZE)) {
+ pr_perror("read less then have to");
+ return 1;
+ }
+ if (datachk(buf, BUF_SIZE, &crc))
+ return 2;
+ }
+ kill(extpid, SIGKILL);
+#else
+ test_waitsig();
+
+ datagen(buf, BUF_SIZE, &crc);
+ if (write_data(fd, buf, BUF_SIZE)) {
+ pr_perror("can't write");
+ return 1;
+ }
+
+ if (read_data(fd, buf, BUF_SIZE)) {
+ pr_perror("read less then have to");
+ return 1;
+ }
+ if (datachk(buf, BUF_SIZE, &crc))
+ return 2;
+#endif
+ optlen = sizeof(val);
+ if (getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, &optlen)) {
+ pr_perror("getsockopt");
+ return 1;
+ }
+ if (val != 1) {
+ fail("SO_REUSEADDR are not set for %d\n", fd);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket-tcp.desc b/test/zdtm/static/socket-tcp.desc
new file mode 100644
index 000000000..7c1ae7a78
--- /dev/null
+++ b/test/zdtm/static/socket-tcp.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'opts': '--tcp-established', 'flags': 'nouser'}
diff --git a/test/zdtm/static/socket-tcp.opts b/test/zdtm/static/socket-tcp.opts
new file mode 100644
index 000000000..f016a5435
--- /dev/null
+++ b/test/zdtm/static/socket-tcp.opts
@@ -0,0 +1 @@
+--tcp-established
diff --git a/test/zdtm/static/socket-tcp6-local.c b/test/zdtm/static/socket-tcp6-local.c
new file mode 120000
index 000000000..8cb60dd03
--- /dev/null
+++ b/test/zdtm/static/socket-tcp6-local.c
@@ -0,0 +1 @@
+socket-tcp.c \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcp6-local.desc b/test/zdtm/static/socket-tcp6-local.desc
new file mode 120000
index 000000000..c915663eb
--- /dev/null
+++ b/test/zdtm/static/socket-tcp6-local.desc
@@ -0,0 +1 @@
+socket-tcp-local.desc \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcp6-local.opts b/test/zdtm/static/socket-tcp6-local.opts
new file mode 120000
index 000000000..5f40ff841
--- /dev/null
+++ b/test/zdtm/static/socket-tcp6-local.opts
@@ -0,0 +1 @@
+socket-tcp.opts \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcp6.c b/test/zdtm/static/socket-tcp6.c
new file mode 120000
index 000000000..8cb60dd03
--- /dev/null
+++ b/test/zdtm/static/socket-tcp6.c
@@ -0,0 +1 @@
+socket-tcp.c \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcp6.desc b/test/zdtm/static/socket-tcp6.desc
new file mode 100644
index 000000000..7c1ae7a78
--- /dev/null
+++ b/test/zdtm/static/socket-tcp6.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'opts': '--tcp-established', 'flags': 'nouser'}
diff --git a/test/zdtm/static/socket-tcp6.opts b/test/zdtm/static/socket-tcp6.opts
new file mode 100644
index 000000000..f016a5435
--- /dev/null
+++ b/test/zdtm/static/socket-tcp6.opts
@@ -0,0 +1 @@
+--tcp-established
diff --git a/test/zdtm/static/socket-tcpbuf-local.c b/test/zdtm/static/socket-tcpbuf-local.c
new file mode 120000
index 000000000..58c46c74a
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf-local.c
@@ -0,0 +1 @@
+socket-tcpbuf.c \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcpbuf-local.desc b/test/zdtm/static/socket-tcpbuf-local.desc
new file mode 100644
index 000000000..e02979edf
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf-local.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns uns', 'opts': '--tcp-established', 'flags': 'nouser'}
diff --git a/test/zdtm/static/socket-tcpbuf-local.opts b/test/zdtm/static/socket-tcpbuf-local.opts
new file mode 100644
index 000000000..f016a5435
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf-local.opts
@@ -0,0 +1 @@
+--tcp-established
diff --git a/test/zdtm/static/socket-tcpbuf.c b/test/zdtm/static/socket-tcpbuf.c
new file mode 100644
index 000000000..f61f6a507
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf.c
@@ -0,0 +1,321 @@
+#include "zdtmtst.h"
+
+#ifdef ZDTM_IPV6
+#define ZDTM_FAMILY AF_INET6
+#else
+#define ZDTM_FAMILY AF_INET
+#endif
+
+const char *test_doc = "Check full tcp buffers with custom sizes\n";
+const char *test_author = "Andrey Vagin <avagin@parallels.com";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+
+static int port = 8880;
+
+#define BUF_SIZE 4096
+#define TCP_MAX_BUF (100 << 20)
+
+static void read_safe(int fd, void *buf, size_t size)
+{
+ if (read(fd, buf, size) != size) {
+ pr_perror("Unable to read from %d", fd);
+ exit(1);
+ }
+}
+
+static void write_safe(int fd, void *buf, size_t size)
+{
+ if (write(fd, buf, size) != size) {
+ pr_perror("Unable to write to %d", fd);
+ exit(1);
+ }
+}
+
+static int fill_sock_buf(int fd)
+{
+ int flags;
+ int size;
+ int ret;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) {
+ pr_perror("Can't get flags");
+ return -1;
+ }
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
+ pr_perror("Can't set flags");
+ return -1;
+ }
+
+ size = 0;
+ while (1) {
+ char zdtm[] = "zdtm test packet";
+ ret = write(fd, zdtm, sizeof(zdtm));
+ if (ret == -1) {
+ if (errno == EAGAIN)
+ break;
+ pr_perror("write");
+ return -1;
+ }
+ size += ret;
+ }
+
+ if (fcntl(fd, F_SETFL, flags) == -1) {
+ pr_perror("Can't set flags");
+ return -1;
+ }
+
+ return size;
+}
+
+static int clean_sk_buf(int fd, int limit)
+{
+ int size, ret;
+ char buf[BUF_SIZE];
+
+ size = 0;
+ while (1) {
+ ret = read(fd, buf, sizeof(buf));
+ if (ret == -1) {
+ pr_perror("read");
+ return -11;
+ }
+
+ if (ret == 0)
+ break;
+
+ size += ret;
+
+ if (limit && size >= limit)
+ break;
+ }
+
+ return size;
+}
+
+int main(int argc, char **argv)
+{
+ int fd, fd_s, ctl_fd;
+ pid_t extpid;
+ int pfd[2];
+ int sk_bsize;
+ int ret, snd, snd_size, rcv_size = 0, rcv_buf_size;
+
+#ifdef ZDTM_TCP_LOCAL
+ test_init(argc, argv);
+#endif
+
+ if (pipe(pfd)) {
+ pr_perror("pipe() failed");
+ return 1;
+ }
+
+ extpid = fork();
+ if (extpid < 0) {
+ pr_perror("fork() failed");
+ return 1;
+ } else if (extpid == 0) {
+ int size;
+ char c;
+
+#ifndef ZDTM_TCP_LOCAL
+ test_ext_init(argc, argv);
+#endif
+
+ close(pfd[1]);
+ read_safe(pfd[0], &port, sizeof(port));
+
+ fd = tcp_init_client(ZDTM_FAMILY, "127.0.0.1", port);
+ if (fd < 0)
+ return 1;
+
+ ctl_fd = tcp_init_client(ZDTM_FAMILY, "127.0.0.1", port);
+ if (fd < 0)
+ return 1;
+
+ snd_size = fill_sock_buf(fd);
+ if (snd_size <= 0)
+ return 1;
+
+ write_safe(ctl_fd, &snd_size, sizeof(snd_size));
+
+ read_safe(ctl_fd, &rcv_buf_size, sizeof(rcv_buf_size));
+
+ while (1) {
+ /* heart beat */
+ read_safe(ctl_fd, &ret, sizeof(ret));
+ if (ret < 0)
+ break;
+ rcv_buf_size += ret;
+
+ snd = fill_sock_buf(fd);
+ if (snd < 0)
+ return -1;
+ snd_size += snd;
+
+ if (rcv_buf_size / 2) {
+ ret = clean_sk_buf(fd, rcv_buf_size / 2);
+ if (ret <= 0)
+ return 1;
+ } else
+ ret = 0;
+
+ rcv_buf_size -= ret;
+ rcv_size += ret;
+
+ write_safe(ctl_fd, &snd, sizeof(snd));
+ }
+
+ read_safe(ctl_fd, &ret, sizeof(ret));
+ rcv_buf_size += ret;
+
+ write_safe(ctl_fd, &snd_size, sizeof(snd_size));
+
+ if (read(ctl_fd, &c, 1) != 0) {
+ pr_perror("read");
+ return 1;
+ }
+
+ if (shutdown(fd, SHUT_WR) == -1) {
+ pr_perror("shutdown");
+ return 1;
+ }
+
+ size = clean_sk_buf(fd, 0);
+ if (size < 0)
+ return 1;
+
+ if (size != rcv_buf_size) {
+ fail("the received buffer contains only %d bytes (%d)\n", size, rcv_buf_size);
+ }
+
+ rcv_size += size;
+
+ write_safe(ctl_fd, &rcv_size, sizeof(rcv_size));
+ close(fd);
+
+ return 0;
+ }
+
+#ifndef ZDTM_TCP_LOCAL
+ test_init(argc, argv);
+#endif
+
+ if ((fd_s = tcp_init_server(ZDTM_FAMILY, &port)) < 0) {
+ pr_err("initializing server failed\n");
+ return 1;
+ }
+
+ close(pfd[0]);
+ write_safe(pfd[1], &port, sizeof(port));
+ close(pfd[1]);
+
+ /*
+ * parent is server of TCP connection
+ */
+ fd = tcp_accept_server(fd_s);
+ if (fd < 0) {
+ pr_err("can't accept client connection\n");
+ return 1;
+ }
+
+ ctl_fd = tcp_accept_server(fd_s);
+ if (ctl_fd < 0) {
+ pr_err("can't accept client connection\n");
+ return 1;
+ }
+
+ sk_bsize = TCP_MAX_BUF;
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
+ &sk_bsize, sizeof(sk_bsize)) == -1) {
+ pr_perror("Can't set snd buf");
+ return 1;
+ }
+
+ sk_bsize = TCP_MAX_BUF;
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
+ &sk_bsize, sizeof(sk_bsize)) == -1) {
+ pr_perror("Can't set snd buf");
+ return 1;
+ }
+
+ snd_size = fill_sock_buf(fd);
+ if (snd_size <= 0)
+ return 1;
+
+ read_safe(ctl_fd, &rcv_buf_size, sizeof(rcv_buf_size));
+
+ write_safe(ctl_fd, &snd_size, sizeof(snd_size));
+
+ test_daemon();
+
+ snd = 0;
+ while (test_go()) {
+ /* heart beat */
+ if (rcv_buf_size / 2) {
+ ret = clean_sk_buf(fd, rcv_buf_size / 2);
+ if (ret <= 0)
+ return 1;
+ } else
+ ret = 0;
+
+ rcv_size += ret;
+ rcv_buf_size -= ret;
+
+ write_safe(ctl_fd, &snd, sizeof(snd));
+ read_safe(ctl_fd, &ret, sizeof(ret));
+
+ rcv_buf_size += ret;
+
+ snd = fill_sock_buf(fd);
+ if (snd < 0)
+ return -1;
+ snd_size += snd;
+ }
+
+ ret = -1;
+ write_safe(ctl_fd, &ret, sizeof(ret));
+ write_safe(ctl_fd, &snd, sizeof(ret));
+ read_safe(ctl_fd, &snd, sizeof(snd));
+
+ if (shutdown(ctl_fd, SHUT_WR) == -1) {
+ pr_perror("shutdown");
+ return 1;
+ }
+
+ if (shutdown(fd, SHUT_WR) == -1) {
+ pr_perror("shutdown");
+ return 1;
+ }
+
+ ret = clean_sk_buf(fd, 0);
+ if (ret != rcv_buf_size) {
+ fail("the received buffer contains only %d bytes (%d)\n", ret, rcv_buf_size);
+ }
+ rcv_size += ret;
+
+ if (snd != rcv_size) {
+ fail("The child sent %d bytes, but the parent received %d bytes\n", rcv_buf_size, rcv_size);
+ return 1;
+ }
+
+ read_safe(ctl_fd, &ret, sizeof(ret));
+
+ if (ret != snd_size) {
+ fail("The parent sent %d bytes, but the child received %d bytes\n", snd_size, ret);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket-tcpbuf.desc b/test/zdtm/static/socket-tcpbuf.desc
new file mode 100644
index 000000000..7c1ae7a78
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'opts': '--tcp-established', 'flags': 'nouser'}
diff --git a/test/zdtm/static/socket-tcpbuf.opts b/test/zdtm/static/socket-tcpbuf.opts
new file mode 100644
index 000000000..f016a5435
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf.opts
@@ -0,0 +1 @@
+--tcp-established
diff --git a/test/zdtm/static/socket-tcpbuf6-local.c b/test/zdtm/static/socket-tcpbuf6-local.c
new file mode 120000
index 000000000..58c46c74a
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf6-local.c
@@ -0,0 +1 @@
+socket-tcpbuf.c \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcpbuf6-local.desc b/test/zdtm/static/socket-tcpbuf6-local.desc
new file mode 120000
index 000000000..3c9afd2b8
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf6-local.desc
@@ -0,0 +1 @@
+socket-tcpbuf-local.desc \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcpbuf6-local.opts b/test/zdtm/static/socket-tcpbuf6-local.opts
new file mode 120000
index 000000000..ed660979e
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf6-local.opts
@@ -0,0 +1 @@
+socket-tcpbuf.opts \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcpbuf6.c b/test/zdtm/static/socket-tcpbuf6.c
new file mode 120000
index 000000000..58c46c74a
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf6.c
@@ -0,0 +1 @@
+socket-tcpbuf.c \ No newline at end of file
diff --git a/test/zdtm/static/socket-tcpbuf6.desc b/test/zdtm/static/socket-tcpbuf6.desc
new file mode 100644
index 000000000..7c1ae7a78
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf6.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'opts': '--tcp-established', 'flags': 'nouser'}
diff --git a/test/zdtm/static/socket-tcpbuf6.opts b/test/zdtm/static/socket-tcpbuf6.opts
new file mode 100644
index 000000000..f016a5435
--- /dev/null
+++ b/test/zdtm/static/socket-tcpbuf6.opts
@@ -0,0 +1 @@
+--tcp-established
diff --git a/test/zdtm/static/socket6_udp.c b/test/zdtm/static/socket6_udp.c
new file mode 100644
index 000000000..30ddc9a40
--- /dev/null
+++ b/test/zdtm/static/socket6_udp.c
@@ -0,0 +1,124 @@
+#include "zdtmtst.h"
+
+const char *test_doc = "Static test for IP6/UDP socket\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>\n";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <arpa/inet.h> /* for sockaddr_in and inet_ntoa() */
+#include <wait.h>
+
+static int port = 8880;
+static char buf[64];
+
+#define MSG1 "msg1"
+#define MSG2 "msg_2"
+
+int main(int argc, char **argv)
+{
+ int ret, sk1, sk2;
+ socklen_t len = sizeof(struct sockaddr_in6);
+ struct sockaddr_in6 addr1, addr2, addr;
+
+ test_init(argc, argv);
+
+ sk1 = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (sk1 < 0) {
+ pr_perror("Can't create socket");
+ return 1;
+ }
+
+ memset(&addr1, 0, sizeof(addr1));
+ addr1.sin6_family = AF_INET6;
+ addr1.sin6_port = htons(port);
+ inet_pton(AF_INET6, "::1", &addr1.sin6_addr);
+
+ ret = bind(sk1, (struct sockaddr *)&addr1, len);
+ if (ret < 0) {
+ pr_perror("Can't bind socket");
+ return 1;
+ }
+
+ sk2 = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (sk2 < 0) {
+ pr_perror("Can't create socket");
+ return 1;
+ }
+
+ memset(&addr2, 0, sizeof(addr2));
+ addr2.sin6_family = AF_INET6;
+ addr2.sin6_port = htons(port+1);
+ inet_pton(AF_INET6, "::1", &addr2.sin6_addr);
+
+ ret = bind(sk2, (struct sockaddr *)&addr2, len);
+ if (ret < 0) {
+ pr_perror("Can't bind socket");
+ return 1;
+ }
+
+ ret = connect(sk2, (struct sockaddr *)&addr1, len);
+ if (ret < 0) {
+ pr_perror("Can't connect");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = sendto(sk1, MSG1, sizeof(MSG1), 0,
+ (struct sockaddr *)&addr2, len);
+ if (ret < 0) {
+ fail("Can't send");
+ return 1;
+ }
+
+ ret = send(sk2, MSG2, sizeof(MSG2), 0);
+ if (ret < 0) {
+ fail("Can't send C");
+ return 1;
+ }
+
+ ret = recvfrom(sk1, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *)&addr, &len);
+ if (ret <= 0) {
+ fail("Can't recv C");
+ return 1;
+ }
+
+ if (len != sizeof(struct sockaddr_in6) || memcmp(&addr2, &addr, len)) {
+ fail("Wrong peer C");
+ return 1;
+ }
+
+ if (ret != sizeof(MSG2) || memcmp(buf, MSG2, ret)) {
+ fail("Wrong message C");
+ return 1;
+ }
+
+ ret = recvfrom(sk2, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *)&addr, &len);
+ if (ret <= 0) {
+ fail("Can't recv");
+ return 1;
+ }
+
+ if (len != sizeof(struct sockaddr_in6) || memcmp(&addr1, &addr, len)) {
+ fail("Wrong peer");
+ return 1;
+ }
+
+ if (ret != sizeof(MSG1) || memcmp(buf, MSG1, ret)) {
+ fail("Wrong message");
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket_aio.c b/test/zdtm/static/socket_aio.c
new file mode 100644
index 000000000..6ca5079b0
--- /dev/null
+++ b/test/zdtm/static/socket_aio.c
@@ -0,0 +1,138 @@
+#include "zdtmtst.h"
+
+const char *test_doc = "static test for AIO\n";
+const char *test_author = "Andrew Vagin <avagin@parallels.com>";
+
+/* Description:
+ * Create two tcp socket, server send asynchronous request on
+ * read data and clietn write data after migration
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <aio.h>
+#include <wait.h>
+#include <netinet/tcp.h>
+
+static int port = 8880;
+
+#define BUF_SIZE 1024
+
+int main(int argc, char **argv)
+{
+ char buf[BUF_SIZE];
+ int fd, fd_s;
+ struct aiocb aiocb;
+ int status;
+ pid_t pid;
+ int ret, res;
+ const struct aiocb *aioary[1];
+
+ test_init(argc, argv);
+
+ if ((fd_s = tcp_init_server(AF_INET, &port)) < 0) {
+ pr_err("initializing server failed\n");
+ return 1;
+ }
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("fork failed");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /*
+ * Chiled is client of TCP connection
+ */
+ close(fd_s);
+ fd = tcp_init_client(AF_INET, "127.0.0.1", port);
+ if (fd < 0)
+ return 1;
+
+ memset(&aiocb, 0, sizeof(struct aiocb));
+ aiocb.aio_fildes = fd;
+ aiocb.aio_buf = buf;
+ aiocb.aio_nbytes = BUF_SIZE;
+ ret = aio_read(&aiocb);
+ if (ret < 0) {
+ pr_perror("aio_read failed");
+ return 1;
+ }
+
+ /* Wait for request completion */
+ aioary[0] = &aiocb;
+ ret = aio_error(&aiocb);
+#ifdef DEBUG
+ test_msg(".");
+#endif
+ res = 0;
+again:
+ if (aio_suspend(aioary, 1, NULL) < 0 && errno != EINTR) {
+ pr_perror("aio_suspend failed");
+ res = 1;
+ }
+
+ ret = aio_error(&aiocb);
+ if (!res && ret == EINPROGRESS) {
+#ifdef DEBUG
+ test_msg("restart aio_suspend\n");
+#endif
+ goto again;
+ }
+ if (ret != 0) {
+ pr_err("Error at aio_error(): %s\n", strerror(ret));
+ res = 1;
+ }
+
+ if (aio_return(&aiocb) != BUF_SIZE) {
+ pr_perror("Error at aio_return()");
+ res = 1;
+ }
+
+ close(fd);
+ return res;
+ }
+
+ /*
+ * parent is server of TCP connection
+ */
+ fd = tcp_accept_server(fd_s);
+ close(fd_s);
+ if (fd < 0) {
+ pr_err("can't accept client connection\n");
+ goto error;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (write(fd, buf, BUF_SIZE) < BUF_SIZE) {
+ pr_perror("can't write");
+ goto error;
+ }
+ close(fd);
+
+
+ if (wait(&status) < 0) {
+ pr_perror("wait failed");
+ goto error;
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+ pr_err("child failed with exit code %d\n", WEXITSTATUS(status));
+ return 1;
+ }
+
+ pass();
+ return 0;
+error:
+ kill(pid, SIGKILL);
+ wait(&status);
+ return -1;
+}
diff --git a/test/zdtm/static/socket_aio.desc b/test/zdtm/static/socket_aio.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/socket_aio.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/socket_close_data.c b/test/zdtm/static/socket_close_data.c
new file mode 100644
index 000000000..de552ada9
--- /dev/null
+++ b/test/zdtm/static/socket_close_data.c
@@ -0,0 +1,43 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check one end of socketpair with data";
+const char *test_author = "Andrew Vagin <avagin@openvz.org";
+
+#define MSG "hello"
+int main(int argc, char **argv)
+{
+ int sks[2], ret;
+ char buf[1024];
+
+ test_init(argc, argv);
+
+ if (socketpair(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, sks) < 0) {
+ pr_perror("socketpair");
+ return 1;
+ }
+
+ if (write(sks[1], MSG, sizeof(MSG)) != sizeof(MSG)) {
+ pr_perror("write");
+ return 1;
+ }
+ close(sks[1]);
+
+ test_daemon();
+ test_waitsig();
+
+ ret = read(sks[0], buf, sizeof(MSG));
+ buf[ret > 0 ? ret : 0] = 0;
+ if (ret != sizeof(MSG)) {
+ fail("%d: %s", ret, buf);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket_close_data01.c b/test/zdtm/static/socket_close_data01.c
new file mode 100644
index 000000000..53b5f7a6d
--- /dev/null
+++ b/test/zdtm/static/socket_close_data01.c
@@ -0,0 +1,115 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check data of bound socket and possibility to connect";
+const char *test_author = "Kirill Tkhai <ktkhai@virtuozzo";
+
+#define MSG "hello"
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+static int client(const char *iter)
+{
+ struct sockaddr_un addr;
+ int sk;
+
+ sk = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (sk < 0) {
+ pr_perror("open client %s", iter);
+ return 1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, filename);
+
+ if (connect(sk, (void *)&addr, sizeof(struct sockaddr_un)) < 0) {
+ pr_perror("connect failed %s\n", iter);
+ return 1;
+ }
+
+ if (send(sk, MSG, sizeof(MSG), 0) != sizeof(MSG)) {
+ pr_perror("send failed %s\n", iter);
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct sockaddr_un addr;
+ int srv, status, ret;
+ char buf[1024];
+
+ test_init(argc, argv);
+
+ srv = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ if (srv < 0) {
+ pr_perror("open srv");
+ exit(1);
+ }
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, filename);
+
+ if (bind(srv, (struct sockaddr *) &addr, sizeof(struct sockaddr_un))) {
+ pr_perror("bind srv");
+ exit(1);
+ }
+
+ if (fork() == 0) {
+ close(srv);
+ client("(iter1)");
+ exit(0);
+ }
+ ret = 1;
+ if (wait(&status) == -1) {
+ fail("wait failed");
+ goto unlink;
+ }
+ if (status) {
+ pr_err("A child exited with 0x%x\n", status);
+ goto unlink;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ /* Test1: check we can read client message: */
+ ret = read(srv, buf, sizeof(MSG));
+ buf[ret > 0 ? ret : 0] = 0;
+ if (ret != sizeof(MSG)) {
+ fail("%d: %s", ret, buf);
+ ret = 1;
+ goto unlink;
+ }
+
+ /* Test2: check it's still possible to connect to the bound socket */
+ if (fork() == 0) {
+ exit(client("(iter2)"));
+ }
+
+ if (wait(&status) < 0) {
+ fail("wait failed");
+ goto unlink;
+ }
+
+ if (WEXITSTATUS(status) != 0) {
+ fail("connect failed");
+ goto unlink;
+ }
+
+ ret = 0;
+ pass();
+unlink:
+ unlink(filename);
+ return ret;
+}
diff --git a/test/zdtm/static/socket_dgram_data.c b/test/zdtm/static/socket_dgram_data.c
new file mode 100644
index 000000000..ec79a381c
--- /dev/null
+++ b/test/zdtm/static/socket_dgram_data.c
@@ -0,0 +1,82 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that data in dgram socket are restored correctly";
+const char *test_author = "Andrew Vagin <avagin@openvz.org";
+
+#define SK_SRV "\0socket_snd_srv"
+
+#define MSG "hello"
+int main(int argc, char **argv)
+{
+ struct sockaddr_un addr;
+ unsigned int addrlen;
+ int srv, clnt1, clnt2, ret;
+ char buf[1024];
+
+ test_init(argc, argv);
+
+ srv = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ if (srv < 0) {
+ pr_perror("socket");
+ return 1;
+ }
+ clnt1 = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ if (clnt1 < 0) {
+ pr_perror("socket");
+ return 1;
+ }
+ clnt2 = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ if (clnt2 < 0) {
+ pr_perror("socket");
+ return 1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, SK_SRV, sizeof(SK_SRV));
+ addrlen = sizeof(addr.sun_family) + sizeof(SK_SRV);
+
+ if (bind(srv, &addr, addrlen)) {
+ fail("bind\n");
+ exit(1);
+ }
+ if (connect(clnt1, &addr, addrlen)) {
+ fail("connect\n");
+ exit(1);
+ }
+ if (connect(clnt2, &addr, addrlen)) {
+ fail("connect\n");
+ exit(1);
+ }
+
+ if (write(clnt1, MSG, sizeof(MSG)) != sizeof(MSG)) {
+ pr_perror("write");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = read(srv, buf, sizeof(buf));
+ buf[ret > 0 ? ret : 0] = 0;
+ if (ret != sizeof(MSG)) {
+ fail("%d: %s", ret, buf);
+ return 1;
+ }
+
+ ret = read(srv, buf, sizeof(buf));
+ if (ret != -1 || errno != EAGAIN) {
+ fail("unexpected data: %d", ret);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket_listen.c b/test/zdtm/static/socket_listen.c
new file mode 100644
index 000000000..f11d37959
--- /dev/null
+++ b/test/zdtm/static/socket_listen.c
@@ -0,0 +1,118 @@
+#include "zdtmtst.h"
+
+#ifdef ZDTM_IPV6
+#define ZDTM_FAMILY AF_INET6
+#else
+#define ZDTM_FAMILY AF_INET
+#endif
+
+const char *test_doc = "static test for listening socket\n";
+const char *test_author = "Stanislav Kinsbursky <skinsbursky@openvz.org>";
+
+/* Description:
+ * Create two tcp socket, server send asynchronous request on
+ * read data and clietn write data after migration
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <wait.h>
+#include <netinet/tcp.h>
+
+static int port = 8880;
+
+#define BUF_SIZE 1024
+
+static void sig_hand(int signo) {}
+
+int main(int argc, char **argv)
+{
+ unsigned char buf[BUF_SIZE];
+ int fd, fd_s;
+ int status;
+ pid_t pid;
+ int res;
+ uint32_t crc;
+ struct sigaction sa = {
+ .sa_handler = sig_hand,
+ /* don't set SA_RESTART */
+ };
+
+ test_init(argc, argv);
+
+ if ((fd_s = tcp_init_server(ZDTM_FAMILY, &port)) < 0) {
+ pr_err("initializing server failed\n");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(SIGCHLD, &sa, NULL))
+ pr_perror("Can't set SIGCHLD handler");
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("fork failed");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /*
+ * Chiled is client of TCP connection
+ */
+ close(fd_s);
+ fd = tcp_init_client(ZDTM_FAMILY, "localhost", port);
+ if (fd < 0)
+ return 1;
+
+ res = read(fd, buf, BUF_SIZE);
+ close(fd);
+ if (res != BUF_SIZE) {
+ pr_perror("read less then have to: %d instead of %d", res, BUF_SIZE);
+ return -1;
+ }
+ if (datachk(buf, BUF_SIZE, &crc))
+ return -2;
+ return 0;
+ }
+
+ /*
+ * parent is server of TCP connection
+ */
+ fd = tcp_accept_server(fd_s);
+ close(fd_s);
+ if (fd < 0) {
+ pr_err("can't accept client connection\n");
+ goto error;
+ }
+
+ datagen(buf, BUF_SIZE, &crc);
+ if (write(fd, buf, BUF_SIZE) < BUF_SIZE) {
+ pr_perror("can't write");
+ goto error;
+ }
+ close(fd);
+
+
+ if (wait(&status) < 0) {
+ pr_perror("wait failed");
+ goto error;
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+ pr_err("child failed with exit code %d\n", WEXITSTATUS(status));
+ return 1;
+ }
+
+ pass();
+ return 0;
+error:
+ kill(pid, SIGKILL);
+ wait(&status);
+ return -1;
+}
diff --git a/test/zdtm/static/socket_listen6.c b/test/zdtm/static/socket_listen6.c
new file mode 120000
index 000000000..d026b3bf8
--- /dev/null
+++ b/test/zdtm/static/socket_listen6.c
@@ -0,0 +1 @@
+socket_listen.c \ No newline at end of file
diff --git a/test/zdtm/static/socket_queues.c b/test/zdtm/static/socket_queues.c
new file mode 100644
index 000000000..486d12a58
--- /dev/null
+++ b/test/zdtm/static/socket_queues.c
@@ -0,0 +1,110 @@
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+/* FIXME Need gram sockets tests */
+
+const char *test_doc = "Test unix sockets queues (2 messages in queue)\n";
+const char *test_author = "Stanislav Kinsbursky <skinsbursky@parallels.com>\n";
+
+#define SK_DATA_S1 "packet stream left"
+#define SK_DATA_S2 "packet stream right"
+#define SK_DATA_D1 "packet dgram left"
+#define SK_DATA_D2 "packet dgram right"
+
+int main(int argc, char *argv[])
+{
+ int ssk_pair_d[2];
+ int ssk_pair_s[2];
+ char buf_left[64], buf_right[64];
+
+ test_init(argc, argv);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, ssk_pair_s) == -1) {
+ fail("socketpair\n");
+ exit(1);
+ }
+
+ write(ssk_pair_s[0], SK_DATA_S1, sizeof(SK_DATA_S1));
+ write(ssk_pair_s[0], SK_DATA_S2, sizeof(SK_DATA_S2));
+ write(ssk_pair_s[1], SK_DATA_S2, sizeof(SK_DATA_S2));
+ write(ssk_pair_s[1], SK_DATA_S1, sizeof(SK_DATA_S1));
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, ssk_pair_d) == -1) {
+ fail("socketpair\n");
+ exit(1);
+ }
+
+ write(ssk_pair_d[0], SK_DATA_D1, sizeof(SK_DATA_D1));
+ write(ssk_pair_d[0], SK_DATA_D2, sizeof(SK_DATA_D2));
+ write(ssk_pair_d[1], SK_DATA_D2, sizeof(SK_DATA_D2));
+ write(ssk_pair_d[1], SK_DATA_D1, sizeof(SK_DATA_D1));
+
+ test_daemon();
+ test_waitsig();
+
+ read(ssk_pair_s[1], buf_left, strlen(SK_DATA_S1) + 1);
+ if (strcmp(buf_left, SK_DATA_S1)) {
+ fail("SK_DATA_S2: '%s\n", SK_DATA_S1);
+ exit(1);
+ }
+ read(ssk_pair_s[1], buf_right, strlen(SK_DATA_S2) + 1);
+ if (strcmp(buf_right, SK_DATA_S2)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("stream1 : '%s' '%s'\n", buf_left, buf_right);
+
+ read(ssk_pair_s[0], buf_left, strlen(SK_DATA_S2) + 1);
+ if (strcmp(buf_left, SK_DATA_S2)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ read(ssk_pair_s[0], buf_right, strlen(SK_DATA_S1) + 1);
+ if (strcmp(buf_right, SK_DATA_S1)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("stream2 : '%s' '%s'\n", buf_left, buf_right);
+
+ read(ssk_pair_d[1], buf_left, strlen(SK_DATA_D1) + 1);
+ if (strcmp(buf_left, SK_DATA_D1)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ read(ssk_pair_d[1], buf_right, strlen(SK_DATA_D2) + 1);
+ if (strcmp(buf_right, SK_DATA_D2)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("dgram1 : '%s' '%s'\n", buf_left, buf_right);
+
+ read(ssk_pair_d[0], buf_left, strlen(SK_DATA_D2) + 1);
+ if (strcmp(buf_left, SK_DATA_D2)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ read(ssk_pair_d[0], buf_right,strlen(SK_DATA_D1) + 1);
+ if (strcmp(buf_right, SK_DATA_D1)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("dgram2 : '%s' '%s'\n", buf_left, buf_right);
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket_snd_addr.c b/test/zdtm/static/socket_snd_addr.c
new file mode 100644
index 000000000..1d72d4e4a
--- /dev/null
+++ b/test/zdtm/static/socket_snd_addr.c
@@ -0,0 +1,101 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that sender addresses are restored";
+const char *test_author = "Andrew Vagin <avagin@openvz.org";
+
+#define SK_SRV "\0socket_snd_srv"
+#define SK_NAME "\0A-socket_snd_clnt"
+
+char sk_names[2][128] = {
+ SK_NAME,
+ SK_NAME,
+ };
+
+#define MSG "hello"
+int main(int argc, char **argv)
+{
+ struct sockaddr_un addr;
+ unsigned int addrlen;
+ int srv, clnt = -1, ret, i;
+ char buf[1024];
+ struct iovec iov = {
+ .iov_base = &buf,
+ .iov_len = sizeof(buf),
+ };
+ struct msghdr hdr = {
+ .msg_name = &addr,
+ .msg_namelen = sizeof(addr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+
+ test_init(argc, argv);
+
+ srv = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, SK_SRV, sizeof(SK_SRV));
+ addrlen = sizeof(addr.sun_family) + sizeof(SK_SRV);
+
+ if (bind(srv, &addr, addrlen)) {
+ fail("bind\n");
+ exit(1);
+ }
+
+ for (i = 0; i < 2; i++) {
+ close(clnt);
+ clnt = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+
+ sk_names[i][1] += i;
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, sk_names[i], sizeof(SK_NAME));
+ addrlen = sizeof(addr.sun_family) + sizeof(SK_NAME);
+
+ if (bind(clnt, &addr, addrlen)) {
+ fail("bind\n");
+ exit(1);
+ }
+
+ memcpy(addr.sun_path, SK_SRV, sizeof(SK_SRV));
+ addrlen = sizeof(addr.sun_family) + sizeof(SK_SRV);
+ if (connect(clnt, &addr, addrlen)) {
+ fail("connect\n");
+ exit(1);
+ }
+
+ if (send(clnt, MSG, sizeof(MSG), 0) != sizeof(MSG)) {
+ pr_perror("write");
+ return 1;
+ }
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ for (i = 0; i < 2; i++) {
+ memset(addr.sun_path, 0, sizeof(addr.sun_path));
+ ret = recvmsg(srv, &hdr, MSG_DONTWAIT);
+ buf[ret > 0 ? ret : 0] = 0;
+ if (ret != sizeof(MSG)) {
+ fail("%d: %s", ret, buf);
+ return 1;
+ }
+ if (hdr.msg_namelen > sizeof(addr.sun_family) + 1)
+ pr_perror("%d, %s", hdr.msg_namelen, addr.sun_path + 1);
+ if (memcmp(addr.sun_path, sk_names[i], sizeof(SK_NAME))) {
+ fail("A sender address is mismatch");
+ return 1;
+ }
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket_snd_addr.desc b/test/zdtm/static/socket_snd_addr.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/socket_snd_addr.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/socket_udp.c b/test/zdtm/static/socket_udp.c
new file mode 100644
index 000000000..e84e8bf29
--- /dev/null
+++ b/test/zdtm/static/socket_udp.c
@@ -0,0 +1,129 @@
+#include "zdtmtst.h"
+
+const char *test_doc = "static test for UDP socket\n";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com<>\n";
+
+/* Description:
+ * Create two tcp socket, server send asynchronous request on
+ * read data and clietn write data after migration
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <arpa/inet.h> /* for sockaddr_in and inet_ntoa() */
+#include <wait.h>
+
+static int port = 8880;
+static char buf[8];
+
+#define MSG1 "msg1"
+#define MSG2 "msg_2"
+
+int main(int argc, char **argv)
+{
+ int ret, sk1, sk2;
+ socklen_t len = sizeof(struct sockaddr_in);
+ struct sockaddr_in addr1, addr2, addr;
+
+ test_init(argc, argv);
+
+ sk1 = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sk1 < 0) {
+ pr_perror("Can't create socket");
+ return 1;
+ }
+
+ memset(&addr1, 0, sizeof(addr1));
+ addr1.sin_family = AF_INET;
+ addr1.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr1.sin_port = htons(port);
+
+ ret = bind(sk1, (struct sockaddr *)&addr1, len);
+ if (ret < 0) {
+ pr_perror("Can't bind socket");
+ return 1;
+ }
+
+ sk2 = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sk2 < 0) {
+ pr_perror("Can't create socket");
+ return 1;
+ }
+
+ memset(&addr2, 0, sizeof(addr1));
+ addr2.sin_family = AF_INET;
+ addr2.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr2.sin_port = htons(port + 1);
+
+ ret = bind(sk2, (struct sockaddr *)&addr2, len);
+ if (ret < 0) {
+ pr_perror("Can't bind socket");
+ return 1;
+ }
+
+ ret = connect(sk2, (struct sockaddr *)&addr1, len);
+ if (ret < 0) {
+ pr_perror("Can't connect");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = sendto(sk1, MSG1, sizeof(MSG1), 0,
+ (struct sockaddr *)&addr2, len);
+ if (ret < 0) {
+ fail("Can't send");
+ return 1;
+ }
+
+ ret = send(sk2, MSG2, sizeof(MSG2), 0);
+ if (ret < 0) {
+ fail("Can't send C");
+ return 1;
+ }
+
+ ret = recvfrom(sk1, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *)&addr, &len);
+ if (ret <= 0) {
+ fail("Can't recv C");
+ return 1;
+ }
+
+ if (len != sizeof(struct sockaddr_in) || memcmp(&addr2, &addr, len)) {
+ fail("Wrong peer C");
+ return 1;
+ }
+
+ if (ret != sizeof(MSG2) || memcmp(buf, MSG2, ret)) {
+ fail("Wrong message C");
+ return 1;
+ }
+
+ ret = recvfrom(sk2, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *)&addr, &len);
+ if (ret <= 0) {
+ fail("Can't recv");
+ return 1;
+ }
+
+ if (len != sizeof(struct sockaddr_in) || memcmp(&addr1, &addr, len)) {
+ fail("Wrong peer");
+ return 1;
+ }
+
+ if (ret != sizeof(MSG1) || memcmp(buf, MSG1, ret)) {
+ fail("Wrong message");
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket_udplite.c b/test/zdtm/static/socket_udplite.c
new file mode 100644
index 000000000..aaa40e1dc
--- /dev/null
+++ b/test/zdtm/static/socket_udplite.c
@@ -0,0 +1,129 @@
+#include "zdtmtst.h"
+
+const char *test_doc = "static test for UDP socket\n";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com<>\n";
+
+/* Description:
+ * Create two tcp socket, server send asynchronous request on
+ * read data and clietn write data after migration
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <arpa/inet.h> /* for sockaddr_in and inet_ntoa() */
+#include <wait.h>
+
+static int port = 8890;
+static char buf[8];
+
+#define MSG1 "msg1"
+#define MSG2 "msg_2"
+
+int main(int argc, char **argv)
+{
+ int ret, sk1, sk2;
+ socklen_t len = sizeof(struct sockaddr_in);
+ struct sockaddr_in addr1, addr2, addr;
+
+ test_init(argc, argv);
+
+ sk1 = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE);
+ if (sk1 < 0) {
+ pr_perror("Can't create socket");
+ return 1;
+ }
+
+ memset(&addr1, 0, sizeof(addr1));
+ addr1.sin_family = AF_INET;
+ addr1.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr1.sin_port = htons(port);
+
+ ret = bind(sk1, (struct sockaddr *)&addr1, len);
+ if (ret < 0) {
+ pr_perror("Can't bind socket");
+ return 1;
+ }
+
+ sk2 = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE);
+ if (sk2 < 0) {
+ pr_perror("Can't create socket");
+ return 1;
+ }
+
+ memset(&addr2, 0, sizeof(addr1));
+ addr2.sin_family = AF_INET;
+ addr2.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr2.sin_port = htons(port + 1);
+
+ ret = bind(sk2, (struct sockaddr *)&addr2, len);
+ if (ret < 0) {
+ pr_perror("Can't bind socket");
+ return 1;
+ }
+
+ ret = connect(sk2, (struct sockaddr *)&addr1, len);
+ if (ret < 0) {
+ pr_perror("Can't connect");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = sendto(sk1, MSG1, sizeof(MSG1), 0,
+ (struct sockaddr *)&addr2, len);
+ if (ret < 0) {
+ fail("Can't send");
+ return 1;
+ }
+
+ ret = send(sk2, MSG2, sizeof(MSG2), 0);
+ if (ret < 0) {
+ fail("Can't send C");
+ return 1;
+ }
+
+ ret = recvfrom(sk1, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *)&addr, &len);
+ if (ret <= 0) {
+ fail("Can't recv C");
+ return 1;
+ }
+
+ if (len != sizeof(struct sockaddr_in) || memcmp(&addr2, &addr, len)) {
+ fail("Wrong peer C");
+ return 1;
+ }
+
+ if (ret != sizeof(MSG2) || memcmp(buf, MSG2, ret)) {
+ fail("Wrong message C");
+ return 1;
+ }
+
+ ret = recvfrom(sk2, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *)&addr, &len);
+ if (ret <= 0) {
+ fail("Can't recv");
+ return 1;
+ }
+
+ if (len != sizeof(struct sockaddr_in) || memcmp(&addr1, &addr, len)) {
+ fail("Wrong peer");
+ return 1;
+ }
+
+ if (ret != sizeof(MSG1) || memcmp(buf, MSG1, ret)) {
+ fail("Wrong message");
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/sockets00.c b/test/zdtm/static/sockets00.c
new file mode 100644
index 000000000..9d0095740
--- /dev/null
+++ b/test/zdtm/static/sockets00.c
@@ -0,0 +1,163 @@
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test unix stream sockets\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org";
+
+#define SK_DATA "packet"
+
+char *filename;
+TEST_OPTION(filename, string, "socket file name", 1);
+
+#define TEST_MODE 0640
+
+int main(int argc, char *argv[])
+{
+ int ssk_icon[4];
+ struct sockaddr_un addr;
+ unsigned int addrlen;
+
+ struct stat st_b, st_a;
+ char path[PATH_MAX];
+ char buf[64];
+ char *cwd;
+ uid_t uid = 18943;
+ gid_t gid = 58467;
+
+ int ret;
+
+ test_init(argc, argv);
+
+ cwd = get_current_dir_name();
+ if (!cwd) {
+ fail("getcwd\n");
+ exit(1);
+ }
+
+ snprintf(path, sizeof(path), "%s/%s", cwd, filename);
+ unlink(path);
+
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+ addrlen = sizeof(addr.sun_family) + strlen(path);
+
+ ssk_icon[0] = socket(AF_UNIX, SOCK_STREAM, 0);
+ ssk_icon[1] = socket(AF_UNIX, SOCK_STREAM, 0);
+ ssk_icon[2] = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ssk_icon[0] < 0 || ssk_icon[1] < 0 || ssk_icon[2] < 0) {
+ fail("socket\n");
+ exit(1);
+ }
+
+ ret = bind(ssk_icon[0], &addr, addrlen);
+ if (ret) {
+ fail("bind\n");
+ exit(1);
+ }
+
+ ret = chmod(path, TEST_MODE);
+ if (ret) {
+ pr_perror("chmod");
+ exit(1);
+ }
+
+ ret = chown(path, uid, gid);
+ if (ret) {
+ pr_perror("chown");
+ exit(1);
+ }
+
+ ret = listen(ssk_icon[0], 16);
+ if (ret) {
+ fail("bind\n");
+ exit(1);
+ }
+
+ ret = connect(ssk_icon[2], &addr, addrlen);
+ if (ret) {
+ fail("connect\n");
+ exit(1);
+ }
+
+ ssk_icon[3] = accept(ssk_icon[0], NULL, NULL);
+ if (ssk_icon[3] < 0) {
+ fail("accept");
+ exit(1);
+ }
+
+ ret = connect(ssk_icon[1], &addr, addrlen);
+ if (ret) {
+ fail("connect\n");
+ exit(1);
+ }
+
+ ret = stat(path, &st_b);
+ if (ret) {
+ fail("stat");
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ ret = stat(path, &st_a);
+ if (ret) {
+ fail("stat");
+ exit(1);
+ }
+
+ if (st_b.st_mode != st_a.st_mode) {
+ fail("The file permissions for %s were changed %o %o\n",
+ path, st_b.st_mode, st_a.st_mode);
+ exit(1);
+ }
+
+ if (st_b.st_uid != uid || st_b.st_gid != gid) {
+ fail("Owner user or group for %s corrupted, uid=%d, gid=%d",
+ path, st_b.st_uid, st_b.st_gid);
+ exit(1);
+ }
+
+ ret = accept(ssk_icon[0], NULL, NULL);
+ if (ret < 0) {
+ fail("accept\n");
+ exit(1);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ write(ssk_icon[1], SK_DATA, sizeof(SK_DATA));
+ read(ret, &buf, sizeof(buf));
+ if (strcmp(buf, SK_DATA)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("stream1 : '%s'\n", buf);
+
+ memset(buf, 0, sizeof(buf));
+ write(ssk_icon[2], SK_DATA, sizeof(SK_DATA));
+ read(ssk_icon[3], &buf, sizeof(buf));
+ if (strcmp(buf, SK_DATA)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("stream2 : '%s'\n", buf);
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/sockets00.desc b/test/zdtm/static/sockets00.desc
new file mode 100644
index 000000000..2eac7e654
--- /dev/null
+++ b/test/zdtm/static/sockets00.desc
@@ -0,0 +1 @@
+{'flags': 'suid'}
diff --git a/test/zdtm/static/sockets01.c b/test/zdtm/static/sockets01.c
new file mode 100644
index 000000000..a111e9d40
--- /dev/null
+++ b/test/zdtm/static/sockets01.c
@@ -0,0 +1,150 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test unix sockets shutdown";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>";
+
+#define fin(msg) do { pr_perror(msg); exit(1); } while (0)
+#define ffin(msg) do { fail(msg); exit(1); } while (0)
+
+#define TEST_MSG "test-message"
+static char buf[sizeof(TEST_MSG)];
+
+int main(int argc, char *argv[])
+{
+ int spu[2], spb[2], dpu[2], dpb[2], dpd[2];
+ int ret;
+
+ test_init(argc, argv);
+
+ signal(SIGPIPE, SIG_IGN);
+
+ /* spu -- stream pair, unidirectional shutdown */
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, spu) < 0)
+ fin("no stream pair 1");
+
+ if (shutdown(spu[0], SHUT_RD) < 0)
+ fin("no stream shutdown 1");
+
+ /* spb -- stream pair, bidirectional shutdown */
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, spb) < 0)
+ fin("no stream pair 2");
+
+ if (shutdown(spb[0], SHUT_RDWR) < 0)
+ fin("no stream shutdown 2");
+
+ /* dpu -- dgram pair, one end read shutdown */
+ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, dpu) < 0)
+ fin("no dgram pair 1");
+
+ if (shutdown(dpu[0], SHUT_RD) < 0)
+ fin("no dgram shutdown 1");
+
+ /* dpb -- dgram pair, one end read-write shutdown */
+ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, dpb) < 0)
+ fin("no dgram pair 2");
+
+ if (shutdown(dpb[0], SHUT_RDWR) < 0)
+ fin("no dgram shutdown 2");
+
+ /* dpd -- dgram pair, one end write shutdown with data */
+ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, dpd) < 0)
+ fin("no dgram pair 3");
+
+ if (write(dpd[0], TEST_MSG, sizeof(TEST_MSG)) < 0)
+ fin("no dgram write");
+
+ if (shutdown(dpd[0], SHUT_WR) < 0)
+ fin("no dgram shutdown 3");
+
+ test_daemon();
+ test_waitsig();
+
+ /*
+ * spu -- check that one direction is blocked and
+ * the other one is not
+ */
+
+ ret = write(spu[0], TEST_MSG, sizeof(TEST_MSG));
+ if (ret < 0)
+ ffin("SU shutdown broken 1");
+
+ ret = read(spu[1], buf, sizeof(buf));
+ if (ret < 0)
+ ffin("SU shutdown broken 2");
+
+ ret = write(spu[1], TEST_MSG, sizeof(TEST_MSG));
+ if (ret >= 0)
+ ffin("SU shutdown broken 3");
+
+ /*
+ * spb -- check that both ends are off
+ */
+
+ ret = write(spb[0], TEST_MSG, sizeof(TEST_MSG));
+ if (ret >= 0)
+ ffin("SB shutdown broken 1");
+
+ ret = write(spb[1], TEST_MSG, sizeof(TEST_MSG));
+ if (ret >= 0)
+ ffin("SB shutdown broken 2");
+
+ /*
+ * dpu -- check that one direction works, and
+ * the other does not
+ */
+
+ ret = write(dpu[0], TEST_MSG, sizeof(TEST_MSG));
+ if (ret < 0)
+ ffin("DU shutdown broken 1");
+
+ ret = read(dpu[1], buf, sizeof(buf));
+ if (ret < 0)
+ ffin("DU shutdown broken 2");
+
+ ret = write(dpu[1], TEST_MSG, sizeof(TEST_MSG));
+ if (ret >= 0)
+ ffin("DU shutdown broken 3");
+
+ /*
+ * dpb -- check that both ends are read
+ */
+
+ ret = write(dpb[0], TEST_MSG, sizeof(TEST_MSG));
+ if (ret >= 0)
+ ffin("DB shutdown broken 1");
+
+ ret = write(dpb[1], TEST_MSG, sizeof(TEST_MSG));
+ if (ret >= 0)
+ ffin("DB shutdown broken 2");
+
+ /*
+ * dpd -- check that data is in there, but can't
+ * feed more
+ */
+
+ ret = read(dpd[1], buf, sizeof(buf));
+ if (ret < 0)
+ ffin("DD shutdown nodata");
+
+ ret = write(dpd[0], TEST_MSG, sizeof(buf));
+ if (ret >= 0)
+ ffin("DB shutdown broken");
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/sockets02.c b/test/zdtm/static/sockets02.c
new file mode 100644
index 000000000..a7bec03f7
--- /dev/null
+++ b/test/zdtm/static/sockets02.c
@@ -0,0 +1,67 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test semi-closed unix stream connection\n";
+const char *test_author = "Pavel Emelyanov <xemul@parallels.com>\n";
+
+int main(int argc, char *argv[])
+{
+ int ssk_pair[2], ret;
+ char aux, data;
+
+ test_init(argc, argv);
+
+ data = (char)lrand48();
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, ssk_pair) == -1) {
+ fail("socketpair\n");
+ exit(1);
+ }
+
+ if (write(ssk_pair[1], &data, sizeof(data)) != sizeof(data)) {
+ fail("write\n");
+ exit(1);
+ }
+
+ close(ssk_pair[1]);
+
+ test_daemon();
+ test_waitsig();
+
+ ret = read(ssk_pair[0], &aux, sizeof(aux));
+ if (ret != sizeof(data) && aux != data) {
+ fail("Data loss (write %d, read %d)", data, aux);
+ return 1;
+ }
+
+ errno = 0;
+ ret = read(ssk_pair[0], &aux, sizeof(aux));
+ if (ret != 0 || errno != 0) {
+ fail("Opened end in wrong state (%d/%d)", ret, errno);
+ return 0;
+ }
+
+ errno = 0;
+ ret = read(ssk_pair[1], &aux, sizeof(aux));
+ if (ret != -1 || errno != EBADF) {
+ fail("Closed end in wrong state (%d/%d)", ret, errno);
+ return 0;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/sockets_dgram.c b/test/zdtm/static/sockets_dgram.c
new file mode 100644
index 000000000..a34bb144c
--- /dev/null
+++ b/test/zdtm/static/sockets_dgram.c
@@ -0,0 +1,212 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test unix dgram sockets\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org";
+
+#define SK_DATA_BOUND "data-packet-bound"
+#define SK_DATA_CONN "data-packet-conn"
+#define SK_DATA_BOUND_CONN "data-packet-bound-conn"
+
+char *filename;
+TEST_OPTION(filename, string, "socket file name", 1);
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_un name_bound;
+ struct sockaddr_un name_conn;
+ struct sockaddr_un name_bound_conn;
+ int sk_dgram_bound_client;
+ int sk_dgram_bound_server;
+ int sk_dgram_conn_client;
+ int sk_dgram_conn_client2;
+ int sk_dgram_conn_server;
+ int sk_dgram_bound_conn;
+
+ char path[PATH_MAX];
+ char buf[64];
+ /*
+ * The original code makes dir to be current working
+ * directory. But it may be too long in google environment
+ * for path to be fit into struct sockaddr_un.
+ * One alternate way to resolve it is to use relative path
+ * for sockaddr_un, but criu has not supported relative
+ * bind path yet.
+ * We change it to "/tmp" to ensure its short length.
+ */
+ char *dirname = "/tmp";
+
+ int ret;
+
+ test_init(argc, argv);
+
+ snprintf(path, sizeof(path), "%s/%s", dirname, filename);
+ unlink(path);
+
+ sk_dgram_bound_client = socket(AF_UNIX, SOCK_DGRAM, 0);
+ sk_dgram_bound_server = socket(AF_UNIX, SOCK_DGRAM, 0);
+ sk_dgram_conn_client = socket(AF_UNIX, SOCK_DGRAM, 0);
+ sk_dgram_conn_client2 = socket(AF_UNIX, SOCK_DGRAM, 0);
+ sk_dgram_conn_server = socket(AF_UNIX, SOCK_DGRAM, 0);
+ sk_dgram_bound_conn = socket(AF_UNIX, SOCK_DGRAM, 0);
+
+ if (sk_dgram_conn_server < 0 ||
+ sk_dgram_bound_server < 0 ||
+ sk_dgram_conn_client < 0 ||
+ sk_dgram_conn_client2 < 0 ||
+ sk_dgram_conn_server < 0 ||
+ sk_dgram_bound_conn < 0) {
+ fail("socket");
+ exit(1);
+ }
+
+ snprintf(path, sizeof(path), "%s/%s.bound", dirname, filename);
+ unlink(path);
+ if (strlen(path) >= sizeof(name_bound.sun_path)) {
+ fail("too long path");
+ exit(1);
+ }
+
+ name_bound.sun_family = AF_UNIX;
+ strncpy(name_bound.sun_path, path, sizeof(name_bound.sun_path));
+
+ snprintf(path, sizeof(path), "%s/%s.conn", dirname, filename);
+ unlink(path);
+ if (strlen(path) >= sizeof(name_conn.sun_path)) {
+ fail("too long path");
+ exit(1);
+ }
+
+ name_conn.sun_family = AF_UNIX;
+ strncpy(name_conn.sun_path, path, sizeof(name_conn.sun_path));
+
+ snprintf(path, sizeof(path), "%s/%s.bound-conn", dirname, filename);
+ unlink(path);
+ if (strlen(path) >= sizeof(name_bound_conn.sun_path)) {
+ fail("too long path");
+ exit(1);
+ }
+
+ name_bound_conn.sun_family = AF_UNIX;
+ strncpy(name_bound_conn.sun_path, path, sizeof(name_bound_conn.sun_path));
+
+ ret = bind(sk_dgram_bound_server, &name_bound, sizeof(name_bound));
+ if (ret) {
+ fail("bind");
+ exit(1);
+ }
+
+ ret = bind(sk_dgram_conn_server, &name_conn, sizeof(name_conn));
+ if (ret) {
+ fail("bind");
+ exit(1);
+ }
+
+ ret = connect(sk_dgram_conn_client, &name_conn, sizeof(name_conn));
+ if (ret) {
+ fail("connect");
+ exit(1);
+ }
+
+ ret = connect(sk_dgram_conn_client2, &name_conn, sizeof(name_conn));
+ if (ret) {
+ fail("connect");
+ exit(1);
+ }
+
+ ret = bind(sk_dgram_bound_conn, &name_bound_conn, sizeof(name_bound_conn));
+ if (ret) {
+ fail("bind");
+ exit(1);
+ }
+
+ /* Note, it's already bound, so make it more idiotic! */
+ ret = connect(sk_dgram_bound_conn, &name_bound_conn, sizeof(name_bound_conn));
+ if (ret) {
+ fail("connect");
+ exit(1);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ sendto(sk_dgram_bound_client, SK_DATA_BOUND, sizeof(SK_DATA_BOUND), 0,
+ &name_bound, sizeof(name_bound));
+ read(sk_dgram_bound_server, &buf, sizeof(buf));
+ if (strcmp(buf, SK_DATA_BOUND)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("dgram-bound : '%s'\n", buf);
+
+ memset(buf, 0, sizeof(buf));
+ write(sk_dgram_conn_client, SK_DATA_CONN, sizeof(SK_DATA_CONN));
+ read(sk_dgram_conn_server, &buf, sizeof(buf));
+ if (strcmp(buf, SK_DATA_CONN)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("dgram-conn : '%s'\n", buf);
+
+ memset(buf, 0, sizeof(buf));
+ write(sk_dgram_bound_conn, SK_DATA_BOUND_CONN, sizeof(SK_DATA_BOUND_CONN));
+ read(sk_dgram_bound_conn, &buf, sizeof(buf));
+ if (strcmp(buf, SK_DATA_BOUND_CONN)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("dgram-bound-conn : '%s'\n", buf);
+
+ test_daemon();
+ test_waitsig();
+
+ memset(buf, 0, sizeof(buf));
+ sendto(sk_dgram_bound_client, SK_DATA_BOUND, sizeof(SK_DATA_BOUND), 0,
+ &name_bound, sizeof(name_bound));
+ read(sk_dgram_bound_server, &buf, sizeof(buf));
+ if (strcmp(buf, SK_DATA_BOUND)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("dgram-bound : '%s'\n", buf);
+
+ memset(buf, 0, sizeof(buf));
+ write(sk_dgram_conn_client, SK_DATA_CONN, sizeof(SK_DATA_CONN));
+ read(sk_dgram_conn_server, &buf, sizeof(buf));
+ if (strcmp(buf, SK_DATA_CONN)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("dgram-conn : '%s'\n", buf);
+
+ memset(buf, 0, sizeof(buf));
+ write(sk_dgram_bound_conn, SK_DATA_BOUND_CONN, sizeof(SK_DATA_BOUND_CONN));
+ read(sk_dgram_bound_conn, &buf, sizeof(buf));
+ if (strcmp(buf, SK_DATA_BOUND_CONN)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("dgram-bound-conn : '%s'\n", buf);
+
+ pass();
+
+ /*
+ * Do cleanup work
+ */
+ unlink(name_bound.sun_path);
+ unlink(name_conn.sun_path);
+ unlink(name_bound_conn.sun_path);
+ return 0;
+}
diff --git a/test/zdtm/static/sockets_spair.c b/test/zdtm/static/sockets_spair.c
new file mode 100644
index 000000000..f2c916f34
--- /dev/null
+++ b/test/zdtm/static/sockets_spair.c
@@ -0,0 +1,58 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test unix stream socketpair\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org";
+
+#define SK_DATA "packet"
+
+int main(int argc, char *argv[])
+{
+ int ssk_pair[2];
+ char buf[64];
+
+ test_init(argc, argv);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, ssk_pair) == -1) {
+ fail("socketpair\n");
+ exit(1);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ write(ssk_pair[0], SK_DATA, sizeof(SK_DATA));
+ read(ssk_pair[1], &buf, sizeof(buf));
+ if (strcmp(buf, SK_DATA)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("stream : '%s'\n", buf);
+
+ test_daemon();
+ test_waitsig();
+
+ memset(buf, 0, sizeof(buf));
+ write(ssk_pair[0], SK_DATA, sizeof(SK_DATA));
+ read(ssk_pair[1], &buf, sizeof(buf));
+ if (strcmp(buf, SK_DATA)) {
+ fail("data corrupted\n");
+ exit(1);
+ }
+ test_msg("stream : '%s'\n", buf);
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/sse00.c b/test/zdtm/static/sse00.c
new file mode 100644
index 000000000..0c04aba1b
--- /dev/null
+++ b/test/zdtm/static/sse00.c
@@ -0,0 +1,88 @@
+#include <string.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Start a calculation, leaving SSE in a certain state,\n"
+ "before migration, continue after";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+#if defined(__i386__) || defined(__x86_64__)
+void start(float *in)
+{
+ __asm__ volatile (
+ "movaps %0, %%xmm0\n"
+ "movaps %1, %%xmm1\n"
+ "addps %%xmm0, %%xmm1\n"
+ "sqrtps %%xmm1, %%xmm2\n"
+ :
+ : "m" (in[0]), "m" (in[4])
+ );
+}
+
+void finish(float *out)
+{
+ __asm__ volatile (
+ "movaps %%xmm1, %0\n"
+ "movaps %%xmm2, %1\n"
+ : "=m" (out[0]), "=m" (out[4])
+ );
+}
+
+static inline void cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
+{
+ __asm__("cpuid"
+ : "=a" (*eax),
+ "=b" (*ebx),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "0" (op), "c"(0));
+}
+
+int chk_proc_sse(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ cpuid(1, &eax, &ebx, &ecx, &edx);
+ return edx & (1 << 25);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ float input[8] __attribute__((aligned(16)));
+ float res1[8] __attribute__((aligned(16)));
+ float res2[8] __attribute__((aligned(16)));
+ int i;
+#endif
+
+ test_init(argc, argv);
+#if defined(__i386__) || defined(__x86_64__)
+ if (!chk_proc_sse()) {
+ skip("SSE not supported");
+ return 1;
+ }
+ for (i = 0; i < sizeof(input) / sizeof(float); i++)
+ input[i] = drand48();
+
+ start(input);
+ finish(res1);
+
+ start(input);
+ finish(res1);
+
+ test_daemon();
+ test_waitsig();
+
+ finish(res2);
+
+ if (memcmp((uint8_t *) res1, (uint8_t *) res2, sizeof(res1)))
+ fail("results differ\n");
+ else
+ pass();
+#else
+ skip("Unsupported arch");
+#endif
+ return 0;
+}
diff --git a/test/zdtm/static/sse00.desc b/test/zdtm/static/sse00.desc
new file mode 100644
index 000000000..d2f501de2
--- /dev/null
+++ b/test/zdtm/static/sse00.desc
@@ -0,0 +1 @@
+{'arch': 'x86_64'}
diff --git a/test/zdtm/static/sse20.c b/test/zdtm/static/sse20.c
new file mode 100644
index 000000000..912528b42
--- /dev/null
+++ b/test/zdtm/static/sse20.c
@@ -0,0 +1,88 @@
+#include <string.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Start a calculation, leaving SSE2 in a certain state,\n"
+ "before migration, continue after";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+#if defined(__i386__) || defined(__x86_64__)
+void start(double *in)
+{
+ __asm__ volatile (
+ "movapd %0, %%xmm0\n"
+ "movapd %1, %%xmm1\n"
+ "addpd %%xmm0, %%xmm1\n"
+ "sqrtpd %%xmm1, %%xmm2\n"
+ :
+ : "m" (in[0]), "m" (in[2])
+ );
+}
+
+void finish(double *out)
+{
+ __asm__ volatile (
+ "movapd %%xmm1, %0\n"
+ "movapd %%xmm2, %1\n"
+ : "=m" (out[0]), "=m" (out[2])
+ );
+}
+
+static inline void cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
+{
+ __asm__("cpuid"
+ : "=a" (*eax),
+ "=b" (*ebx),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "0" (op), "c"(0));
+}
+
+int chk_proc_sse2(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ cpuid(1, &eax, &ebx, &ecx, &edx);
+ return edx & (1 << 26);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ double input[4] __attribute__((aligned(16)));
+ double res1[4] __attribute__((aligned(16)));
+ double res2[4] __attribute__((aligned(16)));
+ int i;
+#endif
+
+ test_init(argc, argv);
+#if defined(__i386__) || defined(__x86_64__)
+ if (!chk_proc_sse2()) {
+ skip("SSE2 not supported");
+ return 1;
+ }
+
+ for (i = 0; i < sizeof(input) / sizeof(double); i++)
+ input[i] = drand48();
+
+ start(input);
+ finish(res1);
+
+ start(input);
+
+ test_daemon();
+ test_waitsig();
+
+ finish(res2);
+
+ if (memcmp((uint8_t *) res1, (uint8_t *) res2, sizeof(res1)))
+ fail("results differ\n");
+ else
+ pass();
+#else
+ skip("Unsupported arch");
+#endif
+ return 0;
+}
diff --git a/test/zdtm/static/sse20.desc b/test/zdtm/static/sse20.desc
new file mode 100644
index 000000000..d2f501de2
--- /dev/null
+++ b/test/zdtm/static/sse20.desc
@@ -0,0 +1 @@
+{'arch': 'x86_64'}
diff --git a/test/zdtm/static/stopped.c b/test/zdtm/static/stopped.c
new file mode 100644
index 000000000..af5780793
--- /dev/null
+++ b/test/zdtm/static/stopped.c
@@ -0,0 +1,86 @@
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <unistd.h>
+#include <syscall.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check, that stopped tasts are restored correctly";
+const char *test_author = "Andrew Vagin <avagin@parallels.com>";
+
+int main(int argc, char **argv)
+{
+ pid_t pid;
+ int p[2], ret, status;
+
+ test_init(argc, argv);
+
+ if (pipe(p)) {
+ pr_perror("Unable to create pipe");
+ return 1;
+ }
+
+ pid = test_fork();
+ if (pid < 0)
+ return -1;
+ else if (pid == 0) {
+ char c;
+
+ close(p[1]);
+ ret = read(p[0], &c, 1);
+ if (ret != 1) {
+ pr_perror("Unable to read: %d", ret);
+ return 1;
+ }
+
+ return 0;
+ }
+ close(p[0]);
+
+ kill(pid, SIGSTOP);
+ if (waitid(P_PID, pid, NULL, WNOWAIT | WSTOPPED) < 0) {
+ pr_perror("waitid");
+ return 1;
+ }
+#ifdef ZDTM_STOPPED_TKILL
+ syscall(__NR_tkill, pid, SIGSTOP);
+#endif
+#ifdef ZDTM_STOPPED_KILL
+ kill(pid, SIGSTOP);
+#endif
+
+ write(p[1], "0", 1);
+ close(p[1]);
+
+ test_daemon();
+ test_waitsig();
+
+ // Return immediately if child run or stopped(by SIGSTOP)
+ if (waitpid(pid, &status, WUNTRACED | WCONTINUED) == -1) {
+ pr_perror("Unable to wait child");
+ goto out;
+ }
+
+ if (WIFSTOPPED(status))
+ test_msg("The procces stopped\n");
+ else{
+ fail("The process doesn't stopped");
+ goto out;
+ }
+
+ kill(pid, SIGCONT);
+
+ if (waitpid(pid, &status, 0) == -1) {
+ pr_perror("Unable to wait child");
+ goto out;
+ }
+
+ if (WIFEXITED(status))
+ pass();
+ else
+ fail("The process doesn't continue");
+out:
+ return 0;
+}
diff --git a/test/zdtm/static/stopped01.c b/test/zdtm/static/stopped01.c
new file mode 120000
index 000000000..87790302e
--- /dev/null
+++ b/test/zdtm/static/stopped01.c
@@ -0,0 +1 @@
+stopped.c \ No newline at end of file
diff --git a/test/zdtm/static/stopped02.c b/test/zdtm/static/stopped02.c
new file mode 120000
index 000000000..87790302e
--- /dev/null
+++ b/test/zdtm/static/stopped02.c
@@ -0,0 +1 @@
+stopped.c \ No newline at end of file
diff --git a/test/zdtm/static/stopped12.c b/test/zdtm/static/stopped12.c
new file mode 120000
index 000000000..87790302e
--- /dev/null
+++ b/test/zdtm/static/stopped12.c
@@ -0,0 +1 @@
+stopped.c \ No newline at end of file
diff --git a/test/zdtm/static/tempfs.c b/test/zdtm/static/tempfs.c
new file mode 100644
index 000000000..66c51a5eb
--- /dev/null
+++ b/test/zdtm/static/tempfs.c
@@ -0,0 +1,111 @@
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check tmpfs mount";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+#define TEST_WORD "testtest"
+#define TEST_WORD2 "TESTTEST"
+
+int main(int argc, char **argv)
+{
+ int fd, fdo, ret = 1;
+ char buf[1024], fname[PATH_MAX], overmount[PATH_MAX];
+
+ test_init(argc, argv);
+
+ mkdir(dirname, 0700);
+ if (mount("none", dirname, "tmpfs", 0, "") < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ snprintf(fname, sizeof(buf), "%s/test.file", dirname);
+ fdo = open(fname, O_RDWR | O_CREAT, 0644);
+ if (fdo < 0) {
+ pr_perror("open failed");
+ goto err;
+ }
+
+ if (write(fdo, TEST_WORD, sizeof(TEST_WORD)) != sizeof(TEST_WORD)) {
+ pr_perror("write() failed");
+ goto err;
+ }
+
+ snprintf(overmount, sizeof(buf), "%s/test", dirname);
+ mkdir(overmount, 0700);
+
+ snprintf(fname, sizeof(buf), "%s/test.file", overmount);
+ fd = open(fname, O_RDWR | O_CREAT, 0644);
+ if (fd < 0) {
+ pr_perror("open failed");
+ goto err;
+ }
+
+ if (write(fd, TEST_WORD2, sizeof(TEST_WORD2)) != sizeof(TEST_WORD2)) {
+ pr_perror("write() failed");
+ goto err;
+ }
+ close(fd);
+
+ if (mount("none", overmount, "tmpfs", 0, "") < 0) {
+ fail("Can't mount tmpfs");
+ goto err;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (umount(overmount) < 0) {
+ fail("Can't mount tmpfs");
+ goto err;
+ }
+
+ lseek(fdo, 0, SEEK_SET);
+ buf[sizeof(TEST_WORD) + 1] = '\0';
+ if (read(fdo, buf, sizeof(TEST_WORD)) != sizeof(TEST_WORD)) {
+ fail("Read failed");
+ goto err;
+ }
+ close(fdo);
+
+ if (strcmp(buf, TEST_WORD)) {
+ fail("File corrupted");
+ goto err;
+ }
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("open failed");
+ goto err;
+ }
+
+ buf[sizeof(TEST_WORD2) + 1] = '\0';
+ if (read(fd, buf, sizeof(TEST_WORD2)) != sizeof(TEST_WORD2)) {
+ fail("Read failed");
+ goto err;
+ }
+ close(fd);
+
+ if (strcmp(buf, TEST_WORD2)) {
+ fail("File corrupted");
+ goto err;
+ }
+
+ pass();
+ ret = 0;
+err:
+ umount2(dirname, MNT_DETACH);
+ rmdir(dirname);
+ return ret;
+}
diff --git a/test/zdtm/static/tempfs.desc b/test/zdtm/static/tempfs.desc
new file mode 100644
index 000000000..7657ba45c
--- /dev/null
+++ b/test/zdtm/static/tempfs.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid'}
diff --git a/test/zdtm/static/tempfs_ro.c b/test/zdtm/static/tempfs_ro.c
new file mode 100644
index 000000000..f30ae8dc5
--- /dev/null
+++ b/test/zdtm/static/tempfs_ro.c
@@ -0,0 +1,78 @@
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check read-only tmpfs mount";
+const char *test_author = "Andrew Vagin <avagin@openvz.org>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+#define TEST_WORD "testtest"
+
+int main(int argc, char **argv)
+{
+ int fd, ret = 1;
+ char buf[1024], fname[PATH_MAX];
+
+ test_init(argc, argv);
+
+ mkdir(dirname, 0700);
+ if (mount("none", dirname, "tmpfs", 0, "") < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ snprintf(fname, sizeof(buf), "%s/test.file", dirname);
+ fd = open(fname, O_RDWR | O_CREAT, 0644);
+ if (fd < 0) {
+ pr_perror("open failed");
+ goto err;
+ }
+
+ if (write(fd, TEST_WORD, sizeof(TEST_WORD)) != sizeof(TEST_WORD)) {
+ pr_perror("write() failed");
+ goto err;
+ }
+ close(fd);
+
+ if (mount(NULL, dirname, "tmpfs", MS_REMOUNT | MS_RDONLY, NULL) < 0) {
+ fail("Can't mount tmpfs");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("open failed");
+ goto err;
+ }
+
+ buf[sizeof(TEST_WORD) + 1] = '\0';
+ if (read(fd, buf, sizeof(TEST_WORD)) != sizeof(TEST_WORD)) {
+ fail("Read failed");
+ goto err;
+ }
+ close(fd);
+
+ if (strcmp(buf, TEST_WORD)) {
+ fail("File corrupted");
+ goto err;
+ }
+
+ pass();
+ ret = 0;
+err:
+ umount2(dirname, MNT_DETACH);
+ rmdir(dirname);
+ return ret;
+}
diff --git a/test/zdtm/static/tempfs_ro.desc b/test/zdtm/static/tempfs_ro.desc
new file mode 100644
index 000000000..dfe829b84
--- /dev/null
+++ b/test/zdtm/static/tempfs_ro.desc
@@ -0,0 +1 @@
+{'flavor': 'ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/tempfs_subns.c b/test/zdtm/static/tempfs_subns.c
new file mode 100644
index 000000000..413d0a1e2
--- /dev/null
+++ b/test/zdtm/static/tempfs_subns.c
@@ -0,0 +1,136 @@
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <signal.h>
+#include <sys/prctl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check tmpfs in a non-root mntns";
+const char *test_author = "Andrew Vagin <avagin@virtuozzo.com";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+int main(int argc, char **argv)
+{
+ int fds[2], i;
+ pid_t pid;
+ int fd, status;
+
+ test_init(argc, argv);
+
+ if (pipe(fds)) {
+ pr_perror("pipe");
+ return 1;
+ }
+
+ if (mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL)) {
+ pr_perror("mount");
+ }
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork");
+ return 1;
+ }
+ if (pid == 0) {
+ void *addr;
+
+ pid = fork();
+ if (pid == 0) {
+ if (write(fds[1], &fd, sizeof(fd)) != sizeof(fd)) {
+ pr_perror("write");
+ return 1;
+ }
+ if (unshare(CLONE_NEWNS)) {
+ pr_perror("unshare");
+ return 1;
+ }
+ prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+ while (1)
+ sleep(1);
+ return 1;
+ }
+ pid = fork();
+ if (pid == 0) {
+ if (write(fds[1], &fd, sizeof(fd)) != sizeof(fd)) {
+ pr_perror("write");
+ return 1;
+ }
+ prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+ while (1)
+ sleep(1);
+ return 1;
+ }
+ if (unshare(CLONE_NEWNS)) {
+ pr_perror("unshare");
+ return 1;
+ }
+ mkdir(dirname, 0755);
+ if (mount("zdtm", dirname, "tmpfs", 0, NULL)) {
+ pr_perror("mount");
+ return 1;
+ }
+
+ chdir(dirname);
+ fd = open("test", O_CREAT | O_RDWR | O_APPEND, 0666);
+ if (fd < 0) {
+ pr_perror("open");
+ return 1;
+ }
+ ftruncate(fd, PAGE_SIZE);
+ addr = mmap(NULL, PAGE_SIZE, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0);
+ if (addr == MAP_FAILED) {
+ pr_perror("mmap");
+ return 1;
+ }
+
+ if (write(fds[1], &fd, sizeof(fd)) != sizeof(fd)) {
+ pr_perror("write");
+ return 1;
+ }
+
+ test_waitsig();
+ if (close(fd)) {
+ pr_perror("close");
+ return 1;
+ }
+
+ fd = open("test", O_RDONLY | O_APPEND);
+ if (fd < 0) {
+ pr_perror("open");
+ return 1;
+ }
+ close(fd);
+ return 0;
+ }
+ close(fds[1]);
+
+ for (i = 0; i < 3; i++) {
+ if (read(fds[0], &fd, sizeof(fd)) != sizeof(fd)) {
+ pr_perror("read");
+ return 1;
+ }
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ kill(pid, SIGTERM);
+ status = -1;
+ if (waitpid(pid, &status, 0) != pid) {
+ pr_perror("waitpid");
+ return 1;
+ }
+ if (status) {
+ pr_err("Returned non-zero code: 0x%x\n", status);
+ return 1;
+ }
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/tempfs_subns.desc b/test/zdtm/static/tempfs_subns.desc
new file mode 100644
index 000000000..a8849e097
--- /dev/null
+++ b/test/zdtm/static/tempfs_subns.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid', 'feature': 'mnt_id'}
diff --git a/test/zdtm/static/timerfd.c b/test/zdtm/static/timerfd.c
new file mode 100644
index 000000000..132c3acd8
--- /dev/null
+++ b/test/zdtm/static/timerfd.c
@@ -0,0 +1,166 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <time.h>
+
+#include <sys/timerfd.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Checks timerfd survives checkpoint/restore\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
+
+#define TIMERFD_VNSEC 50000
+#define TIMERFD_ISEC 4
+
+struct timerfd_status {
+ int clockid;
+ uint64_t ticks;
+ int settime_flags;
+ struct itimerspec v;
+};
+
+static void show_timerfd(char *prefix, struct timerfd_status *s)
+{
+ test_msg("\t%s clockid %d ticks %llu settime_flags %d it_value(%llu, %llu) it_interval(%llu, %llu)\n",
+ prefix,
+ s->clockid,
+ (unsigned long long)s->ticks,
+ s->settime_flags,
+ (unsigned long long)s->v.it_value.tv_sec,
+ (unsigned long long)s->v.it_value.tv_nsec,
+ (unsigned long long)s->v.it_interval.tv_sec,
+ (unsigned long long)s->v.it_interval.tv_nsec);
+}
+
+static int parse_self_fdinfo(int fd, struct timerfd_status *s)
+{
+ char buf[256];
+ int ret = -1;
+ FILE *f;
+
+ sprintf(buf, "/proc/self/fdinfo/%d", fd);
+ f = fopen(buf, "r");
+ if (!f) {
+ pr_perror("Can't open %s to parse", buf);
+ return -1;
+ }
+
+ memset(s, 0, sizeof(*s));
+
+ /*
+ * clockid: 0
+ * ticks: 0
+ * settime flags: 01
+ * it_value: (0, 49406829)
+ * it_interval: (1, 0)
+ */
+ while (fgets(buf, sizeof(buf), f)) {
+ if (strncmp(buf, "clockid:", 8))
+ continue;
+
+ if (sscanf(buf, "clockid: %d", &s->clockid) != 1)
+ goto parse_err;
+
+ if (!fgets(buf, sizeof(buf), f))
+ goto parse_err;
+ if (sscanf(buf, "ticks: %llu", (unsigned long long *)&s->ticks) != 1)
+ goto parse_err;
+
+ if (!fgets(buf, sizeof(buf), f))
+ goto parse_err;
+ if (sscanf(buf, "settime flags: 0%o", &s->settime_flags) != 1)
+ goto parse_err;
+
+ if (!fgets(buf, sizeof(buf), f))
+ goto parse_err;
+ if (sscanf(buf, "it_value: (%llu, %llu)",
+ (unsigned long long *)&s->v.it_value.tv_sec,
+ (unsigned long long *)&s->v.it_value.tv_nsec) != 2)
+ goto parse_err;
+
+ if (!fgets(buf, sizeof(buf), f))
+ goto parse_err;
+ if (sscanf(buf, "it_interval: (%llu, %llu)",
+ (unsigned long long *)&s->v.it_interval.tv_sec,
+ (unsigned long long *)&s->v.it_interval.tv_nsec) != 2)
+ goto parse_err;
+
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ goto parse_err;
+err:
+ fclose(f);
+ return ret;
+
+parse_err:
+ pr_perror("Format error");
+ goto err;
+}
+
+static int check_timerfd(int fd, struct timerfd_status *old)
+{
+ struct timerfd_status new;
+
+ if (parse_self_fdinfo(fd, &new))
+ return -1;
+ show_timerfd("restored", &new);
+
+ if (old->clockid != new.clockid ||
+ old->settime_flags != new.settime_flags ||
+ old->ticks > new.ticks ||
+ old->v.it_value.tv_sec > new.v.it_value.tv_sec ||
+ old->v.it_interval.tv_sec != new.v.it_interval.tv_sec)
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct timerfd_status old = {
+ .clockid = CLOCK_MONOTONIC,
+ .ticks = 0,
+ .settime_flags = 0,
+ .v = {
+ .it_value = {
+ .tv_sec = 0,
+ .tv_nsec= TIMERFD_VNSEC,
+ },
+ .it_interval = {
+ .tv_sec = TIMERFD_ISEC,
+ .tv_nsec= 0,
+ },
+ },
+ };
+ int timerfd = 0, ret;
+
+ test_init(argc, argv);
+
+ timerfd = timerfd_create(old.clockid, 0);
+ if (timerfd < 0) {
+ pr_perror("timerfd_create failed");
+ return -1;
+ }
+
+ show_timerfd("setup", &old);
+ if (timerfd_settime(timerfd, old.settime_flags, &old.v, NULL)) {
+ pr_perror("timerfd_settime failed");
+ return -1;
+ }
+ sleep(1);
+
+ test_daemon();
+ test_waitsig();
+
+ ret = check_timerfd(timerfd, &old);
+ if (ret)
+ fail();
+ else
+ pass();
+ return ret;
+}
diff --git a/test/zdtm/static/timerfd.desc b/test/zdtm/static/timerfd.desc
new file mode 100644
index 000000000..6f5419321
--- /dev/null
+++ b/test/zdtm/static/timerfd.desc
@@ -0,0 +1 @@
+{'feature': 'timerfd'}
diff --git a/test/zdtm/static/timers.c b/test/zdtm/static/timers.c
new file mode 100644
index 000000000..256a5c1be
--- /dev/null
+++ b/test/zdtm/static/timers.c
@@ -0,0 +1,93 @@
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Checks timers keep ticking after migration\n";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+static struct {
+ const int timer_type;
+ const int signal;
+ volatile sig_atomic_t count;
+} timer_tests[] = { /* from slowest to fastest */
+ { ITIMER_VIRTUAL, SIGVTALRM },
+ { ITIMER_PROF, SIGPROF },
+ { ITIMER_REAL, SIGALRM },
+};
+
+#define NUM_TIMERS (sizeof(timer_tests) / sizeof(timer_tests[0]))
+#define MAX_TIMER_COUNT 10
+
+static void timer_tick(int sig)
+{
+ int i;
+ for (i = 0; i < NUM_TIMERS; i++)
+ if (timer_tests[i].signal == sig) {
+ /* don't go beyond MAX_TIMER_COUNT, to avoid overflow */
+ if (timer_tests[i].count < MAX_TIMER_COUNT)
+ timer_tests[i].count++;
+ break;
+ }
+}
+
+static void setup_timers(void)
+{
+ int i;
+ struct itimerval tv = {
+ .it_interval = {
+ .tv_sec = 0,
+ .tv_usec = 100000
+ },
+ .it_value = {
+ .tv_sec = 0,
+ .tv_usec = 100
+ },
+ };
+
+ for (i = 0; i < NUM_TIMERS; i++) {
+ if (signal(timer_tests[i].signal, timer_tick) == SIG_ERR) {
+ pr_perror("can't set signal handler %d", i);
+ exit(1);
+ }
+
+ if (setitimer(timer_tests[i].timer_type, &tv, NULL) < 0) {
+ pr_perror("can't set timer %d", i);
+ exit(1);
+ }
+ }
+}
+
+static void check_timers(void)
+{
+ int i;
+ volatile unsigned int j; /* avoid optimizing the loop away */
+
+ for (i = 0; i < NUM_TIMERS; i++) /* reset counters first */
+ timer_tests[i].count = 0;
+
+ /* waste some real and CPU time: run for MAX_TIMER_COUNT ticks or until
+ * j overflows */
+ for (j = 1; j && timer_tests[0].count < MAX_TIMER_COUNT; j++);
+
+ for (i = 0; i < NUM_TIMERS; i++)
+ if (!timer_tests[i].count) {
+ fail("timer %d stuck", i);
+ return;
+ }
+ pass();
+}
+
+int main(int argc, char **argv)
+{
+ test_init(argc, argv);
+
+ setup_timers();
+
+ test_daemon();
+ test_waitsig();
+
+ check_timers();
+ return 0;
+}
diff --git a/test/zdtm/static/tty00.c b/test/zdtm/static/tty00.c
new file mode 100644
index 000000000..749b9bc64
--- /dev/null
+++ b/test/zdtm/static/tty00.c
@@ -0,0 +1,100 @@
+#define _XOPEN_SOURCE
+#include <stdlib.h>
+#include "zdtmtst.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+const char *test_doc = "Check that a control terminal is restored";
+const char *test_author = "Andrey Vagin <avagin@openvz.org>";
+
+static int sighup = 0;
+static void sighup_handler(int signo)
+{
+ test_msg("SIGHUP is here\n");
+ sighup = 1;
+}
+
+int main(int argc, char ** argv)
+{
+ int fdm, fds, status;
+ char *slavename;
+ pid_t pid;
+
+ test_init(argc, argv);
+
+ fdm = open("/dev/ptmx", O_RDWR);
+ if (fdm == -1) {
+ pr_perror("Can't open a master pseudoterminal");
+ return 1;
+ }
+
+ grantpt(fdm);
+ unlockpt(fdm);
+ slavename = ptsname(fdm);
+
+ pid = test_fork();
+ if (pid < 0) {
+ pr_perror("fork() failed");
+ return 1;
+ }
+
+ if (pid == 0) {
+ close(fdm);
+ signal(SIGHUP, sighup_handler);
+
+ if (setsid() == -1)
+ return 1;
+
+ /* set up a controlling terminal */
+ fds = open(slavename, O_RDWR);
+ if (fds == -1) {
+ pr_perror("Can't open a slave pseudoterminal %s", slavename);
+ return 1;
+ }
+
+ if (ioctl(fdm, TIOCSCTTY, 1) < 0) {
+ pr_perror("Can't setup a controlling terminal");
+ return 1;
+ }
+ close(fds);
+
+ test_waitsig();
+ if (sighup)
+ return 0;
+ return 1;
+ }
+
+
+ test_daemon();
+
+ test_waitsig();
+
+ close(fdm);
+
+ if (kill(pid, SIGTERM) == -1) {
+ pr_perror("kill failed");
+ return 1;
+ }
+
+ pid = waitpid(pid, &status, 0);
+ if (pid < 0)
+ return 1;
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status)) {
+ fail("The child returned %d", WEXITSTATUS(status));
+ return 1;
+ }
+ } else
+ test_msg("The child has been killed by %d\n", WTERMSIG(status));
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/tty00.desc b/test/zdtm/static/tty00.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/tty00.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/tty02.c b/test/zdtm/static/tty02.c
new file mode 100644
index 000000000..b179e2fe3
--- /dev/null
+++ b/test/zdtm/static/tty02.c
@@ -0,0 +1,53 @@
+#define _XOPEN_SOURCE
+#include <stdlib.h>
+#include "zdtmtst.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+const char *test_doc = "Check a non-controling terminal";
+const char *test_author = "Andrey Vagin <avagin@openvz.org>";
+
+int main(int argc, char ** argv)
+{
+ int fdm, fds;
+ char *slavename;
+ pid_t sid;
+
+ test_init(argc, argv);
+
+ setsid();
+
+ fdm = open("/dev/ptmx", O_RDWR);
+ if (fdm == -1) {
+ pr_perror("Can't open a master pseudoterminal");
+ return 1;
+ }
+
+ grantpt(fdm);
+ unlockpt(fdm);
+ slavename = ptsname(fdm);
+
+ /* set up a controlling terminal */
+ fds = open(slavename, O_RDWR | O_NOCTTY);
+ if (fds == -1) {
+ pr_perror("Can't open a slave pseudoterminal %s", slavename);
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (ioctl(fds, TIOCGSID, &sid) != -1 || errno != ENOTTY) {
+ fail("The tty is a controlling for someone");
+ return 1;
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/tty03.c b/test/zdtm/static/tty03.c
new file mode 100644
index 000000000..c9d4a25dc
--- /dev/null
+++ b/test/zdtm/static/tty03.c
@@ -0,0 +1,114 @@
+#define _XOPEN_SOURCE
+#include <stdlib.h>
+#include "zdtmtst.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+const char *test_doc = "Check a controlling terminal, if a proper fd belongs to another session leader";
+const char *test_author = "Andrey Vagin <avagin@openvz.org>";
+
+int main(int argc, char ** argv)
+{
+ int fdm, fds, exit_code = 1, status;
+ char *slavename;
+ pid_t sid_b, sid_a, pid;
+ int pfd[2];
+
+ test_init(argc, argv);
+
+ if (pipe(pfd) == -1) {
+ pr_perror("pipe");
+ return 1;
+ }
+
+ fdm = open("/dev/ptmx", O_RDWR);
+ if (fdm == -1) {
+ pr_perror("Can't open a master pseudoterminal");
+ return 1;
+ }
+
+ grantpt(fdm);
+ unlockpt(fdm);
+ slavename = ptsname(fdm);
+
+ pid = test_fork();
+ if (pid == 0) {
+ if (setsid() == -1) {
+ pr_perror("setsid");
+ return 1;
+ }
+
+ close(pfd[0]);
+
+ /* set up a controlling terminal */
+ fds = open(slavename, O_RDWR | O_NOCTTY);
+ if (fds == -1) {
+ pr_perror("Can't open a slave pseudoterminal %s", slavename);
+ return 1;
+ }
+ ioctl(fds, TIOCSCTTY, 1);
+
+ pid = test_fork();
+ if (pid == 0) {
+ if (setsid() == -1) {
+ pr_perror("setsid");
+ return 1;
+ }
+
+ close(pfd[1]);
+
+ test_waitsig();
+ exit(0);
+ }
+
+ close(fds);
+ close(pfd[1]);
+
+ test_waitsig();
+
+ kill(pid, SIGTERM);
+ wait(&status);
+
+ exit(status);
+ }
+
+ close(pfd[1]);
+ if (read(pfd[0], &sid_a, 1) != 0) {
+ pr_perror("read");
+ goto out;
+ }
+
+ if (ioctl(fdm, TIOCGSID, &sid_b) == -1) {
+ pr_perror("The tty is not a controlling");
+ goto out;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (ioctl(fdm, TIOCGSID, &sid_a) == -1) {
+ fail("The tty is not a controlling");
+ goto out;
+ }
+
+ if (sid_b != sid_a) {
+ fail("The tty is controlling for someone else");
+ goto out;
+ }
+
+ exit_code = 0;
+
+out:
+ kill(pid, SIGTERM);
+ wait(&status);
+
+ if (status == 0 && exit_code == 0)
+ pass();
+
+ return exit_code;
+}
diff --git a/test/zdtm/static/tun.c b/test/zdtm/static/tun.c
new file mode 100644
index 000000000..c53b8fa88
--- /dev/null
+++ b/test/zdtm/static/tun.c
@@ -0,0 +1,236 @@
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test TUN/TAP devices\n";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+#define TUN_DEVICE "/dev/net/tun"
+#ifndef IFF_MULTI_QUEUE
+#define IFF_MULTI_QUEUE 0x0100
+#define IFF_ATTACH_QUEUE 0x0200
+#define IFF_DETACH_QUEUE 0x0400
+#define IFF_PERSIST 0x0800
+#endif
+
+#ifndef TUNSETQUEUE
+#define TUNSETQUEUE _IOW('T', 217, int)
+#endif
+
+static int any_fail = 0;
+
+static int __open_tun(void)
+{
+ int fd;
+
+ fd = open(TUN_DEVICE, O_RDWR);
+ if (fd < 0)
+ pr_perror("Can't open tun file");
+
+ return fd;
+}
+
+static int set_tun_queue(int fd, unsigned flags)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = flags;
+
+ if (ioctl(fd, TUNSETQUEUE, &ifr) < 0) {
+ pr_perror("Can't set queue");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int __attach_tun(int fd, char *name, unsigned flags)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, name);
+ ifr.ifr_flags = flags;
+
+ if (ioctl(fd, TUNSETIFF, &ifr) < 0) {
+ if (!(flags & IFF_TUN_EXCL))
+ pr_perror("Can't attach iff %s", name);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int open_tun(char *name, unsigned flags)
+{
+ int fd;
+
+ fd = __open_tun();
+ if (fd < 0)
+ return -1;
+
+ return __attach_tun(fd, name, flags);
+}
+
+static void check_tun(int fd, char *name, unsigned flags)
+{
+ struct ifreq ifr;
+
+ if (ioctl(fd, TUNGETIFF, &ifr) > 0) {
+ any_fail = 1;
+ fail("Attached tun %s file lost device", name);
+ }
+
+ if (strcmp(ifr.ifr_name, name)) {
+ any_fail = 1;
+ fail("Attached tun %s wrong device", name);
+ }
+
+ if ((ifr.ifr_flags & flags) != flags) {
+ any_fail = 1;
+ fail("Attached tun %s wrong device type", name);
+ }
+}
+
+static int dev_get_hwaddr(int fd, char *a)
+{
+ struct ifreq ifr;
+
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+ pr_perror("Can't get hwaddr");
+ return -1;
+ }
+
+ memcpy(a, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int fds[5], ret;
+ char addr[ETH_ALEN], a2[ETH_ALEN];
+
+ test_init(argc, argv);
+
+ /* fd[0] -- opened file */
+ fds[0] = __open_tun();
+ if (fds[0] < 0) {
+ pr_perror("No file 0");
+ return -1;
+ }
+
+ /* fd[1] -- opened file with tun device */
+ fds[1] = open_tun("tunx0", IFF_TUN);
+ if (fds[1] < 0) {
+ pr_perror("No file 1");
+ return -1;
+ }
+
+ /* fd[2] and [3] -- two-queued device, with 3 detached */
+ fds[2] = open_tun("tunx1", IFF_TUN | IFF_MULTI_QUEUE);
+ if (fds[2] < 0) {
+ pr_perror("No file 2");
+ return -1;
+ }
+
+ fds[3] = open_tun("tunx1", IFF_TUN | IFF_MULTI_QUEUE);
+ if (fds[3] < 0) {
+ pr_perror("No file 3");
+ return -1;
+ }
+
+ ret = set_tun_queue(fds[3], IFF_DETACH_QUEUE);
+ if (ret < 0)
+ return -1;
+
+ /* special case -- persistent device */
+ ret = open_tun("tunx2", IFF_TUN);
+ if (ret < 0) {
+ pr_perror("No persistent device");
+ return -1;
+ }
+
+ if (ioctl(ret, TUNSETPERSIST, 1) < 0) {
+ pr_perror("Can't make persistent");
+ return -1;
+ }
+
+ /* and one tap in fd[4] */
+ fds[4] = open_tun("tapx0", IFF_TAP);
+ if (fds[4] < 0) {
+ pr_perror("No tap");
+ return -1;
+ }
+
+ if (dev_get_hwaddr(fds[4], addr) < 0) {
+ pr_perror("No hwaddr for tap?");
+ return -1;
+ }
+
+ close(ret);
+
+ test_daemon();
+ test_waitsig();
+
+ /* check fds[0] is not attached to device */
+ ret = __attach_tun(fds[0], "tunx3", IFF_TUN);
+ if (ret < 0) {
+ any_fail = 1;
+ fail("Opened tun file broken");
+ }
+
+ /* check that fds[1] has device */
+ check_tun(fds[1], "tunx0", IFF_TUN);
+
+ /* check that fds[2] and [3] are at MQ device with */
+ check_tun(fds[2], "tunx1", IFF_TUN | IFF_MULTI_QUEUE);
+ check_tun(fds[3], "tunx1", IFF_TUN | IFF_MULTI_QUEUE);
+
+ ret = set_tun_queue(fds[2], IFF_DETACH_QUEUE);
+ if (ret < 0) {
+ any_fail = 1;
+ fail("Queue not attached");
+ }
+
+ ret = set_tun_queue(fds[3], IFF_ATTACH_QUEUE);
+ if (ret < 0) {
+ any_fail = 1;
+ fail("Queue not detached");
+ }
+
+ /* check persistent device */
+ ret = open_tun("tunx2", IFF_TUN | IFF_TUN_EXCL);
+ if (ret >= 0) {
+ any_fail = 1;
+ fail("Persistent device lost");
+ } else {
+ ret = open_tun("tunx2", IFF_TUN);
+ if (ret < 0)
+ pr_perror("Can't attach tun2");
+ else
+ ioctl(ret, TUNSETPERSIST, 0);
+ }
+
+ check_tun(fds[4], "tapx0", IFF_TAP);
+ if (dev_get_hwaddr(fds[4], a2) < 0) {
+ pr_perror("No hwaddr for tap? (2)");
+ any_fail = 1;
+ } else if (memcmp(addr, a2, sizeof(addr))) {
+ fail("Address mismatch on tap %x:%x -> %x:%x",
+ (int)addr[0], (int)addr[1],
+ (int)a2[0], (int)a2[1]);
+ any_fail = 1;
+ }
+
+ if (!any_fail)
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/tun.desc b/test/zdtm/static/tun.desc
new file mode 100644
index 000000000..8c7cfe860
--- /dev/null
+++ b/test/zdtm/static/tun.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid', 'feature': 'tun'}
diff --git a/test/zdtm/static/umask00.c b/test/zdtm/static/umask00.c
new file mode 100644
index 000000000..1157f0d08
--- /dev/null
+++ b/test/zdtm/static/umask00.c
@@ -0,0 +1,30 @@
+#include <sys/stat.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that umask didn't change";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+unsigned int mask;
+TEST_OPTION(mask, uint, "umask", 1);
+
+int main(int argc, char **argv)
+{
+ unsigned int cur_mask, mask2;
+
+ test_init(argc, argv);
+
+ cur_mask = umask(mask);
+
+ test_daemon();
+ test_waitsig();
+
+ mask2 = umask(0);
+ if (mask != mask2)
+ fail("mask changed: %o != %o\n", mask, mask2);
+ else
+ pass();
+
+ umask(cur_mask);
+ return 0;
+}
diff --git a/test/zdtm/static/unbound_sock.c b/test/zdtm/static/unbound_sock.c
new file mode 100644
index 000000000..be8318c5f
--- /dev/null
+++ b/test/zdtm/static/unbound_sock.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Create a socket before migration, and bind to it after\n";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+#define TEST_PORT 59687
+#define TEST_ADDR INADDR_ANY
+
+int main(int argc, char ** argv)
+{
+ int sock;
+ struct sockaddr_in name = {
+ .sin_family = AF_INET,
+ .sin_port = htons(TEST_PORT),
+ .sin_addr.s_addr = htonl(TEST_ADDR),
+ };
+
+ test_init(argc, argv);
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ pr_perror("can't create socket");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0)
+ fail("can't bind to a socket: %m");
+ else
+ pass();
+
+ close(sock);
+ return 0;
+}
diff --git a/test/zdtm/static/unhashed_proc.c b/test/zdtm/static/unhashed_proc.c
new file mode 100644
index 000000000..1fdc38f67
--- /dev/null
+++ b/test/zdtm/static/unhashed_proc.c
@@ -0,0 +1,81 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <linux/limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Chdir into unhashed proc entry";
+const char *test_author = "Konstantin Khlebnikov <khlebnikov@openvz.org>";
+
+int main(int argc, char ** argv)
+{
+ int pid, len;
+ char cwd1[PATH_MAX], cwd2[PATH_MAX];
+
+ test_init(argc, argv);
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork failed");
+ exit(1);
+ } else if (!pid) {
+ pause();
+ return 0;
+ }
+
+ sprintf(cwd1, "/proc/%d", pid);
+
+ if (chdir(cwd1) < 0) {
+ kill(pid, SIGKILL);
+ pr_perror("chdir failed");
+ exit(1);
+ }
+
+ kill(pid, SIGKILL);
+ waitpid(pid, NULL, 0);
+
+ if (getcwd(cwd1, sizeof(cwd1))) {
+ pr_perror("successful getcwd: %s", cwd1);
+ exit(1);
+ } else if (errno != ENOENT) {
+ pr_perror("wrong errno");
+ exit(1);
+ }
+
+ len = readlink("/proc/self/cwd", cwd1, sizeof(cwd1));
+ if (len < 0) {
+ pr_perror("can't read cwd symlink");
+ exit(1);
+ }
+ cwd1[len] = 0;
+
+ test_daemon();
+ test_waitsig();
+
+ if (getcwd(cwd2, sizeof(cwd2))) {
+ fail("successful getcwd: %s\n", cwd2);
+ exit(1);
+ } else if (errno != ENOENT) {
+ fail("wrong errno: %m\n");
+ exit(1);
+ }
+
+ len = readlink("/proc/self/cwd", cwd2, sizeof(cwd2)-1);
+ if (len < 0) {
+ fail("can't read cwd symlink %m\n");
+ exit(1);
+ }
+ cwd2[len] = 0;
+
+ if (strcmp(cwd1, cwd2))
+ test_msg("cwd differs: %s != %s\n", cwd1, cwd2);
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/unhashed_proc.desc b/test/zdtm/static/unhashed_proc.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/unhashed_proc.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/unlink_fifo.c b/test/zdtm/static/unlink_fifo.c
new file mode 100644
index 000000000..765f5eb07
--- /dev/null
+++ b/test/zdtm/static/unlink_fifo.c
@@ -0,0 +1,50 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that we can migrate with a named pipe "
+ "open and then unlinked";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char **argv)
+{
+ int fd;
+ mode_t mode = S_IFIFO | 0700;
+
+ test_init(argc, argv);
+
+ if (mknod(filename, mode, 0)) {
+ pr_perror("can't make fifo \"%s\"", filename);
+ exit(1);
+ }
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ return 1;
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink %s", filename);
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (close(fd) < 0) {
+ fail("can't close %s: %m", filename);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/unlink_fifo_wronly.c b/test/zdtm/static/unlink_fifo_wronly.c
new file mode 100644
index 000000000..5fb4c3450
--- /dev/null
+++ b/test/zdtm/static/unlink_fifo_wronly.c
@@ -0,0 +1,60 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that we can migrate with a named pipe, "
+ "opened in WRONLY mode and then unlinked";
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char **argv)
+{
+ int fd, fd1;
+ mode_t mode = S_IFIFO | 0600;
+
+ test_init(argc, argv);
+
+ if (mknod(filename, mode, 0)) {
+ pr_perror("can't make fifo \"%s\"", filename);
+ exit(1);
+ }
+
+ fd = open(filename, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ pr_perror("open(%s, O_RDONLY | O_NONBLOCK) Failed",
+ filename);
+ return 1;
+ }
+
+ fd1 = open(filename, O_WRONLY);
+ if (fd1 < 0) {
+ pr_perror("open(%s, O_WRONLY) Failed", filename);
+ return 1;
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink %s", filename);
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (close(fd) < 0) {
+ fail("can't close (O_RDONLY | O_NONBLOCK) %s: %m", filename);
+ return 1;
+ }
+
+ if (close(fd1) < 0) {
+ fail("can't close (O_WRONLY) %s: %m", filename);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/unlink_fstat00.c b/test/zdtm/static/unlink_fstat00.c
new file mode 100644
index 000000000..4965dbc05
--- /dev/null
+++ b/test/zdtm/static/unlink_fstat00.c
@@ -0,0 +1,136 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Open, unlink, change size, seek, migrate, check size";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char ** argv)
+{
+ int fd;
+ size_t fsize=1000;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ uint8_t buf[fsize];
+ struct stat fst;
+ uint32_t crc;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ if (fstat(fd, &fst) < 0) {
+ pr_perror("can't get file info %s before", filename);
+ goto failed;
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink %s", filename);
+ goto failed;
+ }
+ /* Change file size */
+ if (fst.st_size != 0) {
+ pr_perror("%s file size eq %d", fst.st_size);
+ goto failed;
+ }
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ pr_perror("can't write %s", filename);
+ goto failed;
+ }
+ /* Change file mode */
+ if ((fst.st_mode & S_IXOTH) == 0)
+ mode = (fst.st_mode | S_IXOTH);
+ else
+ mode = (fst.st_mode ^ S_IXOTH);
+
+ if (fchmod(fd, mode) < 0) {
+ pr_perror("can't chmod %s", filename);
+ goto failed;
+ }
+
+ if (getuid()) {
+ uid = getuid();
+ gid = getgid();
+ } else {
+ /* Change uid, gid */
+ if (fchown(fd, (uid = fst.st_uid + 1), (gid = fst.st_gid + 1)) < 0) {
+ pr_perror("can't chown %s", filename);
+ goto failed;
+ }
+ }
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ pr_perror("can't reposition to 0");
+ goto failed;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (fstat(fd, &fst) < 0) {
+ pr_perror("can't get %s file info after", filename);
+ goto failed;
+ }
+
+ /* Check file size */
+ if (fst.st_size != fsize) {
+ fail("(via fstat): file size changed to %d", fst.st_size);
+ goto failed;
+ }
+ fst.st_size = lseek(fd, 0, SEEK_END);
+ if (fst.st_size != fsize) {
+ fail("(via lseek): file size changed to %d", fst.st_size);
+ goto failed;
+ }
+ /* Check mode */
+ if (fst.st_mode != mode) {
+ fail("mode is changed to %o(%o)", fst.st_mode, mode);
+ goto failed;
+ }
+ /* Check uid, gid */
+ if (fst.st_uid != uid || fst.st_gid != gid) {
+ fail("u(g)id changed: uid=%d(%d), gid=%d(%d)",
+ fst.st_uid, uid, fst.st_gid, gid);
+ goto failed;
+ }
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ pr_perror("can't reposition to 0");
+ goto failed;
+ }
+ if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ fail("can't read %s: %m\n", filename);
+ goto failed;
+ }
+
+ crc = ~0;
+ if (datachk(buf, sizeof(buf), &crc)) {
+ fail("CRC mismatch\n");
+ goto failed;
+ }
+
+ close(fd);
+
+ pass();
+ return 0;
+failed:
+ unlink(filename);
+ close(fd);
+ return 1;
+}
diff --git a/test/zdtm/static/unlink_fstat00.hook b/test/zdtm/static/unlink_fstat00.hook
new file mode 100755
index 000000000..dfae0f60c
--- /dev/null
+++ b/test/zdtm/static/unlink_fstat00.hook
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+[ "$1" == "--fault" -a "$2" == "restore" ] || exit 0
+
+if [ $(find -name 'unlink_fstat00*ghost' | wc -l ) -ne 0 ]; then
+ echo "Dangling ghost file"
+ exit 1
+fi
+
+echo "Restore fault handled"
+exit 0
diff --git a/test/zdtm/static/unlink_fstat01+.c b/test/zdtm/static/unlink_fstat01+.c
new file mode 120000
index 000000000..232e6983c
--- /dev/null
+++ b/test/zdtm/static/unlink_fstat01+.c
@@ -0,0 +1 @@
+unlink_fstat01.c \ No newline at end of file
diff --git a/test/zdtm/static/unlink_fstat01.c b/test/zdtm/static/unlink_fstat01.c
new file mode 100644
index 000000000..104abec4f
--- /dev/null
+++ b/test/zdtm/static/unlink_fstat01.c
@@ -0,0 +1,91 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Open, unlink, change size, migrate, check size";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char ** argv)
+{
+ int fd;
+ size_t fsize=1000;
+ uint8_t buf[fsize];
+ struct stat fst;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ if (fstat(fd, &fst) < 0) {
+ pr_perror("can't get file info %s before", filename);
+ goto failed;
+ }
+
+ if (fst.st_size != 0) {
+ pr_perror("%s file size eq %d", fst.st_size);
+ goto failed;
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink %s", filename);
+ goto failed;
+ }
+
+#ifdef UNLINK_OVER
+{
+ int fdo;
+
+ fdo = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fdo < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+}
+#endif
+
+ memset(buf, '0', sizeof(buf));
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ pr_perror("can't write %s", filename);
+ goto failed;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (fstat(fd, &fst) < 0) {
+ pr_perror("can't get %s file info after", filename);
+ goto failed;
+ }
+
+ if (fst.st_size != fsize) {
+ fail("(via fstat): file size changed to %d", fst.st_size);
+ goto failed;
+ }
+
+ fst.st_size = lseek(fd, 0, SEEK_END);
+ if (fst.st_size != fsize) {
+ fail("(via lseek): file size changed to %d", fst.st_size);
+ goto failed;
+ }
+
+ close(fd);
+
+ pass();
+ return 0;
+failed:
+ unlink(filename);
+ close(fd);
+ return 1;
+}
diff --git a/test/zdtm/static/unlink_fstat02.c b/test/zdtm/static/unlink_fstat02.c
new file mode 100644
index 000000000..dd18b8fb0
--- /dev/null
+++ b/test/zdtm/static/unlink_fstat02.c
@@ -0,0 +1,112 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Open, link, unlink x2, change size, migrate, check size";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+static char link_name[1024];
+
+int main(int argc, char ** argv)
+{
+ int fd[2];
+ size_t fsize=1000;
+ uint8_t buf[fsize];
+ struct stat fst, fst2;
+
+ test_init(argc, argv);
+
+ fd[0] = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd[0] < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ sprintf(link_name, "%s.link", filename);
+ if (link(filename, link_name)) {
+ pr_perror("can't link files");
+ goto failed0;
+ }
+
+ fd[1] = open(link_name, O_RDONLY);
+ if (fd[1] < 0) {
+ pr_perror("can't open %s", link_name);
+ goto failed0;
+ }
+
+ if (fstat(fd[0], &fst) < 0) {
+ pr_perror("can't get file info %s before", filename);
+ goto failed;
+ }
+
+ if (fst.st_size != 0) {
+ pr_perror("%s file size eq %d", fst.st_size);
+ goto failed;
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink %s", filename);
+ goto failed;
+ }
+
+ if (unlink(link_name) < 0) {
+ pr_perror("can't unlink %s", link_name);
+ goto failed;
+ }
+
+ memset(buf, '0', sizeof(buf));
+ if (write(fd[0], buf, sizeof(buf)) != sizeof(buf)) {
+ pr_perror("can't write %s", filename);
+ goto failed;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (fstat(fd[0], &fst) < 0) {
+ pr_perror("can't get %s file info after", filename);
+ goto failed;
+ }
+
+ if (fstat(fd[1], &fst2) < 0) {
+ pr_perror("can't get %s file2 info after", link_name);
+ goto failed;
+ }
+
+ if ((fst.st_dev != fst2.st_dev) || (fst.st_ino != fst2.st_ino)) {
+ fail("files differ after restore\n");
+ goto failed;
+ }
+
+ if (fst.st_size != fsize) {
+ fail("(via fstat): file size changed to %d", fst.st_size);
+ goto failed;
+ }
+
+ fst.st_size = lseek(fd[0], 0, SEEK_END);
+ if (fst.st_size != fsize) {
+ fail("(via lseek): file size changed to %d", fst.st_size);
+ goto failed;
+ }
+
+ close(fd[0]);
+ close(fd[1]);
+
+ pass();
+ return 0;
+
+failed:
+ unlink(link_name);
+ close(fd[1]);
+failed0:
+ unlink(filename);
+ close(fd[0]);
+ return 1;
+}
diff --git a/test/zdtm/static/unlink_fstat03.c b/test/zdtm/static/unlink_fstat03.c
new file mode 100644
index 000000000..06268e3e6
--- /dev/null
+++ b/test/zdtm/static/unlink_fstat03.c
@@ -0,0 +1,108 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Open, link, unlink former, change size, migrate, check size";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+static char link_name[1024];
+
+int main(int argc, char ** argv)
+{
+ int fd;
+ size_t fsize=1000;
+ uint8_t buf[fsize];
+ struct stat fst, fst2;
+ struct statfs fsst;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ sprintf(link_name, "%s.link", filename);
+ if (link(filename, link_name)) {
+ pr_perror("can't link files");
+ goto failed0;
+ }
+
+ if (fstat(fd, &fst) < 0) {
+ pr_perror("can't get file info %s before", filename);
+ goto failed;
+ }
+
+ if (fst.st_size != 0) {
+ pr_perror("%s file size eq %d", fst.st_size);
+ goto failed;
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink %s", filename);
+ goto failed;
+ }
+
+ memset(buf, '0', sizeof(buf));
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ pr_perror("can't write %s", filename);
+ goto failed;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (statfs(link_name, &fsst) < 0) {
+ pr_perror("statfs(%s)", link_name);
+ goto failed;
+ }
+
+ if (fstat(fd, &fst2) < 0) {
+ pr_perror("can't get %s file info after", filename);
+ goto failed;
+ }
+
+ /* An NFS mount is restored with another st_dev */
+ if (fsst.f_type != NFS_SUPER_MAGIC && fst.st_dev != fst2.st_dev) {
+ fail("files differ after restore\n");
+ goto failed;
+ }
+
+ if (fst.st_ino != fst2.st_ino) {
+ fail("files differ after restore\n");
+ goto failed;
+ }
+
+ if (fst2.st_size != fsize) {
+ fail("(via fstat): file size changed to %d", fst.st_size);
+ goto failed;
+ }
+
+ fst2.st_size = lseek(fd, 0, SEEK_END);
+ if (fst2.st_size != fsize) {
+ fail("(via lseek): file size changed to %d", fst.st_size);
+ goto failed;
+ }
+
+ close(fd);
+
+ pass();
+ return 0;
+
+failed:
+ unlink(link_name);
+failed0:
+ unlink(filename);
+ close(fd);
+ return 1;
+}
diff --git a/test/zdtm/static/unlink_fstat03.desc b/test/zdtm/static/unlink_fstat03.desc
new file mode 100644
index 000000000..083b58305
--- /dev/null
+++ b/test/zdtm/static/unlink_fstat03.desc
@@ -0,0 +1 @@
+{'opts': '--link-remap', 'flags': 'nouser'}
diff --git a/test/zdtm/static/unlink_fstat03.opts b/test/zdtm/static/unlink_fstat03.opts
new file mode 100644
index 000000000..472294671
--- /dev/null
+++ b/test/zdtm/static/unlink_fstat03.opts
@@ -0,0 +1 @@
+--link-remap
diff --git a/test/zdtm/static/unlink_largefile.c b/test/zdtm/static/unlink_largefile.c
new file mode 100644
index 000000000..b1f8079bc
--- /dev/null
+++ b/test/zdtm/static/unlink_largefile.c
@@ -0,0 +1,59 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Checkpointing/restore of big (2Gb) unlinked files";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char ** argv)
+{
+ int fd;
+ char buf[1000000];
+ off64_t offset= 0x80002000ULL;
+ size_t count;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ if (lseek64(fd, offset, SEEK_SET) < 0) {
+ pr_perror("can't lseek %s, offset= %x", filename,
+ offset);
+ goto failed;
+ }
+
+ count = sizeof(buf);
+ memset(buf, 0, count);
+ if (write(fd, buf, count) != count) {
+ pr_perror("can't write %s", filename);
+ goto failed;
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink %s", filename);
+ goto failed;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ close(fd);
+
+ pass();
+ return 0;
+failed:
+ unlink(filename);
+ close(fd);
+ return 1;
+}
diff --git a/test/zdtm/static/unlink_largefile.desc b/test/zdtm/static/unlink_largefile.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/unlink_largefile.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/unlink_mmap00.c b/test/zdtm/static/unlink_mmap00.c
new file mode 100644
index 000000000..03509aabc
--- /dev/null
+++ b/test/zdtm/static/unlink_mmap00.c
@@ -0,0 +1,78 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test mmaped and unlinked files";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+static void touch_file_page(int fd, unsigned long off, char c)
+{
+ if (lseek(fd, off, SEEK_SET) != off) {
+ pr_perror("Lseek fail");
+ exit(1);
+ }
+
+ if (write(fd, &c, 1) != 1) {
+ pr_perror("Write fail");
+ exit(1);
+ }
+}
+
+int main(int argc, char ** argv)
+{
+ int fd;
+ char *mem_a, *mem_b;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open file");
+ exit(1);
+ }
+
+
+ touch_file_page(fd, 0, 'a');
+ touch_file_page(fd, PAGE_SIZE, 'b');
+ touch_file_page(fd, 2 * PAGE_SIZE - 1, 'c'); /* for aligned file */
+
+ /* map with different prots to create 2 regions */
+ mem_a = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0);
+ mem_b = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, PAGE_SIZE);
+ if (mem_a == MAP_FAILED || mem_b == MAP_FAILED) {
+ pr_perror("can't map file");
+ exit(1);
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink file");
+ exit(1);
+ }
+
+ close(fd);
+
+ test_daemon();
+ test_waitsig();
+
+ if (mem_a[0] != 'a')
+ fail("1st region fail");
+ else if (mem_b[0] != 'b' || mem_b[PAGE_SIZE - 1] != 'c')
+ fail("2nd regin fail");
+ else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/unlink_mmap00.desc b/test/zdtm/static/unlink_mmap00.desc
new file mode 100644
index 000000000..1fda48301
--- /dev/null
+++ b/test/zdtm/static/unlink_mmap00.desc
@@ -0,0 +1 @@
+{'flags': 'nouser'}
diff --git a/test/zdtm/static/unlink_mmap01.c b/test/zdtm/static/unlink_mmap01.c
new file mode 100644
index 000000000..66c1bc353
--- /dev/null
+++ b/test/zdtm/static/unlink_mmap01.c
@@ -0,0 +1,102 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test mmaped and unlinked files (2, with hard links)";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+static char linkname[4096];
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+static void touch_file_page(int fd, unsigned long off, char c)
+{
+ if (lseek(fd, off, SEEK_SET) != off) {
+ pr_perror("Lseek fail");
+ exit(1);
+ }
+
+ if (write(fd, &c, 1) != 1) {
+ pr_perror("Write fail");
+ exit(1);
+ }
+}
+
+int main(int argc, char ** argv)
+{
+ int fd;
+ char *mem_a, *mem_b;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open file");
+ exit(1);
+ }
+
+ touch_file_page(fd, 0, 'a');
+ touch_file_page(fd, PAGE_SIZE - 1, 'b');/* for aligned file */
+
+ mem_a = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0);
+ if (mem_a == MAP_FAILED) {
+ pr_perror("can't map file");
+ exit(1);
+ }
+
+ sprintf(linkname, "%s.lnk", filename);
+ if (link(filename, linkname)) {
+ pr_perror("can't link file");
+ exit(1);
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink file");
+ exit(1);
+ }
+
+ close(fd);
+
+ fd = open(linkname, O_RDWR);
+ if (fd < 0) {
+ pr_perror("can't open link");
+ exit(1);
+ }
+
+ mem_b = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0);
+ if (mem_b == MAP_FAILED) {
+ pr_perror("can't map link");
+ exit(1);
+ }
+
+ if (unlink(linkname) < 0) {
+ pr_perror("can't unlink link");
+ exit(1);
+ }
+
+ close(fd);
+
+ test_daemon();
+ test_waitsig();
+
+ if (mem_a[0] != 'a' || mem_a[PAGE_SIZE - 1] != 'b')
+ fail("1st region fail");
+ else if (mem_b[0] != 'a' || mem_b[PAGE_SIZE - 1] != 'b')
+ fail("2nd regin fail");
+ else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/unlink_mmap01.desc b/test/zdtm/static/unlink_mmap01.desc
new file mode 100644
index 000000000..1fda48301
--- /dev/null
+++ b/test/zdtm/static/unlink_mmap01.desc
@@ -0,0 +1 @@
+{'flags': 'nouser'}
diff --git a/test/zdtm/static/unlink_mmap02.c b/test/zdtm/static/unlink_mmap02.c
new file mode 100644
index 000000000..85d6b3887
--- /dev/null
+++ b/test/zdtm/static/unlink_mmap02.c
@@ -0,0 +1,77 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Test mmaped, opened and unlinked files";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+static void touch_file_page(int fd, unsigned long off, char c)
+{
+ if (lseek(fd, off, SEEK_SET) != off) {
+ pr_perror("Lseek fail");
+ exit(1);
+ }
+
+ if (write(fd, &c, 1) != 1) {
+ pr_perror("Write fail");
+ exit(1);
+ }
+}
+
+int main(int argc, char ** argv)
+{
+ int fd;
+ char *mem_a, *mem_b;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open file");
+ exit(1);
+ }
+
+
+ touch_file_page(fd, 2 * PAGE_SIZE - 1, 'c'); /* for aligned file */
+
+ /* map with different prots to create 2 regions */
+ mem_a = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0);
+ mem_b = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, PAGE_SIZE);
+ if (mem_a == MAP_FAILED || mem_b == MAP_FAILED) {
+ pr_perror("can't map file");
+ exit(1);
+ }
+
+ if (unlink(filename) < 0) {
+ pr_perror("can't unlink file");
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ touch_file_page(fd, 0, 'a');
+ touch_file_page(fd, PAGE_SIZE, 'b');
+
+ if (mem_a[0] != 'a')
+ fail("1st region fail");
+ else if (mem_b[0] != 'b' || mem_b[PAGE_SIZE - 1] != 'c')
+ fail("2nd regin fail");
+ else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/unlink_mmap02.desc b/test/zdtm/static/unlink_mmap02.desc
new file mode 100644
index 000000000..1fda48301
--- /dev/null
+++ b/test/zdtm/static/unlink_mmap02.desc
@@ -0,0 +1 @@
+{'flags': 'nouser'}
diff --git a/test/zdtm/static/unlink_regular00.c b/test/zdtm/static/unlink_regular00.c
new file mode 100644
index 000000000..6e97df2d1
--- /dev/null
+++ b/test/zdtm/static/unlink_regular00.c
@@ -0,0 +1,109 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Checkpointing/restore of unlinked file inside unlinked directory";
+const char *test_author = "Kirill Tkhai <ktkhai@virtuozzo.com>";
+
+char *dirname;
+TEST_OPTION(dirname, string, "directory name", 1);
+
+#define SUBDIR "subdir"
+#define FNAME "testfile"
+#define MSG "Hello!!!111"
+
+int main(int argc, char ** argv)
+{
+ char subdir[PATH_MAX], fname[PATH_MAX], lname[PATH_MAX];
+ char buf[sizeof(MSG) + 1];
+ int fd, ret = -1;
+
+ test_init(argc, argv);
+
+ memset(buf, 0, sizeof(buf));
+
+ if (mkdir(dirname, 0777)) {
+ fail("can't create %s", dirname);
+ exit(1);
+ }
+
+ if (mount("none", dirname, "tmpfs", 0, "") < 0) {
+ fail("can't mount tmpfs to %s", dirname);
+ goto rm_topdir;
+ }
+
+ sprintf(subdir, "%s/" SUBDIR, dirname);
+
+ if (mkdir(subdir, 0777)) {
+ fail("can't create %s", subdir);
+ goto umount;
+ }
+
+ sprintf(fname, "%s/" SUBDIR "/" FNAME, dirname);
+ sprintf(lname, "%s/" FNAME, dirname);
+
+ fd = open(fname, O_RDWR | O_CREAT, 0644);
+ if (fd < 0) {
+ fail("can't open %s", fname);
+ rmdir(subdir);
+ goto umount;
+ }
+
+ if (link(fname, lname) < 0) {
+ fail("can't link %s to %s", fname, lname);
+ unlink(fname);
+ rmdir(subdir);
+ goto umount;
+ }
+
+ if (unlink(fname) || rmdir(subdir)) {
+ fail("can't unlink %s or %s", fname, subdir);
+ goto close_file;
+ }
+
+ if (write(fd, MSG, sizeof(MSG)) != sizeof(MSG)) {
+ fail("can't write %s", fname);
+ goto close_file;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ fail("can't lseek %s", fname);
+ goto close_file;
+ }
+
+ if (read(fd, buf, sizeof(MSG)) != sizeof(MSG)) {
+ fail("can't read %s", fname);
+ goto close_file;
+ }
+
+ if (strcmp(buf, MSG)) {
+ fail("content differs: %s, %s, sizeof=%d", buf, MSG, sizeof(MSG));
+ goto close_file;
+ }
+
+ ret = 0;
+ pass();
+
+close_file:
+ close(fd);
+ unlink(lname);
+umount:
+ if (umount(dirname) < 0)
+ pr_err("Can't umount\n");
+rm_topdir:
+ if (rmdir(dirname) < 0)
+ pr_err("Can't rmdir()\n");
+
+ return ret;
+}
diff --git a/test/zdtm/static/unlink_regular00.desc b/test/zdtm/static/unlink_regular00.desc
new file mode 100644
index 000000000..53aea561b
--- /dev/null
+++ b/test/zdtm/static/unlink_regular00.desc
@@ -0,0 +1 @@
+{'flavor': 'ns', 'flags': 'suid noauto'}
diff --git a/test/zdtm/static/uptime_grow.c b/test/zdtm/static/uptime_grow.c
new file mode 100644
index 000000000..6d99509ca
--- /dev/null
+++ b/test/zdtm/static/uptime_grow.c
@@ -0,0 +1,51 @@
+#include "zdtmtst.h"
+
+const char *test_doc = "test to ensure that monotonic clock doesn't decrease";
+const char *test_author = "Evgeny Antysev <eantyshev@parallels.com>";
+
+#include <time.h>
+#include <stdlib.h>
+
+# define tv_ge(a, b) \
+ (((a)->tv_sec == (b)->tv_sec) ? \
+ ((a)->tv_nsec >= (b)->tv_nsec) : \
+ ((a)->tv_sec > (b)->tv_sec))
+
+int main(int argc, char **argv)
+{
+ struct timespec tm_old, tm, ts;
+ double diff_nsec;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+
+ test_init(argc, argv);
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tm_old)) {
+ pr_perror("clock_gettime failed");
+ exit(1);
+ }
+
+ test_daemon();
+
+ while (test_go()) {
+ if (clock_gettime(CLOCK_MONOTONIC, &tm)) {
+ pr_perror("clock_gettime failed");
+ exit(1);
+ }
+ if (!tv_ge(&tm, &tm_old)) {
+ diff_nsec = (tm_old.tv_sec - tm.tv_sec) * 1.0E9 +\
+ (tm_old.tv_nsec - tm.tv_nsec);
+ fail("clock step backward for %e nsec\n", diff_nsec);
+ exit(1);
+ }
+ tm_old = tm;
+ /*
+ Kernel can't suspend container by design if calls
+ clock_gettime() in a loop, so we need to sleep
+ between clock_gettime().
+ */
+ nanosleep(&ts, NULL);
+ }
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/uptime_grow.desc b/test/zdtm/static/uptime_grow.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/uptime_grow.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/utsname.c b/test/zdtm/static/utsname.c
new file mode 100644
index 000000000..5e1e0cb88
--- /dev/null
+++ b/test/zdtm/static/utsname.c
@@ -0,0 +1,46 @@
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that utsname hasn't changed";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+static struct utsname after;
+
+#define ZDTM_NODE "zdtm.nodename.ru"
+#define ZDTM_DOMAIN "zdtm.nodename.ru"
+
+int main(int argc, char **argv)
+{
+ test_init(argc, argv);
+
+ if (sethostname(ZDTM_NODE, sizeof(ZDTM_NODE))) {
+ pr_perror("Unable to set hostname");
+ return 1;
+ }
+
+ if (setdomainname(ZDTM_DOMAIN, sizeof(ZDTM_DOMAIN))) {
+ pr_perror("Unable to set domainname");
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ uname(&after);
+
+ if (strcmp(ZDTM_NODE, after.nodename)) {
+ fail("Nodename doesn't match");
+ return 1;
+ }
+ if (strcmp(ZDTM_DOMAIN, after.__domainname)) {
+ fail("Domainname doesn't match");
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/utsname.desc b/test/zdtm/static/utsname.desc
new file mode 100644
index 000000000..7657ba45c
--- /dev/null
+++ b/test/zdtm/static/utsname.desc
@@ -0,0 +1 @@
+{'flavor': 'ns uns', 'flags': 'suid'}
diff --git a/test/zdtm/static/vdso00.c b/test/zdtm/static/vdso00.c
new file mode 100644
index 000000000..8ac4ccad9
--- /dev/null
+++ b/test/zdtm/static/vdso00.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include "zdtmtst.h"
+
+const char *test_doc = "Check if we can use vDSO after restore\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org";
+
+int main(int argc, char *argv[])
+{
+ struct timeval tv;
+ struct timezone tz;
+
+ test_init(argc, argv);
+ test_msg("%s pid %d\n", argv[0], getpid());
+
+ gettimeofday(&tv, &tz);
+ test_msg("%d time: %10li\n", getpid(), tv.tv_sec);
+
+ test_daemon();
+ test_waitsig();
+
+ /* this call will fail if vDSO is corrupted */
+ gettimeofday(&tv, &tz);
+ test_msg("%d time: %10li\n", getpid(), tv.tv_sec);
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/vdso01.c b/test/zdtm/static/vdso01.c
new file mode 100644
index 000000000..8db396ed8
--- /dev/null
+++ b/test/zdtm/static/vdso01.c
@@ -0,0 +1,420 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <elf.h>
+#include <time.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check if we can use vDSO using direct vDSO calls\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org";
+
+typedef int (__vdso_clock_gettime_t)(clockid_t clock, struct timespec *ts);
+typedef long (__vdso_getcpu_t)(unsigned *cpu, unsigned *node, void *unused);
+typedef int (__vdso_gettimeofday_t)(struct timeval *tv, struct timezone *tz);
+typedef time_t (__vdso_time_t)(time_t *t);
+
+#define TIME_DELTA_SEC (3)
+
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
+
+#define VDSO_BAD_ADDR (-1ul)
+
+struct vdso_symbol {
+ char name[32];
+ unsigned long offset;
+};
+
+#define VDSO_SYMBOL_INIT { .offset = VDSO_BAD_ADDR, }
+
+/* Check if symbol present in symtable */
+static inline bool vdso_symbol_empty(struct vdso_symbol *s)
+{
+ return s->offset == VDSO_BAD_ADDR && s->name[0] == '\0';
+}
+
+enum {
+ VDSO_SYMBOL_CLOCK_GETTIME,
+ VDSO_SYMBOL_GETCPU,
+ VDSO_SYMBOL_GETTIMEOFDAY,
+ VDSO_SYMBOL_TIME,
+
+ VDSO_SYMBOL_MAX
+};
+
+#define VDSO_SYMBOL_CLOCK_GETTIME_NAME "__vdso_clock_gettime"
+#define VDSO_SYMBOL_GETCPU_NAME "__vdso_getcpu"
+#define VDSO_SYMBOL_GETTIMEOFDAY_NAME "__vdso_gettimeofday"
+#define VDSO_SYMBOL_TIME_NAME "__vdso_time"
+
+struct vdso_symtable {
+ unsigned long vma_start;
+ unsigned long vma_end;
+ struct vdso_symbol symbols[VDSO_SYMBOL_MAX];
+};
+
+#define VDSO_SYMTABLE_INIT \
+ { \
+ .vma_start = VDSO_BAD_ADDR, \
+ .vma_end = VDSO_BAD_ADDR, \
+ .symbols = { \
+ [0 ... VDSO_SYMBOL_MAX - 1] = \
+ (struct vdso_symbol)VDSO_SYMBOL_INIT, \
+ }, \
+ }
+
+static bool __ptr_oob(void *ptr, void *start, size_t size)
+{
+ void *end = (void *)((unsigned long)start + size);
+ return ptr > end || ptr < start;
+}
+
+static unsigned long elf_hash(const unsigned char *name)
+{
+ unsigned long h = 0, g;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ g = h & 0xf0000000ul;
+ if (g)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+static int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t)
+{
+ Elf64_Phdr *dynamic = NULL, *load = NULL;
+ Elf64_Ehdr *ehdr = (void *)mem;
+ Elf64_Dyn *dyn_strtab = NULL;
+ Elf64_Dyn *dyn_symtab = NULL;
+ Elf64_Dyn *dyn_strsz = NULL;
+ Elf64_Dyn *dyn_syment = NULL;
+ Elf64_Dyn *dyn_hash = NULL;
+ Elf64_Word *hash = NULL;
+ Elf64_Phdr *phdr;
+ Elf64_Dyn *d;
+
+ Elf64_Word *bucket, *chain;
+ Elf64_Word nbucket, nchain;
+
+ /*
+ * See Elf specification for this magic values.
+ */
+ const char elf_ident[] = {
+ 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ const char *vdso_symbols[VDSO_SYMBOL_MAX] = {
+ [VDSO_SYMBOL_CLOCK_GETTIME] = VDSO_SYMBOL_CLOCK_GETTIME_NAME,
+ [VDSO_SYMBOL_GETCPU] = VDSO_SYMBOL_GETCPU_NAME,
+ [VDSO_SYMBOL_GETTIMEOFDAY] = VDSO_SYMBOL_GETTIMEOFDAY_NAME,
+ [VDSO_SYMBOL_TIME] = VDSO_SYMBOL_TIME_NAME,
+ };
+
+ char *dynsymbol_names;
+ unsigned int i, j, k;
+
+ BUILD_BUG_ON(sizeof(elf_ident) != sizeof(ehdr->e_ident));
+
+ test_msg("Parsing at %lx %lx\n", (long)mem, (long)mem + (long)size);
+
+ /*
+ * Make sure it's a file we support.
+ */
+ if (memcmp(ehdr->e_ident, elf_ident, sizeof(elf_ident))) {
+ pr_perror("Elf header magic mismatch");
+ return -EINVAL;
+ }
+
+ /*
+ * We need PT_LOAD and PT_DYNAMIC here. Each once.
+ */
+ phdr = (void *)&mem[ehdr->e_phoff];
+ for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+ if (__ptr_oob(phdr, mem, size))
+ goto err_oob;
+ switch (phdr->p_type) {
+ case PT_DYNAMIC:
+ if (dynamic) {
+ pr_perror("Second PT_DYNAMIC header");
+ return -EINVAL;
+ }
+ dynamic = phdr;
+ break;
+ case PT_LOAD:
+ if (load) {
+ pr_perror("Second PT_LOAD header");
+ return -EINVAL;
+ }
+ load = phdr;
+ break;
+ }
+ }
+
+ if (!load || !dynamic) {
+ pr_perror("One of obligated program headers is missed");
+ return -EINVAL;
+ }
+
+ test_msg("PT_LOAD p_vaddr: %lx\n", (unsigned long)load->p_vaddr);
+
+ /*
+ * Dynamic section tags should provide us the rest of information
+ * needed. Note that we're interested in a small set of tags.
+ */
+ d = (void *)&mem[dynamic->p_offset];
+ for (i = 0; i < dynamic->p_filesz / sizeof(*d); i++, d++) {
+ if (__ptr_oob(d, mem, size))
+ goto err_oob;
+
+ if (d->d_tag == DT_NULL) {
+ break;
+ } else if (d->d_tag == DT_STRTAB) {
+ dyn_strtab = d;
+ } else if (d->d_tag == DT_SYMTAB) {
+ dyn_symtab = d;
+ } else if (d->d_tag == DT_STRSZ) {
+ dyn_strsz = d;
+ } else if (d->d_tag == DT_SYMENT) {
+ dyn_syment = d;
+ } else if (d->d_tag == DT_HASH) {
+ dyn_hash = d;
+ }
+ }
+
+ if (!dyn_strtab || !dyn_symtab || !dyn_strsz || !dyn_syment || !dyn_hash) {
+ pr_perror("Not all dynamic entries are present");
+ return -EINVAL;
+ }
+
+ dynsymbol_names = &mem[dyn_strtab->d_un.d_val - load->p_vaddr];
+ if (__ptr_oob(dynsymbol_names, mem, size))
+ goto err_oob;
+
+ hash = (void *)&mem[(unsigned long)dyn_hash->d_un.d_ptr - (unsigned long)load->p_vaddr];
+ if (__ptr_oob(hash, mem, size))
+ goto err_oob;
+
+ nbucket = hash[0];
+ nchain = hash[1];
+ bucket = &hash[2];
+ chain = &hash[nbucket + 2];
+
+ test_msg("nbucket %lu nchain %lu bucket %p chain %p\n",
+ (long)nbucket, (long)nchain, bucket, chain);
+
+ for (i = 0; i < ARRAY_SIZE(vdso_symbols); i++) {
+ k = elf_hash((const unsigned char *)vdso_symbols[i]);
+
+ for (j = bucket[k % nbucket]; j < nchain && chain[j] != STN_UNDEF; j = chain[j]) {
+ Elf64_Sym *sym = (void *)&mem[dyn_symtab->d_un.d_ptr - load->p_vaddr];
+ char *name;
+
+ sym = &sym[j];
+ if (__ptr_oob(sym, mem, size))
+ continue;
+
+ if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC &&
+ ELF64_ST_BIND(sym->st_info) != STB_GLOBAL)
+ continue;
+
+ name = &dynsymbol_names[sym->st_name];
+ if (__ptr_oob(name, mem, size))
+ continue;
+
+ if (strcmp(name, vdso_symbols[i]))
+ continue;
+
+ memcpy(t->symbols[i].name, name, sizeof(t->symbols[i].name));
+ t->symbols[i].offset = (unsigned long)sym->st_value - load->p_vaddr;
+ test_msg("symbol %s offset %lx\n", t->symbols[i].name, t->symbols[i].offset);
+ break;
+ }
+ }
+
+ return 0;
+
+err_oob:
+ pr_perror("Corrupted Elf data");
+ return -EFAULT;
+}
+
+static int vdso_fill_self_symtable(struct vdso_symtable *s)
+{
+ char buf[512];
+ int ret = -1;
+ FILE *maps;
+
+ *s = (struct vdso_symtable)VDSO_SYMTABLE_INIT;
+
+ maps = fopen("/proc/self/maps", "r");
+ if (!maps) {
+ pr_perror("Can't open self-vma");
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), maps)) {
+ unsigned long start, end;
+
+ if (!strstr(buf, "[vdso]"))
+ continue;
+
+ ret = sscanf(buf, "%lx-%lx", &start, &end);
+ if (ret != 2) {
+ ret = -1;
+ pr_perror("Can't find vDSO bounds");
+ goto err;
+ }
+
+ s->vma_start = start;
+ s->vma_end = end;
+
+ ret = vdso_fill_symtable((void *)start, end - start, s);
+ break;
+ }
+
+ test_msg("[vdso] %lx-%lx\n", s->vma_start, s->vma_end);
+err:
+ fclose(maps);
+ return ret;
+}
+
+static int vdso_clock_gettime_handler(void *func)
+{
+ __vdso_clock_gettime_t *vdso_clock_gettime = func;
+ struct timespec ts1, ts2;
+
+ clock_gettime(CLOCK_REALTIME, &ts1);
+ vdso_clock_gettime(CLOCK_REALTIME, &ts2);
+
+ test_msg("clock_gettime: tv_sec %li vdso_clock_gettime: tv_sec %li\n",
+ ts1.tv_sec, ts2.tv_sec);
+
+ if (abs(ts1.tv_sec - ts2.tv_sec) > TIME_DELTA_SEC) {
+ pr_perror("Delta is too big");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int vdso_getcpu_handler(void *func)
+{
+ __vdso_getcpu_t *vdso_getcpu = func;
+ unsigned cpu, node;
+
+ vdso_getcpu(&cpu, &node, NULL);
+ test_msg("vdso_getcpu: cpu %d node %d\n", cpu, node);
+
+ return 0;
+}
+
+static int vdso_gettimeofday_handler(void *func)
+{
+ __vdso_gettimeofday_t *vdso_gettimeofday = func;
+ struct timeval tv1, tv2;
+ struct timezone tz;
+
+ gettimeofday(&tv1, &tz);
+ vdso_gettimeofday(&tv2, &tz);
+
+ test_msg("gettimeofday: tv_sec %li vdso_gettimeofday: tv_sec %li\n",
+ tv1.tv_sec, tv2.tv_sec);
+
+ if (abs(tv1.tv_sec - tv2.tv_sec) > TIME_DELTA_SEC) {
+ pr_perror("Delta is too big");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int vdso_time_handler(void *func)
+{
+ __vdso_time_t *vdso_time = func;
+ time_t t1, t2;
+
+ t1 = time(NULL);
+ t2 = vdso_time(NULL);
+
+ test_msg("time: %li vdso_time: %li\n", (long)t1, (long)t1);
+
+ if (abs(t1 - t2) > TIME_DELTA_SEC) {
+ pr_perror("Delta is too big");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ typedef int (handler_t)(void *func);
+
+ struct vdso_symtable symtable;
+ size_t i;
+
+ handler_t *handlers[VDSO_SYMBOL_MAX] = {
+ [VDSO_SYMBOL_CLOCK_GETTIME] = vdso_clock_gettime_handler,
+ [VDSO_SYMBOL_GETCPU] = vdso_getcpu_handler,
+ [VDSO_SYMBOL_GETTIMEOFDAY] = vdso_gettimeofday_handler,
+ [VDSO_SYMBOL_TIME] = vdso_time_handler,
+ };
+
+ test_init(argc, argv);
+
+ if (vdso_fill_self_symtable(&symtable)) {
+ pr_perror("Faied to parse vdso");
+ return -1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(symtable.symbols); i++) {
+ struct vdso_symbol *s = &symtable.symbols[i];
+ handler_t *func;
+
+ if (vdso_symbol_empty(s) || i > ARRAY_SIZE(handlers))
+ continue;
+ func = handlers[i];
+
+ if (func((void *)(s->offset + symtable.vma_start))) {
+ pr_perror("Handler error");
+ return -1;
+ }
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ /*
+ * After restore the vDSO must remain in old place.
+ */
+ for (i = 0; i < ARRAY_SIZE(symtable.symbols); i++) {
+ struct vdso_symbol *s = &symtable.symbols[i];
+ handler_t *func;
+
+ if (vdso_symbol_empty(s) || i > ARRAY_SIZE(handlers))
+ continue;
+ func = handlers[i];
+
+ if (func((void *)(s->offset + symtable.vma_start))) {
+ fail("Handler error");
+ return -1;
+ }
+ }
+
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/vdso01.desc b/test/zdtm/static/vdso01.desc
new file mode 100644
index 000000000..d2f501de2
--- /dev/null
+++ b/test/zdtm/static/vdso01.desc
@@ -0,0 +1 @@
+{'arch': 'x86_64'}
diff --git a/test/zdtm/static/vfork00.c b/test/zdtm/static/vfork00.c
new file mode 100644
index 000000000..d61bb35f1
--- /dev/null
+++ b/test/zdtm/static/vfork00.c
@@ -0,0 +1,80 @@
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Block migration by a pending (non-exec()-ed) vfork()";
+const char *test_author = "Pavel Emelianov <xemul@sw.ru>";
+
+int main(int argc, char ** argv)
+{
+ int ret = 0;
+ pid_t pid;
+
+ test_init(argc, argv);
+
+ /* vfork() won't let us control the test, so fork() first, and vfork()
+ * in the child */
+ pid = fork();
+ if (pid < 0) {
+ pr_err("fork failed: %m");
+ exit(1);
+ }
+
+ if (pid == 0) {
+ int ret2;
+
+ pid = vfork();
+ if (pid < 0)
+ ret = errno;
+
+ /* wait for signal in _both_ branches */
+ test_waitsig();
+
+ /* vforked guy shouldn't return, hence we exec() */
+ if (pid == 0)
+ execlp("/bin/true", "true", NULL);
+
+ if (wait(&ret2) != pid)
+ ret = errno;
+
+ _exit(ret);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ /* signal the whole process group, because our child is suspended until
+ * the grand-child has exec()-ed, but we don't know the pid of the
+ * latter */
+ if (kill(0, SIGTERM)) {
+ fail("terminating the children failed: %m");
+ exit(1);
+ }
+
+ if (wait(&ret) != pid) {
+ fail("wait() returned wrong pid: %m");
+ exit(1);
+ }
+
+ if (WIFEXITED(ret)) {
+ ret = WEXITSTATUS(ret);
+ if (ret) {
+ fail("child exited with nonzero code %d (%s)", ret, strerror(ret));
+ exit(1);
+ }
+ }
+ if (WIFSIGNALED(ret)) {
+ fail("child exited on unexpected signal %d", WTERMSIG(ret));
+ exit(1);
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/vfork00.desc b/test/zdtm/static/vfork00.desc
new file mode 100644
index 000000000..95c58b401
--- /dev/null
+++ b/test/zdtm/static/vfork00.desc
@@ -0,0 +1 @@
+{'flags': 'noauto'}
diff --git a/test/zdtm/static/vsx.c b/test/zdtm/static/vsx.c
new file mode 100644
index 000000000..33151cab7
--- /dev/null
+++ b/test/zdtm/static/vsx.c
@@ -0,0 +1,400 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+
+#include "zdtmtst.h"
+
+/*
+ * This test is specific to PowerPC
+ */
+#ifndef _ARCH_PPC64
+int main(int argc, char *argv[])
+{
+ test_init(argc, argv);
+ skip("Unsupported arch");
+ return 0;
+}
+
+#else
+
+#include <sys/auxv.h>
+#include <asm/cputable.h>
+
+/*
+ * This test verifies that data stored in the VSX regsiters are still there
+ * once the restart is done.
+ *
+ * The test is filling the registers with dedicated values and then check
+ * their content.
+ */
+
+const char *test_doc = "Test if data in vector registers do survive the c/r";
+const char *test_author = "Laurent Dufour <ldufour@linux.vnet.ibm.com>";
+
+int is_test_doable(void)
+{
+ unsigned long val;
+
+ val = getauxval(AT_HWCAP);
+#define CHECK_FEATURE(f) do { \
+ if (!(val & f)) { \
+ test_msg("CPU feature " #f " is missing\n"); \
+ return 0; \
+ } \
+ } while(0)
+
+ CHECK_FEATURE(PPC_FEATURE_64);
+ CHECK_FEATURE(PPC_FEATURE_HAS_ALTIVEC);
+ CHECK_FEATURE(PPC_FEATURE_HAS_VSX);
+ return 1;
+}
+
+void fill_vsx(uint64_t *pt)
+{
+ asm volatile(
+ "lis 3, 0 \n"
+
+ "lxvd2x 0, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 1, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 2, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 3, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 4, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 5, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 6, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 7, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 8, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 9, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "lxvd2x 10, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 11, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 12, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 13, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 14, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 15, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 16, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 17, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 18, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 19, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "lxvd2x 20, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 21, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 22, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 23, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 24, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 25, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 26, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 27, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 28, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 29, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "lxvd2x 30, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 31, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 32, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 33, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 34, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 35, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 36, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 37, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 38, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 39, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "lxvd2x 40, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 41, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 42, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 43, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 44, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 45, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 46, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 47, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 48, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 49, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "lxvd2x 50, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 51, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 52, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 53, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 54, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 55, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 56, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 57, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 58, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 59, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "lxvd2x 60, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 61, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 62, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "lxvd2x 63, 3, %0 \n"
+ : /* no output */
+ : "r" (pt)
+ : "3");
+}
+
+void read_vsx(uint64_t *pt)
+{
+ asm volatile(
+ "lis 3, 0 \n"
+
+ "stxvd2x 0, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 1, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 2, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 3, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 4, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 5, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 6, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 7, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 8, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 9, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "stxvd2x 10, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 11, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 12, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 13, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 14, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 15, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 16, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 17, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 18, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 19, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "stxvd2x 20, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 21, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 22, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 23, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 24, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 25, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 26, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 27, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 28, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 29, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "stxvd2x 30, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 31, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 32, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 33, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 34, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 35, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 36, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 37, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 38, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 39, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "stxvd2x 40, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 41, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 42, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 43, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 44, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 45, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 46, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 47, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 48, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 49, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "stxvd2x 50, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 51, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 52, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 53, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 54, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 55, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 56, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 57, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 58, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 59, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+
+ "stxvd2x 60, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 61, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 62, 3, %0 \n"
+ "addi 3, 3, 16 \n" /* move to the next qword */
+ "stxvd2x 63, 3, %0 \n"
+
+ : /* no output */
+ : "r" (pt)
+ : "3");
+}
+
+int main(int argc, char *argv[])
+{
+ /* A random buffer of 1024 bytes (64 * 128bit registers to fill) */
+ static const char ibuffer[128/8*64]=
+ "sahwoleiGun9loosliz0Aech9aiph5eiIengei7Ogh8zu7ye"
+ "Aeshie6vai0thaehool1ooK6ayaj3Neitahn8yeeh5ahfuiT"
+ "uCeir1bife4ieceema8choo2Wengaec1seDaidohteipa4ai"
+ "aequee7AiriejaeJar1giak8Gei2uathloh5uemaeG6EiSoo"
+ "PhaenaethoPhej8nEecheegeihosho8Zohroo8ea6Juuheif"
+ "nu2Hahvai1tuf0Zeeeveephu2EitaexiVaekieboac7Nushu"
+ "aeTh6Quoo3iozeisaudaGheed0aPah2Schoog0eiChaeN5su"
+ "xoo1phoic1mahXohSai1thoogo0oesooeaxai7eBahHahMue"
+ "quiloh2ooPahpiujeithae0Dau0shuwicobinaaYooj0ajiw"
+ "iiheeS4awoh3haevlaiGe8phaev3eiluaChaF6ieng4aith4"
+ "aif3TooYo1aigoomZiuhai8eesoo4maiLahr3PoM8Eir5ooz"
+ "Iequ9ahre4Op4bahaiso6ohnah8Shokimooch1Oafahf5aih"
+ "xohphee1pi5Iecaiaigh7Eisah2uew5acie7wi6Zo0Eelah9"
+ "woi8QueerohfeiThaBoh5jaic3peiPohAhng0bu5shoop7ca"
+ "Qui5kodaika8quioahmohreeVe8loquaeeLi5ze3oceiHa0l"
+ "roh8Ooxae7uish9ioog7ieS3aibeo2thOosiuvaiS5lohp4U"
+ "emieG0eit6Bien8EzaiwiTh3geighaexshee8eHiec1TooH2"
+ "Eeceacai0inaejieboo8NeishieweiraHooj9apeecooy0th"
+ "daThei6aexeisahdsei3keik0diPheejchais6ezo0iep5Ae"
+ "Wiqu6aepeing4ba8diek3aev9waYooveAebai9eef6Iex6vo"
+ "Quee9MeitahMighoHuo3seveeMoh3ohtoxaib6ootaiF5EeT"
+ "Ohb9eijoonoh6ich";
+ char obuffer[128/8*64];
+ int do_test;
+
+ test_init(argc, argv);
+
+ do_test = is_test_doable();
+
+ if (do_test) {
+ memset(obuffer, 0xFF, sizeof(obuffer));
+ fill_vsx((uint64_t *)ibuffer);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (do_test) {
+ read_vsx((uint64_t *)obuffer);
+
+ if (!memcmp(ibuffer, obuffer, sizeof(ibuffer)))
+ pass();
+ else {
+ test_msg("Data mismatch\n");
+ fail();
+ }
+ }
+ else {
+ test_msg("The CPU is missing some features.\n");
+ fail();
+ }
+
+ return 0;
+}
+
+#endif /* _ARCH_PPC64 */
diff --git a/test/zdtm/static/vsx.desc b/test/zdtm/static/vsx.desc
new file mode 100644
index 000000000..2ba6eda79
--- /dev/null
+++ b/test/zdtm/static/vsx.desc
@@ -0,0 +1 @@
+{'arch': 'ppc64le'}
diff --git a/test/zdtm/static/vt.c b/test/zdtm/static/vt.c
new file mode 100644
index 000000000..1726a6f32
--- /dev/null
+++ b/test/zdtm/static/vt.c
@@ -0,0 +1,60 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check c/r of a virtual terminal";
+const char *test_author = "Ruslan Kuprieiev <kupruser@gmail.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char **argv)
+{
+ struct stat st1, st2;
+ int fd;
+
+ test_init(argc, argv);
+
+ if (mknod(filename, S_IFCHR | S_IRUSR | S_IWUSR, makedev(4, 5))) {
+ pr_perror("Can't create virtual terminal %s", filename);
+ return 1;
+ }
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("Open virtual terminal %s failed", filename);
+ return 1;
+ }
+
+ if (fstat(fd, &st1)) {
+ pr_perror("Can't stat %s virtual terminal", filename);
+ return 1;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (fstat(fd, &st2)) {
+ pr_perror("Can't stat %s virtual terminal", filename);
+ return 1;
+ }
+
+ if (st1.st_rdev != st2.st_rdev) {
+ fail("Virtual terminal rdev mismatch %x != %x on %s",
+ (int)st1.st_rdev, (int)st2.st_rdev,
+ filename);
+ return 1;
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/vt.desc b/test/zdtm/static/vt.desc
new file mode 100644
index 000000000..d969725f6
--- /dev/null
+++ b/test/zdtm/static/vt.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns', 'flags': 'suid'}
diff --git a/test/zdtm/static/wait00.c b/test/zdtm/static/wait00.c
new file mode 100644
index 000000000..f16505c0f
--- /dev/null
+++ b/test/zdtm/static/wait00.c
@@ -0,0 +1,61 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "See if we can wait() for a child after migration";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+int main(int argc, char ** argv)
+{
+ int ret;
+ pid_t pid;
+
+ test_init(argc, argv);
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("fork failed");
+ exit(1);
+ }
+
+ if (pid == 0) {
+ test_waitsig();
+ _exit(0);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (kill(pid, SIGTERM)) {
+ fail("terminating the child failed: %m\n");
+ goto out;
+ }
+
+ if (wait(&ret) != pid) {
+ fail("wait() returned wrong pid: %m\n");
+ goto out;
+ }
+
+ if (WIFEXITED(ret)) {
+ ret = WEXITSTATUS(ret);
+ if (ret) {
+ fail("child exited with nonzero code %d (%s)\n", ret, strerror(ret));
+ goto out;
+ }
+ }
+ if (WIFSIGNALED(ret)) {
+ fail("child exited on unexpected signal %d\n", WTERMSIG(ret));
+ goto out;
+ }
+
+ pass();
+
+out:
+ return 0;
+}
diff --git a/test/zdtm/static/write_read00.c b/test/zdtm/static/write_read00.c
new file mode 100644
index 000000000..1648e3546
--- /dev/null
+++ b/test/zdtm/static/write_read00.c
@@ -0,0 +1,61 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Write file before migration, read after";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char ** argv)
+{
+ int fd;
+ uint32_t crc;
+ uint8_t buf[1000000];
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ pr_perror("can't write %s", filename);
+ exit(1);
+ }
+
+ close(fd);
+
+ test_daemon();
+ test_waitsig();
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ fail("can't open %s: %m\n", filename);
+ exit(1);
+ }
+
+ if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ fail("can't read %s: %m\n", filename);
+ goto out;
+ }
+
+ crc = ~0;
+ if (datachk(buf, sizeof(buf), &crc)) {
+ fail("CRC mismatch\n");
+ goto out;
+ }
+
+ pass();
+out:
+ unlink(filename);
+ return 0;
+}
diff --git a/test/zdtm/static/write_read01.c b/test/zdtm/static/write_read01.c
new file mode 100644
index 000000000..0d4176742
--- /dev/null
+++ b/test/zdtm/static/write_read01.c
@@ -0,0 +1,69 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Write and half way read file before migration, complete after";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char ** argv)
+{
+ int fd;
+ int len;
+ uint32_t crc = ~0;
+ uint8_t buf[1000000];
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ pr_perror("can't write %s", filename);
+ exit(1);
+ }
+
+ close(fd);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ len = sizeof(buf) / 2;
+ if (read(fd, buf, len) != len) {
+ pr_perror("can't read %s", filename);
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ /* recover reading */
+ if (read(fd, buf + len, sizeof(buf) - len) != (sizeof(buf) - len)) {
+ fail("can't read %s: %m\n", filename);
+ goto out;
+ }
+
+ crc = ~0;
+ if (datachk(buf, sizeof(buf), &crc)) {
+ fail("CRC mismatch\n");
+ goto out;
+ }
+
+ pass();
+out:
+ unlink(filename);
+ return 0;
+}
diff --git a/test/zdtm/static/write_read02.c b/test/zdtm/static/write_read02.c
new file mode 100644
index 000000000..0a8055805
--- /dev/null
+++ b/test/zdtm/static/write_read02.c
@@ -0,0 +1,80 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Write file half way before migration, complete and read after";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char ** argv)
+{
+ int fd, fd1;
+ int len, full_len;
+ uint32_t crc;
+ uint8_t buf[1000000];
+ char str[32];
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+
+ full_len = sizeof(buf);
+ // create standard file
+ sprintf(str, "standard_%s", filename);
+ fd1 = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (write(fd1, buf, full_len) != full_len) {
+ pr_perror("can't write %s", str);
+ exit(1);
+ }
+ close(fd1);
+
+ len = sizeof(buf) / 2;
+ if (write(fd, buf, len) != len) {
+ pr_perror("can't write %s", filename);
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (write(fd, buf + len, sizeof(buf) - len) != (sizeof(buf) - len)) {
+ fail("can't write %s: %m\n", filename);
+ goto out;
+ }
+
+ close(fd);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ fail("can't open %s: %m\n", filename);
+ return 1;
+ }
+
+ if (read(fd, buf, full_len) != full_len) {
+ fail("can't read %s: %m\n", filename);
+ return 1;
+ }
+
+ crc = ~0;
+ if (datachk(buf, full_len, &crc)) {
+ fail("CRC mismatch\n");
+ return 1;
+ }
+
+ pass();
+out:
+ unlink(filename);
+ return 0;
+}
diff --git a/test/zdtm/static/write_read10.c b/test/zdtm/static/write_read10.c
new file mode 100644
index 000000000..8af581dad
--- /dev/null
+++ b/test/zdtm/static/write_read10.c
@@ -0,0 +1,129 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Open r/w and unlink file, and fork before migration;\n"
+ "check that the child can write to it and the parent\n"
+ "can read from it after migration";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+int main(int argc, char ** argv)
+{
+ int fd, child_fd, ret;
+ pid_t pid;
+ uint32_t crc;
+ uint8_t buf[1000000];
+ task_waiter_t t;
+
+ test_init(argc, argv);
+
+ fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ child_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (child_fd < 0) {
+ pr_perror("can't open %s", filename);
+ exit(1);
+ }
+
+ if (unlink(filename)) {
+ pr_perror("can't unlink %s", filename);
+ exit(1);
+ }
+
+ task_waiter_init(&t);
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("can't fork");
+ exit(1);
+ }
+
+ if (pid == 0) { /* child writes to the unlinked file and returns */
+ close(fd);
+ task_waiter_complete_current(&t);
+ test_waitsig();
+
+ crc = ~0;
+ datagen(buf, sizeof(buf), &crc);
+ if (write(child_fd, buf, sizeof(buf)) != sizeof(buf))
+ _exit(errno);
+
+ close(child_fd);
+ _exit(0);
+ } else
+ task_waiter_wait4(&t, pid);
+
+ close(child_fd);
+
+ test_daemon();
+ test_waitsig();
+
+ if (kill(pid, SIGTERM)) {
+ fail("terminating the child failed: %m\n");
+ goto out;
+ }
+
+ if (wait(&ret) != pid) {
+ fail("wait() returned wrong pid %d: %m\n", pid);
+ goto out;
+ }
+
+ if (WIFEXITED(ret)) {
+ ret = WEXITSTATUS(ret);
+ if (ret) {
+ fail("child exited with nonzero code %d (%s)\n", ret, strerror(ret));
+ goto out;
+ }
+ }
+ if (WIFSIGNALED(ret)) {
+ fail("child exited on unexpected signal %d\n", WTERMSIG(ret));
+ goto out;
+ }
+
+ if (lseek(fd, 0, SEEK_SET) < 0) {
+ fail("lseeking to the beginning of file failed: %m\n");
+ goto out;
+ }
+
+ if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ fail("can't read %s: %m\n", filename);
+ goto out;
+ }
+
+ crc = ~0;
+ if (datachk(buf, sizeof(buf), &crc)) {
+ fail("CRC mismatch\n");
+ goto out;
+ }
+
+
+ if (close(fd)) {
+ fail("close failed: %m\n");
+ goto out_noclose;
+ }
+
+ if (unlink(filename) != -1 || errno != ENOENT) {
+ fail("file %s should have been deleted before migration: unlink: %m\n");
+ goto out_noclose;
+ }
+
+ pass();
+
+out:
+ close(fd);
+out_noclose:
+ return 0;
+}
diff --git a/test/zdtm/static/xids00.c b/test/zdtm/static/xids00.c
new file mode 100644
index 000000000..c3ddfb483
--- /dev/null
+++ b/test/zdtm/static/xids00.c
@@ -0,0 +1,128 @@
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that environment didn't change";
+const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
+
+int main(int argc, char **argv)
+{
+ int tmp_pipe[2], i;
+ int pids[2], syncfd[2], stat, fail = 0;
+
+ test_init(argc, argv);
+
+ pipe(tmp_pipe);
+ pids[0] = test_fork();
+ if (pids[0] == 0) {
+ close(tmp_pipe[0]);
+
+ setsid();
+
+ close(tmp_pipe[1]);
+ test_waitsig();
+
+ if (getpid() != getsid(0))
+ exit(1);
+
+ if (getpid() != getpgid(0))
+ exit(2);
+
+ test_msg("P1 OK\n");
+ exit(0);
+ }
+ close(tmp_pipe[1]);
+ syncfd[0] = tmp_pipe[0];
+
+ pipe(tmp_pipe);
+ pids[1] = test_fork();
+ if (pids[1] == 0) {
+ int tmp_pipe_sub[2], pid;
+
+ close(tmp_pipe[0]);
+
+ setsid();
+
+ pipe(tmp_pipe_sub);
+ pid = test_fork();
+ if (pid == 0) {
+ close(tmp_pipe[1]);
+ close(tmp_pipe_sub[0]);
+
+ setpgid(0, 0);
+
+ close(tmp_pipe_sub[1]);
+ test_waitsig();
+
+ if (getsid(0) != getppid())
+ exit(1);
+ if (getpgid(0) != getpid())
+ exit(1);
+
+ exit(0);
+ }
+ close(tmp_pipe_sub[1]);
+
+ read(tmp_pipe_sub[0], &stat, 1);
+ close(tmp_pipe_sub[0]);
+
+ close(tmp_pipe[1]);
+
+ test_waitsig();
+
+ if (getpid() != getsid(0))
+ exit(1);
+
+ if (getpid() != getpgid(0))
+ exit(2);
+
+ kill(pid, SIGTERM);
+ if (waitpid(pid, &stat, 0) < 0) {
+ pr_perror("Unable to wait P2 %d", pid);
+ exit(3);
+ } else if (!WIFEXITED(stat) || WEXITSTATUS(stat)) {
+ pr_perror("P2 stat %d/%d/%d/%d", WIFEXITED(stat), WEXITSTATUS(stat),
+ WIFSIGNALED(stat), WTERMSIG(stat));
+ exit(3);
+ }
+
+ exit(0);
+ }
+ close(tmp_pipe[1]);
+ syncfd[1] = tmp_pipe[0];
+
+ read(syncfd[0], &stat, 1);
+ close(syncfd[0]);
+ read(syncfd[1], &stat, 1);
+ close(syncfd[1]);
+
+ test_daemon();
+ test_waitsig();
+
+ for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++)
+ kill(pids[i], SIGTERM);
+
+ for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++) {
+ if (waitpid(pids[i], &stat, 0) < 0) {
+ pr_perror("Unable to wait %d", pids[i]);
+ fail = 1;
+ } else if (!WIFEXITED(stat) || WEXITSTATUS(stat)) {
+ pr_perror("P%d stat %d/%d/%d/%d", i, WIFEXITED(stat), WEXITSTATUS(stat),
+ WIFSIGNALED(stat), WTERMSIG(stat));
+ fail = 1;
+ }
+ }
+
+ if (fail)
+ fail("Something failed");
+ else
+ pass();
+
+ return 0;
+}
diff --git a/test/zdtm/static/zombie00.c b/test/zdtm/static/zombie00.c
new file mode 100644
index 000000000..8ee9c0765
--- /dev/null
+++ b/test/zdtm/static/zombie00.c
@@ -0,0 +1,110 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "See if we can wait() for a zombified child after migration";
+const char *test_author = "Roman Kagan <rkagan@parallels.com>";
+
+struct zombie {
+ int pid;
+ int exited;
+ int exitcode;
+};
+
+#define NR_ZOMBIES 4
+
+int main(int argc, char ** argv)
+{
+ int i, status;
+ struct zombie zombie[NR_ZOMBIES];
+
+ zombie[0].exited = 1;
+ zombie[0].exitcode = 0;
+
+ zombie[1].exited = 1;
+ zombie[1].exitcode = 3;
+
+ zombie[2].exited = 0;
+ zombie[2].exitcode = SIGKILL;
+
+ zombie[3].exited = 0;
+ zombie[3].exitcode = SIGSEGV;
+
+ test_init(argc, argv);
+
+ for (i = 0; i < NR_ZOMBIES; i++) {
+ zombie[i].pid = fork();
+ if (zombie[i].pid < 0) {
+ pr_perror("fork failed");
+ exit(1);
+ }
+
+ if (zombie[i].pid == 0) {
+ if (zombie[i].exited)
+ _exit(zombie[i].exitcode);
+ else if (zombie[i].exitcode == SIGSEGV)
+ *(int *)NULL = 0;
+ else
+ kill(getpid(), zombie[i].exitcode);
+
+ _exit(13); /* just in case */
+ }
+
+ test_msg("kid %d will %d/%d\n", zombie[i].pid,
+ zombie[i].exited, zombie[i].exitcode);
+ }
+
+ /*
+ * We must wait for zombies to appear, but we cannot use
+ * wait4 here :( Use sleep.
+ */
+
+ for (i = 0; i < NR_ZOMBIES; i++) {
+ siginfo_t siginfo;
+ if (waitid(P_PID, zombie[i].pid, &siginfo, WNOWAIT | WEXITED)) {
+ pr_perror("Unable to wait %d", zombie[i].pid);
+ exit(1);
+ }
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ for (i = 0; i < NR_ZOMBIES; i++) {
+ if (waitpid(zombie[i].pid, &status, 0) != zombie[i].pid) {
+ fail("Exit with wrong pid\n");
+ exit(1);
+ }
+
+ if (zombie[i].exited) {
+ if (!WIFEXITED(status)) {
+ fail("Not exited, but should (%d)\n", zombie[i].pid);
+ exit(1);
+ }
+
+ if (WEXITSTATUS(status) != zombie[i].exitcode) {
+ fail("Exit with wrong status (%d)\n", zombie[i].pid);
+ exit(1);
+ }
+ } else {
+ if (!WIFSIGNALED(status)) {
+ fail("Not killed, but should (%d)\n", zombie[i].pid);
+ exit(1);
+ }
+
+ if (WTERMSIG(status) != zombie[i].exitcode) {
+ fail("Killed with wrong signal (%d)\n", zombie[i].pid);
+ exit(1);
+ }
+ }
+ }
+
+ pass();
+ return 0;
+}