#if 0 # # http://fy.chalmers.se/~appro/linux/vmcdimage.c # # Copyright (c) 2001 by # # Permissions to copy and use are granted as long as the copyright # notice is retained. All warranties are however disclaimed. # # This module emulates CD-ROM ioctls on plain files and is ment to # be LD_PRELOAD-ed to VMware. Idea is to trick VMware to mount # CD-ROM images without burning a CD. Once preloaded (e.g. with # Ženv LD_PRELOAD=./vmcdimage.so vmwareŽ) the module intercepts a # number of system calls to /dev/cdrom and emulates responses # whenever appropriate. Note that /dev/cdrom is normally a symbolic # link and you can adjust it at your will on-the-fly! No VMware # restart is required! If you find the idea of using /dev/cdrom # disgusting, set _DEV_CDROM environmnet variable and adjust your # VMware guest configuration accordingly. Keep in mind that vmware # is a set-root-uid program, so that you have to be root in order to # LD_PRELOAD this module. # # Cheers, Andy:-) # /bin/sh << EOS MODNAME=\`basename "$0" .c\` set -x gcc -O -fbuiltin -fomit-frame-pointer -fPIC -shared -o \$MODNAME.so "$0" -ldl EOS exit # # Revision history: # # 20010822 Support for bootable CD images added. # 20010823 Support for mortal users(*) added. # # (*) as LD_PRELOAD works for root only, mortals has to pass a # set-root-uid wrapper of the following design (feel motivated # to rewrite it in C): # #!/usr/bin/perl -T foreach $i (%ENV) { if ($i !~ /^(DISPLAY|XAUTHORITY|TZ|USER|LOGNAME|HOME|_DEV_CDROM)$/) { delete $ENV{$i}; } } $ENV{LD_PRELOAD} ="/usr/lib/vmware/vmcdimage.so"; $ENV{_DROP_TO_RUID}="$<"; # instruct the .so to drop privileges $<=$>; exec ("/usr/bin/vmware",@ARGV); # #endif #ifndef __linux # error "Linux only" #endif #define _LIBC #define _LIBC_REENTRANT #include #include #include #include #include #include #include #include #include #include #include #ifdef __GNU_LIBRARY__ # if __GNU_LIBRARY__==6 # define LIBC_SO "libc.so.6" # else # define LIBC_SO "libc.so.5" # endif #else # error "what's your libc?" #endif static int (*__libc_open)(const char *path,int flags, ...); static int (*__libc_close)(int f); static int (*__libc_ioctl)(int f, int request, ...); static char _dev_cdrom_path[1024]="/dev/cdrom"; static fd_set _dev_cdrom_fds; static int _dev_cdrom_fd=-1; static struct stat _dev_cdrom_stat; asm (" .section \".init\" call _dev_cdrom_init "); static _dev_cdrom_init() { char *env; uid_t uid; void *libh; if (getuid()==0 && (env=getenv("_DROP_TO_RUID"))) { if (uid = strtoul (env,NULL,10)) setresuid (uid,-1,-1); } if (!(libh = dlopen (LIBC_SO,RTLD_LAZY))) { fprintf (stderr,"can't dlopen(\"%s\").",LIBC_SO); exit (1); } if (!(__libc_close = (int (*)(int))dlsym (libh,"close"))) { fprintf (stderr,"can't link to \"close\" from %s",LIBC_SO); exit (1); } if (!(__libc_open = (int (*)(const char *,int,...))dlsym (libh,"open"))) { fprintf (stderr,"can't link to \"open\" from %s",LIBC_SO); exit (1); } if (!(__libc_ioctl = (int (*)(int,int,...))dlsym (libh,"ioctl"))) { fprintf (stderr,"can't link to \"ioctl\" from %s",LIBC_SO); exit (1); } if (env=getenv ("_DEV_CDROM")) strncpy (_dev_cdrom_path,env,sizeof(_dev_cdrom_path)-1); } static int safer_open (const char *path,int flags) { uid_t uid; int fd; setresuid (-1,0,-1); fd = (*__libc_open) (path,flags); if (uid=getuid()) /* mortal user running */ { setresuid (-1,uid,-1); if (fd>=0) { /* make sure it's a cdrom device! */ if ((*__libc_ioctl) (fd,CDROM_DRIVE_STATUS,INT_MAX) == -1) { close (fd); fd = (*__libc_open) (path,flags); } } else /* might happen in NFS environment */ { fd = (*__libc_open) (path,flags); } } return fd; } static int my_open (const void *ra,const char *path,int flags,mode_t mode) { int fd; if (strcmp (path,_dev_cdrom_path)) return -ENOSYS; if (_dev_cdrom_fd==-1) { flags &= ~O_CREAT; if ((fd=safer_open (path,flags))>=0) { FD_SET(fd,&_dev_cdrom_fds); fstat (_dev_cdrom_fd=fd,&_dev_cdrom_stat); } return fd; } else { if ((fd=dup(_dev_cdrom_fd))>=0) { FD_SET(fd,&_dev_cdrom_fds); } return fd; } return -ENOSYS; } static int my_close (const void *ra,int fd) { FD_CLR(fd,&_dev_cdrom_fds); if (fd == _dev_cdrom_fd) _dev_cdrom_fd=-1; return -ENOSYS; } static int my_ioctl (const void *ra,int fd,int request,int arg) { static int cdrom_options=0xd,media_changed=1; struct stat sb; int newfd,i,ret; off_t size; if (((request>>8)&0xff) == 'S') { if (fd >= 0 && FD_ISSET(fd,&_dev_cdrom_fds)) { fprintf (stderr,"CDROM ioctl(%d,0x%x,0x%08x): ",fd,request,arg); switch (request) { case CDROMPAUSE: case CDROMRESUME: case CDROMPLAYMSF: case CDROMPLAYTRKIND: case CDROMVOLREAD: case CDROMSUBCHNL: fprintf (stderr,"discarding audio ioctls\n"); __set_errno(ENOSYS); return -1; case CDROM_MEDIA_CHANGED: if (media_changed) { fprintf (stderr,"\"media\" changed\n"); cdrom_options = 0xd; media_changed = 0; return 1; } /* no break; */ case CDROM_DRIVE_STATUS: if (stat (_dev_cdrom_path,&sb) != 0) { fprintf (stderr,"\"tray\" is open\n"); return CDS_TRAY_OPEN; } if (sb.st_dev != _dev_cdrom_stat.st_dev || sb.st_ino != _dev_cdrom_stat.st_ino ) { newfd = safer_open (_dev_cdrom_path,O_RDONLY|O_NONBLOCK); if (newfd < 0) { fprintf (stderr,"close the \"tray\"\n"); return CDS_TRAY_OPEN; } dup2 (newfd,_dev_cdrom_fd); (*__libc_close) (newfd); fprintf (stderr,"changing \"media\", "); memcpy (&_dev_cdrom_stat,&sb,sizeof(struct stat)); media_changed = 1; } else if (sb.st_size != _dev_cdrom_stat.st_size) { /* use with extreme caution */ fprintf (stderr,"\"media\" is changing, "); memcpy (&_dev_cdrom_stat,&sb,sizeof(struct stat)); media_changed = 1; } /* no break; */ default: if (!S_ISREG(_dev_cdrom_stat.st_mode)) { fprintf (stderr,"assuming hardware %s\n",_dev_cdrom_path); return -ENOSYS; } } switch (request) /* minimum ioctl set to mount CD-ROM image under VMware 2.0.4 */ { case CDROMMULTISESSION: fprintf (stderr,"emulating single session\n"); i=((struct cdrom_multisession *)arg)->addr_format; memset ((void *)arg,0,sizeof(struct cdrom_multisession)); ((struct cdrom_multisession *)arg)->addr_format=i; if (i==CDROM_MSF) ((struct cdrom_multisession *)arg)->addr.msf.second=2; else if (i==CDROM_LBA) ((struct cdrom_multisession *)arg)->addr.lba=0; else { __set_errno(EINVAL); return -1; } return 0; case CDROMREADTOCHDR: fprintf (stderr,"emulating single track\n"); ((struct cdrom_tochdr *)arg)->cdth_trk0=1; ((struct cdrom_tochdr *)arg)->cdth_trk1=1; return 0; case CDROMREADTOCENTRY: { struct cdrom_tocentry *te=(struct cdrom_tocentry *)arg; /* I'm not sure about these three, suggestions appreciated! */ te->cdte_adr = 1; te->cdte_ctrl = 4; te->cdte_datamode = 0; switch (te->cdte_track) { case 1: /* the track at LBA #0 */ if (te->cdte_format == CDROM_MSF) te->cdte_addr.msf.frame = 0, te->cdte_addr.msf.second = 2, te->cdte_addr.msf.minute = 0; else if (te->cdte_format == CDROM_LBA) te->cdte_addr.lba = 0; else { __set_errno(EINVAL); return -1; } fprintf (stderr,"track #0 at 0\n"); break; case 0xaa: /* the lead-out at st_size */ if (te->cdte_format == CDROM_MSF) size = _dev_cdrom_stat.st_size + 150, te->cdte_addr.msf.frame = size%75, te->cdte_addr.msf.second = (size/=75)%60, te->cdte_addr.msf.minute = (size)/60; else if (te->cdte_format == CDROM_LBA) te->cdte_addr.lba = _dev_cdrom_stat.st_size; else { __set_errno(EINVAL); return -1; } fprintf (stderr,"track #aa at %d\n",_dev_cdrom_stat.st_size); break; default: fprintf (stderr,"bad track #%x\n",te->cdte_track); __set_errno(EINVAL); return -1; } return 0; } case CDROM_DRIVE_STATUS: fprintf (stderr,"\"disk\" is always in\n"); return CDS_DISC_OK; case CDROM_MEDIA_CHANGED: fprintf (stderr,"\"media\" not changed\n"); return 0; case CDROM_DISC_STATUS: fprintf (stderr,"emulating generic data cd\n"); return CDS_DATA_1; case CDROM_LOCKDOOR: fprintf (stderr,"locking, doing nothing...\n"); return 0; case CDROMEJECT: fprintf (stderr,"ejecting, doing nothing...\n"); media_changed = 1; return 0; case CDROM_SET_OPTIONS: cdrom_options |= arg; fprintf (stderr,"emulated uopt is 0x%x\n",cdrom_options); return cdrom_options; case CDROM_CLEAR_OPTIONS: cdrom_options &= ~arg; fprintf (stderr,"emulated uopt is 0x%x\n",cdrom_options); return cdrom_options; default: fprintf (stderr,"unimplemented"); __set_errno(ENOSYS); return -1; } } } return -ENOSYS; } asm (" .text .type __open,@function .global __open .type open,@function .global open .align 4 __open: open: call my_open cmpl $-38,%eax jne 1f jmp *__libc_open 1: ret .size __open,.-__open .size open,.-open .text .type __close,@function .global __close .type close,@function .global close .align 4 __close: close: call my_close cmpl $-38,%eax jne 1f jmp *__libc_close 1: ret .size __close,.-__close .size close,.-close .type ioctl,@function .global ioctl .align 4 ioctl: call my_ioctl cmpl $-38,%eax jne 1f jmp *__libc_ioctl 1: ret .size ioctl,.-ioctl ");