From 904029b4774b28ec1f1c19bd6c293cb34b40bd81 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 11 Nov 2016 23:27:31 +0100 Subject: [PATCH] initial --- tw_cli.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tw_cli.pl | 35 +++++++++ 2 files changed, 260 insertions(+) create mode 100644 tw_cli.c create mode 100755 tw_cli.pl diff --git a/tw_cli.c b/tw_cli.c new file mode 100644 index 0000000..202cea2 --- /dev/null +++ b/tw_cli.c @@ -0,0 +1,225 @@ +#define _GNU_SOURCE /* See feature_test_macros(7) */ +#include +#include /* For SYS_xxx definitions */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef uint64_t u64; +typedef int64_t s64; + +/* from include/linux/dirent.h : */ + +struct linux_dirent64 { + u64 d_ino; + s64 d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[0]; +}; + +void die(char *fmt,...) { + va_list ap; + va_start(ap,fmt); + vfprintf(stderr,fmt,ap); + exit(1); +} + +void die_regerror(int status,regex_t *re) { + char msg[80]; + int s; + s=regerror(status,re,msg,sizeof(msg)); + die("regex: %s\n",msg); +} + +int hostnum(char *hostname) { + static regex_t *re=NULL; + int status; + regmatch_t match[2]; + + if (!re) { + re=malloc(sizeof(*re)); + if (!re) die(""); + status=regcomp(re,"^host([0-9]+)$",REG_EXTENDED); + if (status) die_regerror(status,re); + } + + status=regexec(re,hostname,sizeof(match)/sizeof(*match),match,0); + if (status==0) { + char c=hostname[match[1].rm_eo]; + match[1].rm_eo='\0'; + int num=atoi(&hostname[match[1].rm_so]); + match[1].rm_eo=c; + return(num); + } else if (status==REG_NOMATCH) { + return(-1); + } else { + die_regerror(status,re); + } +} + +struct sortentry { + struct linux_dirent64 *dirent; + int hostnum; +}; + +int compare_sortentry(const void *vp1,const void *vp2) { + struct sortentry *p1=(struct sortentry *)vp1; + struct sortentry *p2=(struct sortentry *)vp2; + + if (p1->hostnum!=-1 && p2->hostnum!=-1) { + return p1->hostnumhostnum ? -1 : p1->hostnum>p2->hostnum ? 1 : 0; + } + return strcmp(p1->dirent->d_name,p2->dirent->d_name); +} + +void fix_memory(pid_t pid,size_t count,void *dirp) { + + char *memfilename; + int fd; + + char *dirents_unsorted,*dirents_sorted; + struct sortentry *sort_array; + struct sortentry *sort_entry; + + size_t s; + + int entry_count; + int bpos; + int i; + + struct linux_dirent64 *d; + + if (count==0) return; + + if (asprintf(&memfilename,"/proc/%d/mem",pid)==-1) die("%m\n"); + + fd=open(memfilename,O_RDWR); + if (fd==-1) die (memfilename); + + dirents_unsorted=malloc(count); + if(!dirents_unsorted) die (""); + + if (lseek(fd,(off_t)dirp,SEEK_SET)<-1) die("%s: %m\n",memfilename); + s=read(fd,dirents_unsorted,count); + if (s == -1) die("%s: %m\n",memfilename); + if (s != count) die("short reads on childs memory not implemented"); + + entry_count=0; + for (bpos=0;bposd_reclen; + } + + sort_array=malloc(entry_count*sizeof (*sort_array)); + if (!sort_array) die (""); + + sort_entry=sort_array; + for (bpos=0;bposdirent=d; + sort_entry->hostnum=hostnum(d->d_name); + sort_entry++; + bpos+=d->d_reclen; + } + + // for (i=0;id_name,sort_array[i].hostnum); } + + qsort(sort_array,entry_count,sizeof(*sort_array),compare_sortentry); + + // for (i=0;id_name,sort_array[i].hostnum); } + + dirents_sorted=malloc(count); + if(!dirents_sorted) die ("%m\n"); + + bpos=0; + for (i=0;id_reclen); + bpos+=sort_array[i].dirent->d_reclen; + } + + // for (bpos=0;bpos %s\n",d->d_name); bpos+=d->d_reclen; } + + if (lseek(fd,(off_t)dirp,SEEK_SET)<-1) die("%s: %m\n",memfilename); + s=write(fd,dirents_sorted,count); + if (s == -1) die(memfilename); + if (s != count) die("internal error: short write"); + + close(fd); + + free(memfilename); + free(dirents_unsorted); + free(dirents_sorted); + free(sort_array); +} + +int main(int argc, char **argv) { + pid_t pid; + int status; + int syscall_state=0; + struct user user; + + static const char *TW_CLI_ORIG="/root/bin/tw_cli.exe"; + + pid=fork(); + if (pid==0) { + if (ptrace(PTRACE_TRACEME,NULL,NULL)==-1) die("ptrace: %m\n"); + execv(TW_CLI_ORIG,argv); + die("%s: %m\n",TW_CLI_ORIG); + } else if (pid==-1) { + die("fork: %m\n"); + } + + while(1) { + pid=wait(&status); + if (pid==-1) die("wait: %m\n"); + if (WIFSIGNALED(status)) { + int signal=WTERMSIG(status); + die("child got signal %d - exiting\n",signal); + } else if (WIFSTOPPED(status)) { + int signal=WSTOPSIG(status); + if (signal==SIGTRAP) { + if(ptrace(PTRACE_SETOPTIONS,pid,NULL,PTRACE_O_TRACESYSGOOD)==-1) die("ptrace: %m\n"); + if(ptrace(PTRACE_SYSCALL,pid,NULL,NULL)==-1) die ("ptrace: %m\n"); + } else if (signal==SIGTRAP|0x80) { + if (syscall_state==1) { + if(ptrace(PTRACE_GETREGS,pid,NULL,&user)==-1) die("ptrace: %m\n");; + + /* the 0xFF is not right but rax is 0xffffffffffffffda , _NR_getdents64 is 217 which is 0xda + * and the syscall interface somehow fixes is */ + + if ((unsigned char)(user.regs.orig_rax & 0xFF) == SYS_getdents64) { + fix_memory(pid,(int)user.regs.rax,(void *)user.regs.rsi); + } + } + syscall_state=1-syscall_state; + if(ptrace(PTRACE_SYSCALL,pid,NULL,NULL)==-1) die("ptrace: %m\n");; + } else { + die("child stopped by signal %d - exiting\n",signal); + } + } else if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) { + exit(1); + } else { + exit(0); + } + } else { + die("unexpected return from wait. status=%08x - exiting\n",status); + } + } +} + diff --git a/tw_cli.pl b/tw_cli.pl new file mode 100755 index 0000000..43bf68e --- /dev/null +++ b/tw_cli.pl @@ -0,0 +1,35 @@ +#! /usr/bin/perl +use strict; +use warnings; + +sub sort_host { + my ($n1,$n2); + ($n1)=$a=~/^host(\d+)$/ and ($n2)=$b=~/^host(\d+)$/ and return $n1 <=> $n2; + return $a cmp $b; +} + + +our $SYS_unshare=272; # /usr/include/asm/unistd_64.h +our $CLONE_NEWNS=0x20000; # /usr/include/linux/sched.h + +my $pid=fork; +defined $pid or die "$!\n"; +unless ($pid) { + opendir my $d,"/sys/class/scsi_host"; + my @names=sort sort_host grep !/^\.\.?$/,readdir $d; + + syscall($SYS_unshare,$CLONE_NEWNS) and die "$!\n"; + -d '/tmp/sysfs' or mkdir("/tmp/sysfs") or die "/tmp/sysfs: $!\n"; + system 'mount','-tsysfs','BLA','/tmp/sysfs' and exit 1; + system 'mount','-ttmpfs','BLA','/sys/class/scsi_host' and exit 1; + + for my $name (reverse @names) { + symlink("/tmp/sysfs/class/scsi_host/$name","/sys/class/scsi_host/$name") or die "/sys/class/scsi_host/$name: $!\n"; + } + exec '/home/buczek/tw_cli/tw_cli.orig',@ARGV; + die "$!\n"; +} +wait; +$? and exit 1; + +