Index: dlls/ntdll/cdrom.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/cdrom.c,v
retrieving revision 1.75
diff -u -r1.75 cdrom.c
--- dlls/ntdll/cdrom.c	1 Dec 2005 12:01:43 -0000	1.75
+++ dlls/ntdll/cdrom.c	30 Dec 2005 17:26:35 -0000
@@ -5,6 +5,7 @@
  * Copyright 1999, 2001, 2003 Eric Pouech
  * Copyright 2000 Andreas Mohr
  * Copyright 2005 Ivan Leo Puoti
+ * Copyright 2005 Emmanuel Maillard
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -75,14 +76,37 @@
 # include <sys/scsiio.h>
 #endif
 
+#if defined(__APPLE__)
+#include <sys/mount.h>
+#include <sys/disk.h>
+#include <paths.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+/*
+ *   IOKit headers
+ */ 
 #ifdef HAVE_IOKIT_IOKITLIB_H
+# include <IOKit/IOKitLib.h>
+# include <IOKit/IOBSD.h>
+# include <IOKit/storage/IOMediaBSDClient.h>
+# include <IOKit/storage/IOCDMediaBSDClient.h>
+# include <IOKit/storage/IOMedia.h>
+# include <IOKit/storage/IOCDMedia.h>
+# include <IOKit/storage/IOCDTypes.h>
+# include <IOKit/storage/IOCDBlockStorageDevice.h>
+# include <IOKit/storage/IODVDMedia.h>
+# include <IOKit/storage/IODVDTypes.h>
 # ifndef SENSEBUFLEN
-#  include <IOKit/IOKitLib.h>
 #  include <IOKit/scsi/SCSICmds_REQUEST_SENSE_Defs.h>
 #  define SENSEBUFLEN kSenseDefaultSize
 # endif
 #endif
 
+#ifdef HAVE_DISKARBITRATION_DISKARBITRATION_H
+# include <DiskArbitration/DiskArbitration.h>
+#endif
+
 #define NONAMELESSUNION
 #define NONAMELESSSTRUCT
 #include "ntstatus.h"
@@ -325,7 +349,6 @@
 static NTSTATUS CDROM_ReadTOC(int, int, CDROM_TOC*);
 static NTSTATUS CDROM_GetStatusCode(int);
 
-
 #ifdef linux
 
 # ifndef IDE6_MAJOR
@@ -353,7 +376,6 @@
 };
 # endif  /* CDROM_SEND_PACKET */
 
-#endif  /* linux */
 
 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom
  * this should be removed when a proper device interface is implemented
@@ -375,6 +397,48 @@
  */
 #define MAX_CACHE_ENTRIES       5
 static struct cdrom_cache cdrom_cache[MAX_CACHE_ENTRIES];
+#elif defined (__APPLE__)
+
+/* Could be use for all removable device types */
+typedef struct _cd_device {
+    io_service_t service;
+    /* void **interface; */
+    CFMutableDictionaryRef characteristics;
+} cd_device;
+
+#define CDROM_DeviceHasCDMediaInsert(dev) \
+        ( \
+            CFDictionaryContainsKey(dev, CFSTR("CD Media Present")) \
+            && CFEqual( CFDictionaryGetValue(dev, CFSTR("CD Media Present")), \
+                                        kCFBooleanTrue) \
+        )
+        
+#define MAX_REMOVABLE_DEVICES   6
+static cd_device removable_devices[MAX_REMOVABLE_DEVICES];
+static int cd_devices_cpt;
+
+CFMutableDictionaryRef CDROM_GetMediaForDevice ( CFMutableStringRef device);
+int CDROM_DeviceRefForName(const char *bsdName);
+
+char *CDROM_BSDPathForMedia(CFDictionaryRef media);
+char *CDROM_GetMountPointForBSDDevice (const char *path);
+
+static BOOL CFStringCompareWithCString (CFStringRef cfString, const char * cString);
+static void CDROM_GetBSDName(CFDictionaryRef dict, char* name);
+static NTSTATUS CDROM_IOReturn2NtStatus(IOReturn ret);
+
+extern int wine_server_handle_to_bsd_device (HANDLE hdl, char *path, int size);
+#endif /* defined (__APPLE__) */
+
+#ifdef HAVE_DISKARBITRATION_DISKARBITRATION_H
+static DASessionRef daSession = NULL;
+
+static void CDROM_InitDiskArb(void);
+static void CDROM_DAEjectCallback(DADiskRef disk, DADissenterRef dissenter, void * context);
+static void CDROM_DAUnmountCallback(DADiskRef disk, DADissenterRef dissenter, void * context);
+static BOOL CDROM_DAUnmount(const char *disk);
+static BOOL CDROM_DAEject(const char *disk);
+#endif
 
 static RTL_CRITICAL_SECTION cache_section;
 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
@@ -422,6 +486,8 @@
 }
 #endif
 
