#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/loadable_fs.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <dev/disk.h>
#include <machine/byte_order.h>
#include <CoreFoundation/CFString.h>
#define fromLE16 NXSwapLittleShortToHost
#define fromLE32 NXSwapLittleIntToHost
#define FS_TYPE "udf"
#define FS_NAME_FILE "UDF"
#define DEV_PREFIX "/dev/"
#define RAWDEV_PREFIX "/dev/r"
#define MOUNT_COMMAND "/sbin/mount"
#define UMOUNT_COMMAND "/sbin/umount"
#define KEXTLOAD_COMMAND "/sbin/kextload"
#define FS_KEXT_DIR "/System/Library/Extensions/udf.kext"
#define ISO_BLOCKSIZE 2048
#define VOL_ID_OFFSET 24
#define VOL_ID_LENGTH 32
#define OSTA_COMPRESSED 8
#define OSTA_UNCOMPRESSED 16
#define MAX_LABEL 136
const char *progname;
int debug;
static void
usage()
{
fprintf(stderr, "usage: %s action_arg device_arg [mount_point_arg]\n",
progname);
fprintf(stderr, "action_arg:\n");
fprintf(stderr, " -%c (Probe)\n", FSUC_PROBE);
fprintf(stderr, " -%c (Mount)\n", FSUC_MOUNT);
fprintf(stderr, " -%c (Unmount)\n", FSUC_UNMOUNT);
fprintf(stderr, "Example:\n");
fprintf(stderr, " %s.util -%c disk2 /mnt\n", FS_TYPE, FSUC_MOUNT);
exit(FSUR_INVAL);
}
int
safe_open(char *path, int flags, mode_t mode)
{
int fd = open(path, flags, mode);
if (fd < 0) {
fprintf(stderr, "%s: open %s failed, %s\n", progname, path,
strerror(errno));
exit(FSUR_IO_FAIL);
}
return(fd);
}
void
safe_close(int fd)
{
if (close(fd)) {
fprintf(stderr, "%s: close failed, %s\n", progname,
strerror(errno));
exit(FSUR_IO_FAIL);
}
}
void
safe_write(int fd, char *data, int len)
{
if (write(fd, data, len) != len) {
fprintf(stderr, "%s: write failed, %s\n", progname,
strerror(errno));
exit(FSUR_IO_FAIL);
}
}
void
safe_execv(char *args[])
{
int pid;
union wait status;
pid = fork();
if (pid == 0) {
(void)execv(args[0], args);
fprintf(stderr, "%s: execv %s failed, %s\n", progname, args[0],
strerror(errno));
exit(FSUR_IO_FAIL);
}
if (pid == -1) {
fprintf(stderr, "%s: fork failed, %s\n", progname,
strerror(errno));
exit(FSUR_IO_FAIL);
}
if (wait4(pid, (int *)&status, 0, NULL) != pid) {
fprintf(stderr, "%s: BUG executing %s command\n", progname,
args[0]);
exit(FSUR_IO_FAIL);
} else if (!WIFEXITED(status)) {
fprintf(stderr, "%s: %s command aborted by signal %d\n",
progname, args[0], WTERMSIG(status));
exit(FSUR_IO_FAIL);
} else if (WEXITSTATUS(status)) {
fprintf(stderr, "%s: %s command failed, exit status %d: %s\n",
progname, args[0], WEXITSTATUS(status),
strerror(WEXITSTATUS(status)));
exit(FSUR_IO_FAIL);
}
}
void
safe_unlink(char *path)
{
if (unlink(path) && errno != ENOENT) {
fprintf(stderr, "%s: unlink %s failed, %s\n", progname, path,
strerror(errno));
exit(FSUR_IO_FAIL);
}
}
void
safe_read(int fd, char *buf, int nbytes, off_t off)
{
if (lseek(fd, off, SEEK_SET) == -1) {
fprintf(stderr, "%s: device seek error @ %qu, %s\n", progname,
off, strerror(errno));
exit(FSUR_IO_FAIL);
}
if (read(fd, buf, nbytes) != nbytes) {
if (debug)
fprintf(stderr, "%s: device read error @ %qu, %s\n",
progname, off, strerror(errno));
exit(FSUR_IO_FAIL);
}
}
int
bsum(unsigned char *s, unsigned len)
{
int sum = 0;
while (len--)
sum += *s++;
return(sum);
}
void
consumeIllegal( char **name, int * maxLength )
{
char *s = *name;
int len = *maxLength;
while ( len ) {
if ((*s != '/') && (*s != '\0'))
break;
s++; len--;
}
*name = s; *maxLength = len;
}
int
validateLength( char *bytes, int fieldLen )
{
char *end = &bytes[fieldLen - 1];
int length = *end;
if ( length == 0 ) {
bytes++; while ( end != bytes ) {
if (*end != '\0') break;
end--;
}
length = end + 1 - bytes;
}
else length -= 1;
return length;
}
int
label_get(int fd, unsigned sectorsize, unsigned sector, char *label)
{
unsigned char *buf = malloc(sectorsize);
int found = 0;
CFStringRef volumeName;
char *defaultName = "Unknown";
char *src, *dst;
char theChar;
int namelength;
if (debug) printf("looking for AVDP @ sector %d\n", sector);
if (!buf) {
fprintf(stderr, "%s: malloc %d failed\n", progname, sectorsize);
exit(FSUR_IO_FAIL);
}
safe_read(fd, buf, sectorsize, (off_t)(sector * sectorsize));
if (debug) {
unsigned char *bp = buf;
while (bp < buf+16)
printf("%.2x ", *bp++);
printf("\n");
}
if (fromLE16(*(unsigned short *)buf) == 2 &&
buf[4] == (bsum(buf, 4) + bsum(buf+5, 11)) % 256) {
unsigned len = fromLE32(*(unsigned *)(buf+16));
unsigned loc = fromLE32(*(unsigned *)(buf+20));
if (debug) printf("VDS %d long at sector %d\n", len, loc);
for (; len >= sectorsize; len -= sectorsize, loc++) {
safe_read(fd, buf, sectorsize,
(off_t)(loc * sectorsize));
if (fromLE16(*(unsigned short *)buf) != 1)
continue;
if (debug) {
unsigned char *bp = buf+24;
printf("Volume id, hex (buf[24]-buf[55])\n\t");
while (bp < buf+56)
printf("%.2x ", *bp++);
printf("\n");
printf("Volume id, interpreted:\n\t");
bp = buf+24;
printf("CS:%d STR:", *bp++);
while (bp < buf+55)
printf("%c", *bp++);
printf(" LEN:%d\n", *bp);
}
namelength = validateLength( &buf[VOL_ID_OFFSET], VOL_ID_LENGTH );
if ( buf[VOL_ID_OFFSET] == OSTA_UNCOMPRESSED ) {
volumeName = CFStringCreateWithCharacters(NULL, (UniChar*) &buf[VOL_ID_OFFSET+1], namelength/2 );
if ( volumeName != NULL ) {
(void) CFStringGetCString(volumeName, label, MAX_LABEL-1, kCFStringEncodingUTF8);
CFRelease(volumeName);
namelength = strlen(label);
}
}
else if ( buf[VOL_ID_OFFSET] == OSTA_COMPRESSED ) {
memcpy(label, &buf[VOL_ID_OFFSET+1], namelength);
label[namelength] = '\0';
}
else {
namelength = strlen(defaultName);
strcpy(label, defaultName);
}
src = dst = label;
while ( namelength-- > 0 ) {
theChar = *src++;
if ( (theChar == '/') || (theChar == '\0') ) {
consumeIllegal(&src, &namelength);
theChar = '_';
}
*(dst++) = theChar;
}
*dst = '\0';
found = 1;
break;
}
}
free(buf);
return(found);
}
void
safe_ioctl(int fd, unsigned req, char *argp, char *reqstr)
{
if (ioctl(fd, req, argp) < 0) {
fprintf(stderr, "%s: %s failed, %s\n", progname, reqstr,
strerror(errno));
exit(FSUR_IO_FAIL);
}
}
int
main(int argc, const char *argv[])
{
char devpath[MAXPATHLEN];
char opt;
struct stat sb;
int ret = FSUR_INVAL;
progname = argv[0];
argc--;
argv++;
debug = (argc > 0 && !strcmp(argv[0], "-D"));
if (debug) {
argc--;
argv++;
}
if (argc < 2 || argv[0][0] != '-')
usage();
opt = argv[0][1];
if (opt != FSUC_PROBE && opt != FSUC_MOUNT && opt != FSUC_UNMOUNT)
usage();
if ((opt == FSUC_MOUNT || opt == FSUC_UNMOUNT) && argc < 3)
usage();
sprintf(devpath, "%s%s", RAWDEV_PREFIX, argv[1]);
if (stat(devpath, &sb) != 0) {
fprintf(stderr, "%s: stat %s failed, %s\n", progname, devpath,
strerror(errno));
exit(FSUR_INVAL);
}
switch (opt) {
case FSUC_PROBE: {
unsigned char filename[MAXPATHLEN];
int fd, n;
unsigned char label[MAX_LABEL];
unsigned char buf[ISO_BLOCKSIZE];
unsigned numsectors, sectorsize;
sprintf(filename, "%s/%s%s/%s.label", FS_DIR_LOCATION,
FS_TYPE, FS_DIR_SUFFIX, FS_TYPE);
safe_unlink(filename);
sprintf(filename, "%s/%s%s/%s.name", FS_DIR_LOCATION,
FS_TYPE, FS_DIR_SUFFIX, FS_TYPE);
safe_unlink(filename);
fd = safe_open(devpath, O_RDONLY, 0);
for (n = 16; n < 32; n++) {
safe_read(fd, buf, ISO_BLOCKSIZE,
(off_t)(n * ISO_BLOCKSIZE));
if (buf[0] == '\0' && !memcmp(&buf[1], "NSR", 3))
break;
}
if (n >= 32)
exit(FSUR_UNRECOGNIZED);
if (debug) printf("found NSR @ sector %d\n", n);
safe_ioctl(fd, DKIOCBLKSIZE, (char *)§orsize,
"DKIOCBLKSIZE");
safe_ioctl(fd, DKIOCNUMBLKS, (char *)&numsectors,
"DKIOCNUMBLKS");
if (debug) printf("sectorsize = %d, numsectors = %d\n",
sectorsize, numsectors);
if (!label_get(fd, sectorsize, 256, label) &&
!label_get(fd, sectorsize, numsectors-256, label) &&
!label_get(fd, sectorsize, numsectors-1, label)) {
fprintf(stderr, "%s: no volume descriptor found using native %d byte sector size.\n",
progname, sectorsize);
if (sectorsize != ISO_BLOCKSIZE) {
fprintf(stderr, "%s: trying %d logical sector size.\n",
progname, ISO_BLOCKSIZE);
numsectors = ((off_t)(sectorsize * numsectors)) / ISO_BLOCKSIZE;
sectorsize = ISO_BLOCKSIZE;
if (!label_get(fd, sectorsize, 256, label) &&
!label_get(fd, sectorsize, numsectors-256, label) &&
!label_get(fd, sectorsize, numsectors-1, label)) {
fprintf (stderr, "%s: no volume descriptor found using simulated sector size!\n",
progname);
exit(FSUR_UNRECOGNIZED);
}
} else exit(FSUR_UNRECOGNIZED);
}
safe_close(fd);
sprintf(filename, "%s/%s%s/%s.label", FS_DIR_LOCATION,
FS_TYPE, FS_DIR_SUFFIX, FS_TYPE);
fd = safe_open(filename, O_WRONLY|O_CREAT|O_EXCL, 0755);
safe_write(fd, label, strlen(label) + 1);
safe_close(fd);
sprintf(filename, "%s/%s%s/%s.name", FS_DIR_LOCATION,
FS_TYPE, FS_DIR_SUFFIX, FS_TYPE);
fd = safe_open(filename, O_WRONLY|O_CREAT|O_EXCL, 0755);
safe_write(fd, FS_NAME_FILE, 1 + strlen(FS_NAME_FILE));
safe_close(fd);
ret = FSUR_RECOGNIZED;
break;
}
case FSUC_MOUNT: {
const char *kextargs[] = {KEXTLOAD_COMMAND, FS_KEXT_DIR, NULL};
const char *mountargs[11];
mountargs[0] = MOUNT_COMMAND;
mountargs[1] = "-t";
mountargs[2] = FS_TYPE;
mountargs[3] = "-r";
if (argc >= 4 && !strcmp(argv[4], DEVICE_WRITABLE))
mountargs[3] = "-w";
mountargs[4] = "-o";
mountargs[5] = "nosuid";
mountargs[6] = "-o";
mountargs[7] = "nodev";
mountargs[8] = devpath;
mountargs[9] = argv[2];
mountargs[10] = NULL;
sprintf(devpath, "%s%s", DEV_PREFIX, argv[1]);
safe_execv(kextargs);
safe_execv(mountargs);
ret = FSUR_IO_SUCCESS;
break;
}
case FSUC_UNMOUNT: {
const char *umountargs[] = {UMOUNT_COMMAND, argv[2], NULL};
safe_execv(umountargs);
ret = FSUR_IO_SUCCESS;
break;
}
default:
exit(FSUR_INVAL);
break;
}
exit(ret);
return(ret);
}