#include <sys/param.h>
#define CD9660
#include <sys/mount.h>
#include <sys/../isofs/cd9660/cd9660_mount.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <CoreFoundation/CFBase.h>
#include <libkern/OSByteOrder.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOCDMedia.h>
#include "../disklib/mntopts.h"
struct CDMSF {
u_char minute;
u_char second;
u_char frame;
};
struct CDTOC_Desc {
u_char session;
u_char ctrl_adr;
u_char tno;
u_char point;
struct CDMSF address;
u_char zero;
struct CDMSF p;
};
struct CDTOC {
u_short length;
u_char first_session;
u_char last_session;
struct CDTOC_Desc trackdesc[1];
};
#define CD_MIN_TRACK_NO 1
#define CD_MAX_TRACK_NO 99
#define CD_SUBQ_DATA 0
#define CD_CURRENT_POSITION 1
#define CD_MEDIA_CATALOG 2
#define CD_TRACK_INFO 3
#define CD_CTRL_DATA 0x4
#define CD_CTRL_AUDIO 0x8
#define MSF_TO_LBA(msf) \
(((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
struct mntopt mopts[] = {
MOPT_STDOPTS,
MOPT_UPDATE,
{ "extatt", 0, ISOFSMNT_EXTATT, 1 },
{ "gens", 0, ISOFSMNT_GENS, 1 },
{ "rrip", 1, ISOFSMNT_NORRIP, 1 },
{ "joliet", 1, ISOFSMNT_NOJOLIET, 1 },
{ NULL }
};
typedef struct ISOVolumeDescriptor
{
char type[1];
char id[5]; char version[1];
char filler[33];
char volumeID[32];
char filler2[1976];
} ISOVolumeDescriptor, *ISOVolumeDescriptorPtr;
#define CDROM_BLOCK_SIZE 2048
#define ISO_STANDARD_ID "CD001"
#define ISO_VD_PRIMARY 0x01 // for ISOVolumeDescriptor.type when it's a primary descriptor
void usage __P((void));
static int get_ssector(char *devpath);
static u_char * get_cdtoc(char * devpath);
static u_char * CreateBufferFromCFData(CFDataRef theData);
struct CDTOC *toc = NULL;
int
main(int argc, char **argv)
{
struct iso_args args;
int ch, mntflags, opts;
char *dev, dir[MAXPATHLEN];
int altflg;
mntflags = opts = 0;
memset(&args, 0, sizeof args);
args.ssector = -1;
while ((ch = getopt(argc, argv, "egjo:rs:")) != EOF)
switch (ch) {
case 'e':
opts |= ISOFSMNT_EXTATT;
break;
case 'g':
opts |= ISOFSMNT_GENS;
break;
case 'j':
opts |= ISOFSMNT_NOJOLIET;
break;
case 'o':
getmntopts(optarg, mopts, &mntflags, &altflg);
break;
case 'r':
opts |= ISOFSMNT_NORRIP;
break;
case 's':
args.ssector = atoi(optarg);
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
if (argc != 2)
usage();
dev = argv[0];
if (realpath(argv[1], dir) == NULL)
err(1, "realpath %s", dir);
mntflags |= MNT_RDONLY;
args.fspec = dev;
args.flags = opts;
toc = (struct CDTOC *) get_cdtoc(dev);
if (args.ssector == -1)
args.ssector = get_ssector(dev);
if (toc) {
toc->length = OSSwapBigToHostInt16 ( toc->length );
args.toc_length = toc->length + sizeof(toc->length);
args.toc = toc;
args.flags |= ISOFSMNT_TOC;
}
if (mount("cd9660", dir, mntflags, &args) < 0)
err(1, NULL);
if (toc)
free(toc);
exit(0);
}
void
usage()
{
(void)fprintf(stderr,
"usage: mount_cd9660 [-egjr] [-o options] [-s startsector] special node\n");
exit(1);
}
static int
get_ssector(char *devpath)
{
struct CDTOC_Desc *toc_desc;
struct ISOVolumeDescriptor *isovdp;
char iobuf[CDROM_BLOCK_SIZE];
int cmpsize = sizeof(isovdp->id);
int i, count;
int ssector;
u_char track;
int devfd;
ssector = 0;
isovdp = (struct ISOVolumeDescriptor *)iobuf;
if (toc == NULL)
goto exit;
devfd = open(devpath, O_RDONLY | O_NDELAY , 0);
if (devfd <= 0)
goto exit;
count = (toc->length - 2) / sizeof(struct CDTOC_Desc);
toc_desc = toc->trackdesc;
for (i = count - 1; i >= 0; i--) {
track = toc_desc[i].point;
if (track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO)
continue;
if ((toc_desc[i].ctrl_adr >> 4) != CD_CURRENT_POSITION)
continue;
if (toc_desc[i].ctrl_adr & CD_CTRL_DATA) {
int sector;
sector = MSF_TO_LBA(toc_desc[i].p);
if (sector == 0)
break;
lseek(devfd, ((16 + sector) * CDROM_BLOCK_SIZE), 0);
if (read(devfd, iobuf, CDROM_BLOCK_SIZE) != CDROM_BLOCK_SIZE) {
if (errno == EIO) {
int rawfd;
ssize_t readlen;
char rawname[32];
char *dp;
if ((dp = strrchr(devpath, '/')) == 0)
continue;
sprintf(rawname, "/dev/r%s", dp + 1);
rawfd = open(rawname, O_RDONLY | O_NDELAY , 0);
if (rawfd <= 0)
continue;
lseek(rawfd, ((16 + sector) * CDROM_BLOCK_SIZE), 0);
readlen = read(rawfd, iobuf, CDROM_BLOCK_SIZE);
close(rawfd);
if (readlen != CDROM_BLOCK_SIZE)
continue;
} else {
continue;
}
}
if ((memcmp(&isovdp->id[0], ISO_STANDARD_ID, cmpsize) == 0)
&& (isovdp->type[0] == ISO_VD_PRIMARY)) {
ssector = sector;
break;
}
}
}
close(devfd);
exit:
return ssector;
}
static u_char *
get_cdtoc(char * devpath)
{
u_char * result;
io_registry_entry_t service;
io_registry_entry_t parent;
CFDataRef data;
char * devname;
service = 0;
parent = 0;
data = 0;
result = NULL;
if ((devname = strrchr(devpath, '/')) != NULL)
++devname;
else
devname = devpath;
if (*devname == 'r')
++devname;
service = IOServiceGetMatchingService(kIOMasterPortDefault,
IOBSDNameMatching(kIOMasterPortDefault,0,devname));
while (service && !IOObjectConformsTo(service, kIOCDMediaClass)) {
if (IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent))
goto Exit;
IOObjectRelease(service);
service = parent;
parent = 0;
}
if (service == IO_OBJECT_NULL)
goto Exit;
data = IORegistryEntryCreateCFProperty(
service,
CFSTR(kIOCDMediaTOCKey),
kCFAllocatorDefault,
0);
if (data != NULL) {
result = CreateBufferFromCFData(data);
CFRelease(data);
}
Exit:
if (service)
(void) IOObjectRelease(service);
return result;
}
static u_char *
CreateBufferFromCFData(CFDataRef cfdata)
{
CFRange range;
CFIndex buflen;
u_char * bufptr;
buflen = CFDataGetLength(cfdata) + 1;
range = CFRangeMake(0, buflen);
bufptr = (u_char *) malloc(buflen);
if (bufptr != NULL)
CFDataGetBytes(cfdata, range, bufptr);
return bufptr;
}