+#ifndef __APPLE__ /* CDROM_SyncCache and CDROM_ClearCacheEntry are not used on Darwin */
+
 /******************************************************************
  *		CDROM_SyncCache                          [internal]
  *
@@ -538,13 +604,16 @@
 #endif
 }
 
+
 static void CDROM_ClearCacheEntry(int dev)
 {
     RtlEnterCriticalSection( &cache_section );
     cdrom_cache[dev].toc_good = 0;
     RtlLeaveCriticalSection( &cache_section );
 }
-
+#else 
+#define CDROM_ClearCacheEntry(d)
+#endif /* !__APPLE__ */
 
 
 /******************************************************************
@@ -626,7 +695,7 @@
 #endif
 }
 
-
+#ifndef __APPLE__
 /******************************************************************
  *		CDROM_Open
  *
@@ -665,6 +734,7 @@
     TRACE("%d, %d\n", *dev, fd);
     return ret;
 }
+#endif
 
 /******************************************************************
  *		CDROM_GetStatusCode
@@ -706,7 +776,7 @@
   CDROM_TOC     toc;
   NTSTATUS      ret = 0;
   int           fsize = 0;
-
+    
   if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
 
   fsize = FRAME_OF_TOC(toc, toc.LastTrack+1)
@@ -730,6 +800,9 @@
     return CDROM_GetStatusCode(ioctl(fd, CDROMRESET));
 #elif defined(__FreeBSD__) || defined(__NetBSD__)
     return CDROM_GetStatusCode(ioctl(fd, CDIOCRESET, NULL));
+#elif defined (__APPLE__)
+    FIXME("\n");
+    return STATUS_NOT_SUPPORTED;
 #else
     return STATUS_NOT_SUPPORTED;
 #endif
@@ -748,6 +821,33 @@
     return CDROM_GetStatusCode((ioctl(fd, CDIOCALLOW, NULL)) ||
                                (ioctl(fd, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
                                (ioctl(fd, CDIOCPREVENT, NULL)));
+#elif defined(__APPLE__)
+    NTSTATUS status = STATUS_NOT_SUPPORTED;
+
+    TRACE("fd : %d doEject : %d\n", fd, doEject);
+
+    if ( CDROM_DeviceHasCDMediaInsert(removable_devices[fd].characteristics) && doEject)
+    {
+#ifdef HAVE_DISKARBITRATION_DISKARBITRATION_H
+        char bsdName[6];
+        CDROM_GetBSDName(removable_devices[fd].characteristics, bsdName);
+        
+        status = (CDROM_DAEject(bsdName)) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
+#else
+        FIXME("use hdiutil to unmount and eject media\n");
+        status = STATUS_NOT_SUPPORTED;
+#endif
+    }
+    else
+    {
+        IOReturn ioError;
+        CFTypeRef tray = doEject ? kCFBooleanTrue : kCFBooleanFalse;
+    
+        ioError = IORegistryEntrySetCFProperty(removable_devices[fd].service, CFSTR("TrayState"), tray);
+
+        status = CDROM_IOReturn2NtStatus(ioError);
+    }
+    return status;
 #else
     return STATUS_NOT_SUPPORTED;
 #endif
@@ -764,11 +864,39 @@
     return CDROM_GetStatusCode(ioctl(fd, CDROM_LOCKDOOR, rmv->PreventMediaRemoval));
 #elif defined(__FreeBSD__) || defined(__NetBSD__)
     return CDROM_GetStatusCode(ioctl(fd, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL));
+#elif defined(__APPLE__)
+    FIXME("%s\n", (rmv->PreventMediaRemoval) ? "PREVENT": "ALLOW");
+    return STATUS_SUCCESS;
 #else
     return STATUS_NOT_SUPPORTED;
 #endif
 }
 
+#ifdef __APPLE__
+/*
+ * CDROM_ReadTOC helpers
+ */
+static int CDROM_IsAudioTrack(CDTOCDescriptor *ptr)
+{
+    if (ptr->point < 100 && ptr->point > 0)
+    {
+        if ( (ptr->control & 0x04 ) == 0 )
+            return 1;
+    }
+    return 0;
+}
+
+static CDTOCDescriptor *CDROM_TOCGetTrackDescriptor(CDTOC *toc, UInt8 track)
+{
+    int i;
+    for (i = 0; i < CDTOCGetDescriptorCount(toc); i++)
+        if (toc->descriptors[i].point == track)
+            return &toc->descriptors[i];
+    
+    return NULL;
+}
+#endif
+
 /******************************************************************
  *		CDROM_ReadTOC
  *
@@ -776,8 +904,8 @@
  */
 static NTSTATUS CDROM_ReadTOC(int dev, int fd, CDROM_TOC* toc)
 {
+#ifndef __APPLE__    
     NTSTATUS       ret = STATUS_NOT_SUPPORTED;
-
     if (dev < 0 || dev >= MAX_CACHE_ENTRIES)
         return STATUS_INVALID_PARAMETER;
 
@@ -788,7 +916,67 @@
         ret = STATUS_SUCCESS;
     }
     RtlLeaveCriticalSection( &cache_section );
