#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
");
|
|