+    
     return ret;
+#else
+    int i;
+    int tsz;
+    TRACE("\n");
+
+    if ( ! CDROM_DeviceHasCDMediaInsert(removable_devices[dev].characteristics) )
+        return STATUS_NO_MEDIA_IN_DEVICE; 
+    
+    CFDictionaryRef media = (CFDictionaryRef) CFDictionaryGetValue(removable_devices[dev].characteristics, CFSTR("Media"));
+    
+    CDTOC *tocPtr = (CDTOC *) CFDataGetBytePtr(CFDictionaryGetValue(media, CFSTR("TOC")));
+    CDTOCDescriptor *descrPtr;
+    UInt32 descrCount = CDTOCGetDescriptorCount(tocPtr);
+    
+    /*
+     *   Find "Audio tracks" for this session
+     */
+    toc->FirstTrack = toc->LastTrack = 1;
+    for (i = 0; i < descrCount; i++)
+    {
+        if (CDROM_IsAudioTrack(&tocPtr->descriptors[i]))
+        {
+            toc->FirstTrack = min(toc->FirstTrack, tocPtr->descriptors[i].point);
+            toc->LastTrack = max(toc->LastTrack, tocPtr->descriptors[i].point);
+        }
+    }
+    tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
+        + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
+    toc->Length[0] = tsz >> 8;
+    toc->Length[1] = tsz;
+
+    TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
+
+    for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
+    {
+        if (i == toc->LastTrack + 1)
+        {
+            /* On Darwin leadout is 0xA2 */
+            descrPtr = CDROM_TOCGetTrackDescriptor(tocPtr, 0xA2);
+        } else {
+            descrPtr = CDROM_TOCGetTrackDescriptor(tocPtr, (UInt8) i);
+        }
+        
+        if (descrPtr)
+        {            
+            toc->TrackData[i - toc->FirstTrack].Control = descrPtr->control;
+            toc->TrackData[i - toc->FirstTrack].Adr = descrPtr->adr;
+            toc->TrackData[i - toc->FirstTrack].TrackNumber = descrPtr->point;
+
+            CDMSF msf = CDConvertTrackNumberToMSF(descrPtr->point, tocPtr);
+            toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
+            toc->TrackData[i - toc->FirstTrack].Address[1] = msf.minute;
+            toc->TrackData[i - toc->FirstTrack].Address[2] = msf.second;
+            toc->TrackData[i - toc->FirstTrack].Address[3] = msf.frame;
+        }
+    }
+    
+    return STATUS_SUCCESS;
+#endif
 }
 
 /******************************************************************
@@ -801,7 +989,7 @@
     CDROM_TOC   toc;
     NTSTATUS    ret;
     int         i;
-
+    
     if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
     data->DiskData = 0;
     for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
@@ -1023,6 +1211,71 @@
 
  end:
     ret = CDROM_GetStatusCode(io);
+#elif defined(__APPLE__)
+    FIXME("()\n");
+    SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
+    
+    if ( CDROM_DeviceHasCDMediaInsert(removable_devices[dev].characteristics) )
+    {
+        hdr->AudioStatus = AUDIO_STATUS_NO_STATUS; 
+
+        unsigned int size;
+        int ioret = -1;
+        dk_cd_read_isrc_t read_isrc;
+        dk_cd_read_mcn_t read_mcn;
+        char *buffer;
+        u_int16_t len;
+        
+        switch (fmt->Format)
+        {
+            case IOCTL_CDROM_CURRENT_POSITION:
+                FIXME("IOCTL_CDROM_CURRENT_POSITION\n");
+                break;
+            case IOCTL_CDROM_MEDIA_CATALOG:
+                TRACE("IOCTL_CDROM_MEDIA_CATALOG\n");
+                ioret = ioctl(fd, DKIOCCDREADMCN, &read_mcn);
+                break;
+            case IOCTL_CDROM_TRACK_ISRC:
+                TRACE("IOCTL_CDROM_TRACK_ISRC\n");
+                read_isrc.track = data->TrackIsrc.Track;
+                ioret = ioctl(fd, DKIOCCDREADISRC, &read_isrc);
+                break;
+        }
+
+        if (ioret == -1)
+        {
+            TRACE("opened or no_media (%s)!\n", strerror(errno));
+            hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
+            goto end;
+        }
+
+        hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
+
+        switch (fmt->Format)
+        {
+            case IOCTL_CDROM_CURRENT_POSITION:
+                break;
+            case IOCTL_CDROM_MEDIA_CATALOG:
+                size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
+                data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
+                data->MediaCatalog.Mcval = 1; /* FIXME */
+                memcpy(data->MediaCatalog.MediaCatalog, read_mcn.mcn, kCDMCNMaxLength+1);
+                break;
+            case IOCTL_CDROM_TRACK_ISRC:
+                size = sizeof(SUB_Q_CURRENT_POSITION);
+                data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
+                data->TrackIsrc.Tcval = 1; /* FIXME */
+                memcpy(data->TrackIsrc.TrackIsrc, read_isrc.isrc, kCDISRCMaxLength+1);
+                break;
+        }
+        ret = CDROM_GetStatusCode(ioret);
+    }
+    else
+    {
+        hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
+        ret = STATUS_NO_MEDIA_IN_DEVICE;
+    }
+end:
 #endif
     return ret;
 }
@@ -1118,6 +1371,9 @@
 	  msf.end_m,   msf.end_s,   msf.end_f);
 end:
     ret = CDROM_GetStatusCode(io);
+#elif defined(__APPLE__)
+    FIXME("\n");  
+    ret = STATUS_NOT_SUPPORTED;
 #endif
     return ret;
 }
@@ -1142,6 +1398,7 @@
     int final_frame;
 #endif
 
+#if !defined(__APPLE__)
     /* Use the information on the TOC to compute the new current
      * position, which is shadowed on the cache. [Portable]. */
     frame = FRAME_OF_MSF(*audio_msf);
@@ -1168,6 +1425,7 @@
     cp->TrackRelativeAddress[0] = 0;
     MSF_OF_FRAME(cp->TrackRelativeAddress[1], frame); 
     RtlLeaveCriticalSection( &cache_section );
+#endif /* #if !defined(__APPLE__) */
 
     /* If playing, then issue a seek command, otherwise do nothing */
 #ifdef linux
@@ -1214,6 +1472,9 @@
       return CDROM_GetStatusCode(ioctl(fd, CDIOCPLAYMSF, &msf));
     }
     return STATUS_SUCCESS;
+#elif defined(__APPLE__)
+    FIXME("\n");
+    return STATUS_NOT_SUPPORTED;
 #else
     return STATUS_NOT_SUPPORTED;
 #endif
@@ -1230,6 +1491,9 @@
     return CDROM_GetStatusCode(ioctl(fd, CDROMPAUSE));
 #elif defined(__FreeBSD__) || defined(__NetBSD__)
     return CDROM_GetStatusCode(ioctl(fd, CDIOCPAUSE, NULL));
+#elif defined(__APPLE__)
+    FIXME("\n");
+    return STATUS_NOT_SUPPORTED;
 #else
     return STATUS_NOT_SUPPORTED;
 #endif
@@ -1246,6 +1510,9 @@
     return CDROM_GetStatusCode(ioctl(fd, CDROMRESUME));
 #elif defined(__FreeBSD__) || defined(__NetBSD__)
     return CDROM_GetStatusCode(ioctl(fd, CDIOCRESUME, NULL));
+#elif defined(__APPLE__)
+    FIXME("\n");
+    return STATUS_NOT_SUPPORTED;
 #else
     return STATUS_NOT_SUPPORTED;
 #endif
@@ -1262,6 +1529,10 @@
     return CDROM_GetStatusCode(ioctl(fd, CDROMSTOP));
 #elif defined(__FreeBSD__) || defined(__NetBSD__)
     return CDROM_GetStatusCode(ioctl(fd, CDIOCSTOP, NULL));
+#elif defined(__APPLE__)
+    FIXME("\n");
+    
+    return STATUS_NOT_SUPPORTED;
 #else
     return STATUS_NOT_SUPPORTED;
 #endif
@@ -1300,6 +1571,10 @@
         vc->PortVolume[3] = volc.vol[3];
     }
     return CDROM_GetStatusCode(io);
+#elif defined(__APPLE__)
+    FIXME("\n");
+    
+    return STATUS_NOT_SUPPORTED;
 #else
     return STATUS_NOT_SUPPORTED;
 #endif
@@ -1330,6 +1605,10 @@
     volc.vol[3] = vc->PortVolume[3];
 
     return CDROM_GetStatusCode(ioctl(fd, CDIOCSETVOL, &volc));
+#elif defined(__APPLE__)
+    FIXME("\n");
+    
+    return STATUS_NOT_SUPPORTED;
 #else
     return STATUS_NOT_SUPPORTED;
 #endif
@@ -1453,6 +1732,10 @@
     int io;
 #endif
 
+#ifdef __APPLE__
+    FIXME("\n");
+#endif 
+
     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
         return STATUS_BUFFER_TOO_SMALL;
 
@@ -1574,6 +1857,10 @@
     int io;
 #endif
 
+#ifdef __APPLE__
+    FIXME("\n");
+#endif 
+
     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
         return STATUS_BUFFER_TOO_SMALL;
 
@@ -2022,11 +2309,25 @@
     piosb->Information = 0;
 
     if ((status = wine_server_handle_to_fd( hDevice, 0, &fd, NULL ))) goto error;
+#ifndef __APPLE__
     if ((status = CDROM_Open(fd, &dev)))
     {
         wine_server_release_fd( hDevice, fd );
         goto error;
     }
+#else
+    char bsdName[64];
+    
+    if ((status = wine_server_handle_to_bsd_device( hDevice, bsdName, sizeof(bsdName)))) goto error;
+    
+    if ( (dev = CDROM_DeviceRefForName( bsdName )) == -1 )
+    {
+        status = STATUS_NO_SUCH_DEVICE;
+        goto error;
+    }
+
+    TRACE("bsdName : %s -- dev : %d -- fd : %d\n", bsdName, dev, fd);
+#endif
 
     switch (dwIoControlCode)
     {
@@ -2051,14 +2352,23 @@
 	CDROM_ClearCacheEntry(dev);
         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
             status = STATUS_INVALID_PARAMETER;
-        else status = CDROM_SetTray(fd, FALSE);
+#ifndef __APPLE__
+            status = CDROM_SetTray(fd, FALSE);
+#else
+            status = CDROM_SetTray(dev, FALSE);
+#endif
         break;
      case IOCTL_STORAGE_EJECT_MEDIA:
         sz = 0;
 	CDROM_ClearCacheEntry(dev);
         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
             status = STATUS_INVALID_PARAMETER;
-        else status = CDROM_SetTray(fd, TRUE);
+        else
+#ifndef __APPLE__
+            status = CDROM_SetTray(fd, TRUE);
+#else
+            status = CDROM_SetTray(dev, TRUE);
+#endif
         break;
 
     case IOCTL_CDROM_MEDIA_REMOVAL:
@@ -2284,3 +2594,475 @@
     if (hEvent) NtSetEvent(hEvent, NULL);
     return status;
 }
+
+
+#if defined (__APPLE__)
+#pragma mark - Darwin
+
+/* FindEjectableCDMedia came from an Apple sample code */
+kern_return_t CDROM_FindEjectableCDMedia( io_iterator_t *mediaIterator )
+{
+    kern_return_t kernResult = KERN_FAILURE; 
+    mach_port_t masterPort;
+    CFMutableDictionaryRef matchingClasses;
+
+    kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
+    if ( KERN_SUCCESS != kernResult )
+    {
+        return kernResult;
+    }
+        
+    matchingClasses = IOServiceMatching( kIOCDMediaClass ); 
+    if (matchingClasses)
+    {
+	CFDictionarySetValue( matchingClasses, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); 
+        // Each IOMedia object has a property with key kIOMediaEjectableKey which is true if the
+        // media is indeed ejectable. So add this property to the CFDictionary we're matching on.
+         
+        kernResult = IOServiceGetMatchingServices( masterPort, matchingClasses, mediaIterator );
+    }
+    return kernResult;
+}
+
+CFMutableDictionaryRef CDROM_GetMediaForDevice ( CFMutableStringRef device)
+{
+    io_iterator_t mediaIterator;
+    CFMutableDictionaryRef cdMedia = NULL;
+    kern_return_t kernResult;
+    CFMutableStringRef mediaName = CFStringCreateMutable(kCFAllocatorDefault, 255);
+    
+    kernResult = CDROM_FindEjectableCDMedia(&mediaIterator );
+    
+    if (kernResult == KERN_SUCCESS && mediaName)
+    {
+        io_object_t nextMedia;
+        
+        CFStringAppend (mediaName, device);
+        CFStringAppend (mediaName, CFSTR(" Media"));
+        
+        while ( (nextMedia = IOIteratorNext( mediaIterator )) )
+        {
+            CFStringRef name;
+            CFTypeRef cfValue;
+            io_name_t className;
+            
+            IORegistryEntryGetName ( nextMedia, className);
+            
+            name = CFStringCreateWithCString( kCFAllocatorDefault, className, kCFStringEncodingASCII); 
+            
+            if (name && CFStringCompare ( name, mediaName, 0) == kCFCompareEqualTo )
+            {
+                cdMedia = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                
+                if ( cdMedia )
+                {
+                    cfValue = IORegistryEntryCreateCFProperty( nextMedia, 
+                                                               CFSTR("Type"), 
+                                                               kCFAllocatorDefault, 
+                                                               0 );
+                    if (cfValue)
+                    {
+                        CFDictionaryAddValue( cdMedia, CFSTR("Type"), cfValue);
+                        CFRelease(cfValue);
+                    }
+                
+                    cfValue = IORegistryEntryCreateCFProperty( nextMedia, 
+                                                               CFSTR("Size"), 
+                                                               kCFAllocatorDefault, 
+                                                               0 );
+                    if (cfValue)
+                    {
+                        CFDictionaryAddValue( cdMedia, CFSTR("Size"), cfValue);
+                        CFRelease(cfValue);
+                    }
+                    
+                    cfValue = IORegistryEntryCreateCFProperty( nextMedia, 
+                                                               CFSTR("BSD Name"), 
+                                                               kCFAllocatorDefault, 
+                                                               0 );
+                    if (cfValue)
+                    {
+                        CFDictionaryAddValue( cdMedia, CFSTR("BSD Name"), cfValue);
+                        CFRelease(cfValue);
+                    }
+                    
+                    cfValue = IORegistryEntryCreateCFProperty( nextMedia, 
+                                                               CFSTR("TOC"), 
+                                                               kCFAllocatorDefault, 
+                                                               0 );
+                    if (cfValue)
+                    {
+                        CFDictionaryAddValue( cdMedia, CFSTR("TOC"), cfValue);
+                        CFRelease(cfValue);
+                    }
+                }
+                
+                break;
+            }   /* if ( CFStringCompare ( name, device, 0) == kCFCompareEqualTo ) */
+            if (name)
+                CFRelease(name);
+        }
+    }
+    if (mediaName)
+        CFRelease(mediaName);
+    return cdMedia;
+}
+
+BOOL CFStringCompareWithCString (CFStringRef cfString, const char * cString)
+{
+    BOOL retVal = FALSE;
+    char _cString[256];
+    
+    if (cfString && CFStringGetCString(cfString, _cString, 256, kCFStringEncodingASCII))
+        retVal = (strcmp(_cString, cString) == 0);
+        
+    return retVal;
+}
+
+CFMutableDictionaryRef CDROM_GetCDDeviceCharacteristics (io_service_t device)
+{
+    CFMutableDictionaryRef characteristics = NULL;
+    CFTypeRef cfValue;
+            
+    characteristics = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+            
+    if ( characteristics )
+    {
+        cfValue = IORegistryEntryCreateCFProperty( device, 
+                                                    CFSTR("Device Characteristics"), 
+                                                    kCFAllocatorDefault, 
+                                                    0 );
+        if (cfValue)
+        {
+            CFDictionaryAddValue( characteristics, CFSTR("Device Characteristics"), cfValue);
+                    
+            /* compute device name */
+            CFMutableStringRef deviceName = CFStringCreateMutable(kCFAllocatorDefault, 255);
+                    
+            if (deviceName)
+            {
+                CFStringAppend(deviceName , CFDictionaryGetValue (cfValue, CFSTR("Vendor Name")));
+                CFStringAppend(deviceName , CFSTR(" "));
+                CFStringAppend(deviceName , CFDictionaryGetValue (cfValue, CFSTR("Product Name")));
+                        
+                CFDictionaryAddValue( characteristics, CFSTR("Device Name"), deviceName);
+                        
+                CFRelease(deviceName);
+            }
+            CFRelease(cfValue);
+        }
+        
+        cfValue = IORegistryEntryCreateCFProperty( device, 
+                                                    CFSTR("device-type"), 
+                                                    kCFAllocatorDefault, 
+                                                    0 );
+        if (cfValue)
+        {
+            CFDictionaryAddValue( characteristics, CFSTR("device-type"), cfValue);
+            CFRelease(cfValue);
+        }
+	                
+        CFMutableStringRef deviceName = (CFMutableStringRef) CFDictionaryGetValue(characteristics, CFSTR("Device Name"));
+                
+        if (deviceName)
+        {
+            CFTypeRef cd;
+            cd = CDROM_GetMediaForDevice (deviceName);
+                    
+            if (cd)
+            {
+                CFDictionaryAddValue( characteristics, CFSTR("CD Media Present"), kCFBooleanTrue);
+                CFDictionaryAddValue( characteristics, CFSTR("Media"), cd);
+                CFRelease(cd);
+            }
+        }
+    }
+    return characteristics;
+}
+
+char *CDROM_BSDPathForMedia(CFDictionaryRef media)
+{
+    static const char *path_raw_dev = "/dev/r";
+    
+    char bsdNode[6];
+    char *bsdPath = (char *) malloc (sizeof(path_raw_dev) + sizeof(bsdNode) - 1);
+    
+    if (bsdPath)
+    {
+        CFStringGetCString( CFDictionaryGetValue(media, CFSTR("BSD Name")),
+                                bsdNode, sizeof(bsdNode), kCFStringEncodingASCII );
+        
+        strcpy(bsdPath, path_raw_dev);
+        strcat(bsdPath, bsdNode);
+    }
+    return bsdPath;
+}
+
+char *CDROM_GetMountPointForBSDDevice(const char *path)
+{
+    struct statfs *mntStat;
+    int i;
+    int mntSize;
+    char *mntPath = NULL;
+       
+    mntSize = getmntinfo(&mntStat, MNT_NOWAIT);
+
+    for (i = 0; i < mntSize && !mntPath; i++)
+    {
+        if ( ! strncmp(path+(strlen(_PATH_DEV) + 1) , mntStat[i].f_mntfromname+(strlen(_PATH_DEV)), 5) )
+        {
+            mntPath = (char *) malloc ( strlen(mntStat[i].f_mntonname) + 1 );
+            
+            if ( mntPath )
+                strcpy (mntPath, mntStat[i].f_mntonname);
+        }
+    }
+    return mntPath;
+}
+
+/*
+ *    Parse IORegistryEntry to find available devices (aka IOCDBlockStorageDevice).
+ */
+kern_return_t CDROM_FindCDMediaDevice(io_iterator_t *mediaIterator )
+{
+    kern_return_t kernResult; 
+    mach_port_t masterPort;
+    CFMutableDictionaryRef matchingClasses = NULL;
+
+    kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
+
+    if ( kernResult == KERN_SUCCESS)
+        matchingClasses = IOServiceMatching( kIOCDBlockStorageDeviceClass ); 
+    
+    if ( matchingClasses == NULL )
+        return KERN_FAILURE;
+    
+    return IOServiceGetMatchingServices( masterPort, matchingClasses, mediaIterator );
+}
+
+int CDROM_DeviceRefForName(const char *bsdName)
+{
+    int i;
+    int retVal = -1;
+        
+    RtlEnterCriticalSection( &cache_section );
+    for (i = 0; i < cd_devices_cpt; i++)
+    {
+        if ( CDROM_DeviceHasCDMediaInsert( removable_devices[i].characteristics ) )                              
+        {
+            CFDictionaryRef media = CFDictionaryGetValue(removable_devices[i].characteristics, CFSTR("Media"));
+            
+            if (CFStringCompareWithCString (CFDictionaryGetValue(media, CFSTR("BSD Name")), bsdName))
+            {
+                retVal = i;
+                break;
+            }
+        }
+        else
+        {
+            if (CFStringCompareWithCString (CFDictionaryGetValue(removable_devices[i].characteristics, CFSTR("Device Name")), bsdName))
+            {
+                retVal = i;
+                break;
+            }
+        }
+    }
+    RtlLeaveCriticalSection( &cache_section );
+    
+    return retVal;
+}
+
+/*
+ *   CDROM_FoundDevices
+ *   return an array with characteristics of all founded devices.
+ *   caller must release the array.
+ */
+CFMutableArrayRef CDROM_FoundDevices(void)
+{
+    CFMutableArrayRef retVal = CFArrayCreateMutable(kCFAllocatorDefault, cd_devices_cpt, &kCFTypeArrayCallBacks);
+    
+    if (retVal)
+    {
+        int i;
+        for(i = 0; i < cd_devices_cpt; i++)
+        {
+            CFArrayAppendValue(retVal, removable_devices[i].characteristics);
+        }
+    }
+    return retVal;
+}
+
+/*
+ *  Init routine for CD support.
+ */
+void CDROM_InitCDDevices(void)
+{
+    io_iterator_t iterator;
+    cd_devices_cpt = 0;
+
+    TRACE("()\n");
+
+#ifdef HAVE_DISKARBITRATION_DISKARBITRATION_H
+    CDROM_InitDiskArb();
+#endif
+
+    if (CDROM_FindCDMediaDevice(&iterator) == KERN_SUCCESS)
+    {
+        io_object_t nextDevice;
+        
+        while ( (nextDevice = IOIteratorNext(iterator))
+                && (cd_devices_cpt < MAX_REMOVABLE_DEVICES) )
+        {
+            CFMutableDictionaryRef characteristics = NULL;
+                
+            removable_devices[cd_devices_cpt].service = nextDevice;
+
+            if ( (characteristics = CDROM_GetCDDeviceCharacteristics(nextDevice)) )
+                removable_devices[cd_devices_cpt].characteristics = characteristics;
+
+            cd_devices_cpt++;
+        }
+    }
+    if (iterator)
+        IOObjectRelease(iterator);
+}
+
+char *CDROM_BSDPathForHandle(HANDLE hdl)
+{
+    char bsdName[6];
+    char *fullPath = NULL;
+       
+    if ( ! wine_server_handle_to_bsd_device( hdl, bsdName, sizeof(bsdName)) )
+    {
+        int ref;
+        if ( (ref = CDROM_DeviceRefForName( bsdName )) != -1 )
+            fullPath = CDROM_BSDPathForMedia( CFDictionaryGetValue(removable_devices[ref].characteristics,
+                                                                    CFSTR("Media")));
+    }
+    TRACE("return fullPath : %s\n", (fullPath)?fullPath:"NULL");
+    return fullPath;
+}
+
+void CDROM_GetBSDName(CFDictionaryRef dict, char *name)
+{
+    CFDictionaryRef media = CFDictionaryGetValue(dict, CFSTR("Media"));
+    CFStringGetCString( CFDictionaryGetValue(media, CFSTR("BSD Name")),
+                                name, 6, kCFStringEncodingASCII );
+}
+
+NTSTATUS CDROM_IOReturn2NtStatus(IOReturn ret)
+{
+    /* FIXME */
+    TRACE("( 0x%08x )\n", ret);
+    switch (ret)
+    {
+        case kIOReturnSuccess: return STATUS_SUCCESS;
+        case kIOReturnBusy: return STATUS_DEVICE_BUSY;
+        case kIOReturnNoDevice: return STATUS_NO_SUCH_DEVICE;
+        case kIOReturnNoMemory: return STATUS_NO_MEMORY;
+        case kIOReturnUnsupported: return STATUS_NOT_SUPPORTED;
+        case kIOReturnNoMedia: return STATUS_NO_MEDIA_IN_DEVICE;
+        case kIOReturnTimeout:
+        case kIOReturnNotPermitted: 
+        case kIOReturnExclusiveAccess:
+        case kIOReturnError: 
+            return STATUS_UNSUCCESSFUL;
+    }
+}
+#else /* ! __APPLE__ */
+void CDROM_InitCDDevices(void)
+{
+}
+
+void *CDROM_FoundDevices(void)
+{
+    return NULL;
+}
+
+char *CDROM_GetMountPointForBSDDevice (const char *path)
+{
+    return NULL;
+}
+
+char *CDROM_BSDPathForMedia(void *media)
+{
+    return NULL;
+}
+
+char *CDROM_GetMountPointForBSDDevice (const char *path)
+{
+    return NULL;
+}
+
+char *CDROM_BSDPathForHandle(HANDLE hdl)
+{
+    return NULL;
+}
+#endif
+
+#ifdef HAVE_DISKARBITRATION_DISKARBITRATION_H
+
+void CDROM_InitDiskArb(void)
+{
+    TRACE("\n");
+    
+    daSession = DASessionCreate(kCFAllocatorDefault);
+    if (daSession)
+    {
+        DASessionScheduleWithRunLoop(daSession, CFRunLoopGetCurrent(), CFSTR("MyCallbackMode"));
+        
+        /*
+         * TODO
+         * Register for disk appeared/disappeared notifications.
+         */
+    }
+    else ERR("DASessionCreate failed\n");
+}
+
+BOOL CDROM_DAUnmount(const char *disk)
+{
+    TRACE("\n");
+    DADiskRef unmount;
+    BOOL unmounted = TRUE;
+    
+    unmount = DADiskCreateFromBSDName(kCFAllocatorDefault, daSession, disk);
+    if (unmount)
+    {
+        DADiskUnmount(unmount, kDADiskUnmountOptionDefault, CDROM_DAUnmountCallback, (void *) &unmounted);
+        CFRelease(unmount);
+    }
+    
+    TRACE("ret %s\n", (unmounted)? "YES":"NO");
+    return unmounted;
+}
+
+BOOL CDROM_DAEject(const char *disk)
+{
+    TRACE("\n");
+    BOOL ejected;
+    if ( (ejected = CDROM_DAUnmount(disk)) )
+    {
+        DADiskRef eject = DADiskCreateFromBSDName(kCFAllocatorDefault, daSession, disk);
+            
+        if (eject)
+        {
+            DADiskEject(eject, kDADiskUnmountOptionDefault, CDROM_DAEjectCallback, (void *) &ejected);
+            CFRelease(eject);
+        }
+    }
+    TRACE("ret %s\n", (ejected)? "YES":"NO");
+    return ejected;
+}
+
+void CDROM_DAUnmountCallback(DADiskRef disk, DADissenterRef dissenter, void *context)
+{
+    *((BOOL *) context) = FALSE;
+    ERR("Can't unmount %s ( 0x%08x )\n", DADiskGetBSDName(disk), DADissenterGetStatus(dissenter));
+}
+
+void CDROM_DAEjectCallback(DADiskRef disk, DADissenterRef dissenter, void *context)
+{
+    *((BOOL *) context) = FALSE;
+    ERR("Can't eject %s ( 0x%08x )\n", DADiskGetBSDName(disk), DADissenterGetStatus(dissenter));
+}
+# endif
Index: dlls/ntdll/ntdll.spec
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/ntdll.spec,v
retrieving revision 1.203
diff -u -r1.203 ntdll.spec
--- dlls/ntdll/ntdll.spec	16 Dec 2005 13:41:49 -0000	1.203
+++ dlls/ntdll/ntdll.spec	30 Dec 2005 17:25:34 -0000
@@ -1396,3 +1396,8 @@
 #
 @ cdecl MODULE_DllThreadAttach(ptr)
 @ cdecl MODULE_GetLoadOrderW(ptr wstr wstr)
+@ cdecl CDROM_InitCDDevices()
+@ cdecl CDROM_FoundDevices()
+@ cdecl CDROM_BSDPathForMedia(ptr)
+@ cdecl CDROM_GetMountPointForBSDDevice(ptr)
+@ cdecl CDROM_BSDPathForHandle(long)
