Blame src/cdinfo-linux.c

Packit dd8086
/*
Packit dd8086
  $Id: cdinfo-linux.c,v 1.4 2008/04/14 17:30:27 karl Exp $
Packit dd8086
Packit dd8086
  Copyright (C) 2003,2008 Rocky Bernstein <rocky@gnu.org>
Packit dd8086
  Copyright (C) 1996,1997,1998  Gerd Knorr <kraxel@bytesex.org>
Packit dd8086
        and Heiko Eißfeldt <heiko@hexco.de>
Packit dd8086
Packit dd8086
  This program is free software: you can redistribute it and/or modify
Packit dd8086
  it under the terms of the GNU General Public License as published by
Packit dd8086
  the Free Software Foundation, either version 3 of the License, or
Packit dd8086
  (at your option) any later version.
Packit dd8086
Packit dd8086
  This program is distributed in the hope that it will be useful,
Packit dd8086
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit dd8086
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit dd8086
  GNU General Public License for more details.
Packit dd8086
Packit dd8086
  You should have received a copy of the GNU General Public License
Packit dd8086
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit dd8086
*/
Packit dd8086
/*
Packit dd8086
  CD Info - prints various information about a CD, and detects the type of 
Packit dd8086
  the CD.
Packit dd8086
 
Packit dd8086
  usage: cdinfo  [options] [ dev ]
Packit dd8086
 
Packit dd8086
*/
Packit dd8086
#define PROGRAM_NAME "CD Info"
Packit dd8086
#define CDINFO_VERSION "2.0"
Packit dd8086
Packit dd8086
#include "config.h"
Packit dd8086
#include <cdio/cdio.h>
Packit dd8086
#include <cdio/logging.h>
Packit dd8086
#include <cdio/util.h>
Packit dd8086
#include <cdio/cd_types.h>
Packit dd8086
#include <cdio/sector.h>
Packit dd8086
Packit dd8086
#include <stdio.h>
Packit dd8086
#include <stdlib.h>
Packit dd8086
#include <unistd.h>
Packit dd8086
#include <string.h>
Packit dd8086
#include <fcntl.h>
Packit dd8086
#include <sys/ioctl.h>
Packit dd8086
#ifdef __linux__
Packit dd8086
# include <linux/version.h>
Packit dd8086
# include <linux/cdrom.h>
Packit dd8086
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,50)
Packit dd8086
#  include <linux/ucdrom.h>
Packit dd8086
# endif
Packit dd8086
#endif
Packit dd8086
 
Packit dd8086
#include <sys/ioctl.h>
Packit dd8086
#include <errno.h>
Packit dd8086
#include <argp.h>
Packit dd8086
Packit dd8086
#ifdef ENABLE_NLS
Packit dd8086
#include <locale.h>
Packit dd8086
#    include <libintl.h>
Packit dd8086
#    define _(String) dgettext ("cdinfo", String)
Packit dd8086
#else
Packit dd8086
/* Stubs that do something close enough.  */
Packit dd8086
#    define _(String) (String)
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/* The following test is to work around the gross typo in
Packit dd8086
   systems like Sony NEWS-OS Release 4.0C, whereby EXIT_FAILURE
Packit dd8086
   is defined to 0, not 1.  */
Packit dd8086
#if !EXIT_FAILURE
Packit dd8086
# undef EXIT_FAILURE
Packit dd8086
# define EXIT_FAILURE 1
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifndef EXIT_SUCCESS
Packit dd8086
# define EXIT_SUCCESS 0
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/* Used by `main' to communicate with `parse_opt'. And global options
Packit dd8086
 */
Packit dd8086
struct arguments
Packit dd8086
{
Packit dd8086
  bool show_tracks;
Packit dd8086
  bool show_ioctl;
Packit dd8086
  bool show_analysis;
Packit dd8086
  int debug_level;
Packit dd8086
  bool silent;
Packit dd8086
} opts;
Packit dd8086
     
Packit dd8086
#define DEBUG 1
Packit dd8086
#if DEBUG
Packit dd8086
#define dbg_print(level, s, args...) \
Packit dd8086
   if (opts.debug_level >= level) \
Packit dd8086
     fprintf(stderr, "%s: "s, __func__ , ##args)
Packit dd8086
#else
Packit dd8086
#define dbg_print(level, s, args...) 
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#define err_exit(fmt, args...) \
Packit dd8086
  fprintf(stderr, "%s: "fmt, program_name, ##args); \
Packit dd8086
  myexit(EXIT_FAILURE)		     
Packit dd8086
  
Packit dd8086
/*
Packit dd8086
Subject:   -65- How can I read an IRIX (EFS) CD-ROM on a machine which
Packit dd8086
                doesn't use EFS?
Packit dd8086
Date: 18 Jun 1995 00:00:01 EST
Packit dd8086
Packit dd8086
  You want 'efslook', at
Packit dd8086
  ftp://viz.tamu.edu/pub/sgi/software/efslook.tar.gz.
Packit dd8086
Packit dd8086
and
Packit dd8086
! Robert E. Seastrom <rs@access4.digex.net>'s software (with source
Packit dd8086
! code) for using an SGI CD-ROM on a Macintosh is at
Packit dd8086
! ftp://bifrost.seastrom.com/pub/mac/CDROM-Jumpstart.sit151.hqx.
Packit dd8086
Packit dd8086
*/
Packit dd8086
Packit dd8086
#define FS_NO_DATA              0   /* audio only */
Packit dd8086
#define FS_HIGH_SIERRA		1
Packit dd8086
#define FS_ISO_9660		2
Packit dd8086
#define FS_INTERACTIVE		3
Packit dd8086
#define FS_HFS			4
Packit dd8086
#define FS_UFS			5
Packit dd8086
#define FS_EXT2			6
Packit dd8086
#define FS_ISO_HFS              7  /* both hfs & isofs filesystem */
Packit dd8086
#define FS_ISO_9660_INTERACTIVE 8  /* both CD-RTOS and isofs filesystem */
Packit dd8086
#define FS_3DO			9
Packit dd8086
#define FS_UNKNOWN	       15
Packit dd8086
#define FS_MASK		       15
Packit dd8086
Packit dd8086
#define XA		       16
Packit dd8086
#define MULTISESSION	       32
Packit dd8086
#define PHOTO_CD	       64
Packit dd8086
#define HIDDEN_TRACK          128
Packit dd8086
#define CDTV		      256
Packit dd8086
#define BOOTABLE       	      512
Packit dd8086
#define VIDEOCDI       	     1024
Packit dd8086
#define ROCKRIDGE            2048
Packit dd8086
#define JOLIET               4096
Packit dd8086
Packit dd8086
/* Some interesting sector numbers. */
Packit dd8086
#define ISO_SUPERBLOCK_SECTOR  16
Packit dd8086
#define UFS_SUPERBLOCK_SECTOR   4
Packit dd8086
#define BOOT_SECTOR            17
Packit dd8086
#define VCD_INFO_SECTOR       150
Packit dd8086
Packit dd8086
#if 0
Packit dd8086
#define STRONG "\033[1m"
Packit dd8086
#define NORMAL "\033[0m"
Packit dd8086
#else
Packit dd8086
#define STRONG "__________________________________\n"
Packit dd8086
#define NORMAL ""
Packit dd8086
#endif
Packit dd8086
Packit dd8086
typedef struct signature
Packit dd8086
{
Packit dd8086
  unsigned int buf_num;
Packit dd8086
  unsigned int offset;
Packit dd8086
  const char *sig_str;
Packit dd8086
  const char *description;
Packit dd8086
} signature_t;
Packit dd8086
Packit dd8086
#define IS_ISOFS     0
Packit dd8086
#define IS_CD_I      1
Packit dd8086
#define IS_CDTV      2
Packit dd8086
#define IS_CD_RTOS   3
Packit dd8086
#define IS_HS        4
Packit dd8086
#define IS_BRIDGE    5
Packit dd8086
#define IS_XA        6
Packit dd8086
#define IS_PHOTO_CD  7
Packit dd8086
#define IS_EXT2      8
Packit dd8086
#define IS_UFS       9
Packit dd8086
#define IS_BOOTABLE 10
Packit dd8086
#define IS_VIDEO_CD 11
Packit dd8086
Packit dd8086
static signature_t sigs[] =
Packit dd8086
  {
Packit dd8086
  /* Buff off  look for     description */
Packit dd8086
    {0,     1, "CD001",     "ISO 9660"}, 
Packit dd8086
    {0,     1, "CD-I",      "CD-I"}, 
Packit dd8086
    {0,     8, "CDTV",      "CDTV"}, 
Packit dd8086
    {0,     8, "CD-RTOS",   "CD-RTOS"}, 
Packit dd8086
    {0,     9, "CDROM",     "HIGH SIERRA"}, 
Packit dd8086
    {0,    16, "CD-BRIDGE", "BRIDGE"}, 
Packit dd8086
    {0,  1024, "CD-XA001",  "XA"}, 
Packit dd8086
    {1,    64, "PPPPHHHHOOOOTTTTOOOO____CCCCDDDD",  "PHOTO CD"}, 
Packit dd8086
    {1, 0x438, "\x53\xef",  "EXT2 FS"}, 
Packit dd8086
    {2,  1372, "\x54\x19\x01\x0", "UFS"}, 
Packit dd8086
    {3,     7, "EL TORITO", "BOOTABLE"}, 
Packit dd8086
    {4,     0, "VIDEO_CD",  "VIDEO CD"}, 
Packit dd8086
    { 0 }
Packit dd8086
  };
Packit dd8086
Packit dd8086
int filehandle;                                   /* Handle of /dev/>cdrom< */
Packit dd8086
int rc;                                                      /* return code */
Packit dd8086
int i,j;                                                           /* index */
Packit dd8086
int isofs_size = 0;                                      /* size of session */
Packit dd8086
int start_track;                                   /* first sector of track */
Packit dd8086
int ms_offset;                /* multisession offset found by track-walking */
Packit dd8086
int data_start;                                       /* start of data area */
Packit dd8086
int joliet_level = 0;
Packit dd8086
Packit dd8086
char buffer[6][CDIO_CD_FRAMESIZE_RAW];  /* for CD-Data */
Packit dd8086
Packit dd8086
CdIo *img; 
Packit dd8086
track_t num_tracks;
Packit dd8086
track_t first_track_num;
Packit dd8086
Packit dd8086
struct cdrom_tocentry      *toc[CDIO_CDROM_LEADOUT_TRACK+1];  /* TOC-entries */
Packit dd8086
struct cdrom_mcn           mcn;
Packit dd8086
struct cdrom_multisession  ms;
Packit dd8086
struct cdrom_subchnl       sub;
Packit dd8086
int                        first_data = -1;        /* # of first data track */
Packit dd8086
int                        num_data = 0;                /* # of data tracks */
Packit dd8086
int                        first_audio = -1;      /* # of first audio track */
Packit dd8086
int                        num_audio = 0;              /* # of audio tracks */
Packit dd8086
Packit dd8086
Packit dd8086
char *devname = NULL;
Packit dd8086
char *program_name;
Packit dd8086
Packit dd8086
const char *argp_program_version     = PROGRAM_NAME CDINFO_VERSION;
Packit dd8086
const char *argp_program_bug_address = "rocky@gnu.org";
Packit dd8086
Packit dd8086
/* Program documentation. */
Packit dd8086
const char doc[] = 
Packit dd8086
  PROGRAM_NAME " -- Get information about a Compact Disk or CD image.";
Packit dd8086
Packit dd8086
/* A description of the arguments we accept. */
Packit dd8086
const char args_doc[] = "[DEVICE or DISK-IMAGE]";
Packit dd8086
Packit dd8086
static struct argp_option options[] =
Packit dd8086
{
Packit dd8086
  {"debug",    'd', "LEVEL", 0, "Set debugging to LEVEL"},
Packit dd8086
  {"quiet",    'q', 0,       0,  "Don't produce any output" },
Packit dd8086
  {"silent",   's', 0,       OPTION_ALIAS },
Packit dd8086
  {"notracks", 'T', 0,       0, "Don't show track information"},
Packit dd8086
  {"noanalyze",'A', 0,       0, "Don't filesystem analysis"},
Packit dd8086
  {"noioctl",  'I', 0,       0, "Don't show ioctl() information"},
Packit dd8086
  { 0 }
Packit dd8086
};
Packit dd8086
Packit dd8086
/* Parse a single option. */
Packit dd8086
static error_t
Packit dd8086
parse_opt (int key, char *arg, struct argp_state *state)
Packit dd8086
{
Packit dd8086
  /* Get the INPUT argument from `argp_parse', which we
Packit dd8086
     know is a pointer to our arguments structure. */
Packit dd8086
  struct arguments *arguments = state->input;
Packit dd8086
  
Packit dd8086
  switch (key)
Packit dd8086
    {
Packit dd8086
    case 'q': case 's':
Packit dd8086
      arguments->silent = 1;
Packit dd8086
      break;
Packit dd8086
    case 'd':
Packit dd8086
      /* Default debug level is 1. */
Packit dd8086
      arguments->debug_level = arg ? atol(arg): 1;
Packit dd8086
      break;
Packit dd8086
    case 'I':
Packit dd8086
      arguments->show_ioctl  = false;
Packit dd8086
      break;
Packit dd8086
    case 'T':
Packit dd8086
      arguments->show_tracks = false;
Packit dd8086
      break;
Packit dd8086
    case 'A':
Packit dd8086
      arguments->show_analysis = false;
Packit dd8086
      break;
Packit dd8086
    case ARGP_KEY_ARG:
Packit dd8086
      /* Let the next case parse it.  */
Packit dd8086
      return ARGP_ERR_UNKNOWN;
Packit dd8086
      break;
Packit dd8086
Packit dd8086
    case ARGP_KEY_ARGS:
Packit dd8086
      {
Packit dd8086
	/* Check that only one device given. If so, handle it. */
Packit dd8086
	unsigned int num_remaining_args = state->argc - state->next;
Packit dd8086
	char **remaining_args = state->argv + state->next;
Packit dd8086
	if (num_remaining_args > 1) {
Packit dd8086
	  argp_usage (state);
Packit dd8086
	}
Packit dd8086
	if (0 == strncmp(remaining_args[0],"/dev/",5))
Packit dd8086
	  devname = remaining_args[0];
Packit dd8086
	else {
Packit dd8086
	  devname=malloc(6+strlen(remaining_args[0]));
Packit dd8086
	  sprintf(devname,"/dev/%s", remaining_args[0]);
Packit dd8086
	}
Packit dd8086
      }
Packit dd8086
      break;
Packit dd8086
Packit dd8086
    default:
Packit dd8086
      return ARGP_ERR_UNKNOWN;
Packit dd8086
    }
Packit dd8086
  return 0;
Packit dd8086
}
Packit dd8086
     
Packit dd8086
static void
Packit dd8086
print_version (void)
Packit dd8086
{
Packit dd8086
  printf( _("CD Info %s | (c) 2003 Gerd Knorr, Heiko Eißfeldt & R. Bernstein\n\
Packit dd8086
This is free software; see the source for copying conditions.\n\
Packit dd8086
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n\
Packit dd8086
PARTICULAR PURPOSE.\n\
Packit dd8086
"),
Packit dd8086
	 CDINFO_VERSION);
Packit dd8086
}
Packit dd8086
Packit dd8086
/* ------------------------------------------------------------------------ */
Packit dd8086
/* some ISO 9660 fiddling                                                   */
Packit dd8086
Packit dd8086
static int 
Packit dd8086
read_block(int superblock, uint32_t offset, uint8_t bufnum, bool is_green)
Packit dd8086
{
Packit dd8086
  memset(buffer[bufnum], 0, CDIO_CD_FRAMESIZE);
Packit dd8086
  
Packit dd8086
  dbg_print(2, "about to read sector %u\n", offset+superblock);
Packit dd8086
  if (cdio_read_mode2_sector(img, buffer[bufnum],
Packit dd8086
			     offset+superblock, !is_green))
Packit dd8086
    return -1;
Packit dd8086
Packit dd8086
Packit dd8086
  /* For now compare with what we get the old way.... */
Packit dd8086
  if (0 > lseek(filehandle, CDIO_CD_FRAMESIZE*(offset+superblock), SEEK_SET))
Packit dd8086
    return -1;
Packit dd8086
  
Packit dd8086
  memset(buffer[5],0,CDIO_CD_FRAMESIZE);
Packit dd8086
  if (0 > read(filehandle,buffer[5], CDIO_CD_FRAMESIZE))
Packit dd8086
    return -1;
Packit dd8086
Packit dd8086
  if (memcmp(buffer[bufnum], buffer[5], CDIO_CD_FRAMESIZE) != 0) {
Packit dd8086
    dbg_print(0, 
Packit dd8086
	      "libcdio conversion problem in reading super, buf %d\n",
Packit dd8086
	      bufnum);
Packit dd8086
  }
Packit dd8086
  
Packit dd8086
  return 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
static bool 
Packit dd8086
is_it(int num) 
Packit dd8086
{
Packit dd8086
  signature_t *sigp;
Packit dd8086
Packit dd8086
  /* TODO: check that num < largest sig. */
Packit dd8086
  sigp = &sigs[num];
Packit dd8086
Packit dd8086
  int len = strlen(sigp->sig_str);
Packit dd8086
  return 0 == memcmp(&buffer[sigp->buf_num][sigp->offset], 
Packit dd8086
		     sigp->sig_str, len);
Packit dd8086
}
Packit dd8086
Packit dd8086
static int 
Packit dd8086
is_hfs(void)
Packit dd8086
{
Packit dd8086
  return (0 == memcmp(&buffer[1][512],"PM",2)) ||
Packit dd8086
    (0 == memcmp(&buffer[1][512],"TS",2)) ||
Packit dd8086
    (0 == memcmp(&buffer[1][1024], "BD",2));
Packit dd8086
}
Packit dd8086
Packit dd8086
static int 
Packit dd8086
is_3do(void)
Packit dd8086
{
Packit dd8086
  return (0 == memcmp(&buffer[1][0],"\x01\x5a\x5a\x5a\x5a\x5a\x01", 7)) &&
Packit dd8086
    (0 == memcmp(&buffer[1][40], "CD-ROM", 6));
Packit dd8086
}
Packit dd8086
Packit dd8086
static int is_joliet(void)
Packit dd8086
{
Packit dd8086
  return 2 == buffer[3][0] && buffer[3][88] == 0x25 && buffer[3][89] == 0x2f;
Packit dd8086
}
Packit dd8086
Packit dd8086
/* ISO 9660 volume space in M2F1_SECTOR_SIZE byte units */
Packit dd8086
static int 
Packit dd8086
get_size(void)
Packit dd8086
{
Packit dd8086
  return ((buffer[0][80] & 0xff) |
Packit dd8086
	  ((buffer[0][81] & 0xff) << 8) |
Packit dd8086
	  ((buffer[0][82] & 0xff) << 16) |
Packit dd8086
	  ((buffer[0][83] & 0xff) << 24));
Packit dd8086
}
Packit dd8086
Packit dd8086
static int 
Packit dd8086
get_joliet_level( void )
Packit dd8086
{
Packit dd8086
  switch (buffer[3][90]) {
Packit dd8086
  case 0x40: return 1;
Packit dd8086
  case 0x43: return 2;
Packit dd8086
  case 0x45: return 3;
Packit dd8086
  }
Packit dd8086
  return 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
#define is_it_dbg(sig) \
Packit dd8086
    if (is_it(sig)) printf("%s, ", sigs[sig].description)
Packit dd8086
Packit dd8086
static int 
Packit dd8086
guess_filesystem(int start_session, bool is_green)
Packit dd8086
{
Packit dd8086
  int ret = 0;
Packit dd8086
  
Packit dd8086
  if (read_block(ISO_SUPERBLOCK_SECTOR, start_session, 0, is_green) < 0)
Packit dd8086
    return FS_UNKNOWN;
Packit dd8086
  
Packit dd8086
  if (opts.debug_level > 0) {
Packit dd8086
    /* buffer is defined */
Packit dd8086
    is_it_dbg(IS_CD_I);
Packit dd8086
    is_it_dbg(IS_CD_RTOS);
Packit dd8086
    is_it_dbg(IS_ISOFS);
Packit dd8086
    is_it_dbg(IS_HS);
Packit dd8086
    is_it_dbg(IS_BRIDGE);
Packit dd8086
    is_it_dbg(IS_XA);
Packit dd8086
    is_it_dbg(IS_CDTV);
Packit dd8086
    puts("");
Packit dd8086
  }
Packit dd8086
  
Packit dd8086
  /* filesystem */
Packit dd8086
  if (is_it(IS_CD_I) && is_it(IS_CD_RTOS) 
Packit dd8086
      && !is_it(IS_BRIDGE) && !is_it(IS_XA)) {
Packit dd8086
    return FS_INTERACTIVE;
Packit dd8086
  } else {	/* read sector 0 ONLY, when NO greenbook CD-I !!!! */
Packit dd8086
Packit dd8086
    if (read_block(0, start_session, 1, true) < 0)
Packit dd8086
      return ret;
Packit dd8086
    
Packit dd8086
    if (opts.debug_level > 0) {
Packit dd8086
      /* buffer[1] is defined */
Packit dd8086
      is_it_dbg(IS_PHOTO_CD);
Packit dd8086
      if (is_hfs()) printf("HFS, ");
Packit dd8086
      is_it_dbg(IS_EXT2);
Packit dd8086
      if (is_3do()) printf("3DO, ");
Packit dd8086
      puts("");
Packit dd8086
    }
Packit dd8086
    
Packit dd8086
    if (is_it(IS_HS))
Packit dd8086
      ret |= FS_HIGH_SIERRA;
Packit dd8086
    else if (is_it(IS_ISOFS)) {
Packit dd8086
      if (is_it(IS_CD_RTOS) && is_it(IS_BRIDGE))
Packit dd8086
	ret = FS_ISO_9660_INTERACTIVE;
Packit dd8086
      else if (is_hfs())
Packit dd8086
	ret = FS_ISO_HFS;
Packit dd8086
      else
Packit dd8086
	ret = FS_ISO_9660;
Packit dd8086
      isofs_size = get_size();
Packit dd8086
      
Packit dd8086
#if 0
Packit dd8086
      if (is_rockridge())
Packit dd8086
	ret |= ROCKRIDGE;
Packit dd8086
#endif
Packit dd8086
Packit dd8086
      if (read_block(BOOT_SECTOR, start_session, 3, true) < 0)
Packit dd8086
	return ret;
Packit dd8086
      
Packit dd8086
      if (opts.debug_level > 0) {
Packit dd8086
	
Packit dd8086
	/* buffer[3] is defined */
Packit dd8086
	if (is_joliet()) printf("JOLIET, ");
Packit dd8086
	puts("");
Packit dd8086
	is_it_dbg(IS_BOOTABLE);
Packit dd8086
	puts("");
Packit dd8086
      }
Packit dd8086
      
Packit dd8086
      if (is_joliet()) {
Packit dd8086
	joliet_level = get_joliet_level();
Packit dd8086
	ret |= JOLIET;
Packit dd8086
      }
Packit dd8086
      if (is_it(IS_BOOTABLE))
Packit dd8086
	ret |= BOOTABLE;
Packit dd8086
      
Packit dd8086
      if (is_it(IS_BRIDGE) && is_it(IS_XA) && is_it(IS_ISOFS) 
Packit dd8086
	  && is_it(IS_CD_RTOS) &&
Packit dd8086
	  !is_it(IS_PHOTO_CD)) {
Packit dd8086
Packit dd8086
        if (read_block(VCD_INFO_SECTOR, start_session, 4, true) < 0)
Packit dd8086
	  return ret;
Packit dd8086
	
Packit dd8086
	if (opts.debug_level > 0) {
Packit dd8086
	  /* buffer[4] is defined */
Packit dd8086
	  is_it_dbg(IS_VIDEO_CD);
Packit dd8086
	  puts("");
Packit dd8086
	}
Packit dd8086
	
Packit dd8086
	if (is_it(IS_VIDEO_CD)) ret |= VIDEOCDI;
Packit dd8086
      }
Packit dd8086
    } 
Packit dd8086
    else if (is_hfs())       ret |= FS_HFS;
Packit dd8086
    else if (is_it(IS_EXT2)) ret |= FS_EXT2;
Packit dd8086
    else if (is_3do())       ret |= FS_3DO;
Packit dd8086
    else {
Packit dd8086
Packit dd8086
      if (read_block(UFS_SUPERBLOCK_SECTOR, start_session, 2, true) < 0)
Packit dd8086
	return ret;
Packit dd8086
      
Packit dd8086
      if (opts.debug_level > 0) {
Packit dd8086
	/* buffer[2] is defined */
Packit dd8086
	is_it_dbg(IS_UFS);
Packit dd8086
	puts("");
Packit dd8086
      }
Packit dd8086
      
Packit dd8086
      if (is_it(IS_UFS)) 
Packit dd8086
	ret |= FS_UFS;
Packit dd8086
      else
Packit dd8086
	ret |= FS_UNKNOWN;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  
Packit dd8086
  /* other checks */
Packit dd8086
  if (is_it(IS_XA))       ret |= XA;
Packit dd8086
  if (is_it(IS_PHOTO_CD)) ret |= PHOTO_CD;
Packit dd8086
  if (is_it(IS_CDTV))     ret |= CDTV;
Packit dd8086
  return ret;
Packit dd8086
}
Packit dd8086
Packit dd8086
static void 
Packit dd8086
myexit(int rc) 
Packit dd8086
{
Packit dd8086
  close(filehandle);
Packit dd8086
  cdio_destroy(img);
Packit dd8086
  exit(rc);
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* ------------------------------------------------------------------------ */
Packit dd8086
/* CDDB                                                                     */
Packit dd8086
Packit dd8086
/* 
Packit dd8086
   Returns the sum of the decimal digits in a number. Eg. 1955 = 20
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
cddb_dec_digit_sum(int n)
Packit dd8086
{
Packit dd8086
  int ret=0;
Packit dd8086
  
Packit dd8086
  for (;;) {
Packit dd8086
    ret += n%10;
Packit dd8086
    n    = n/10;
Packit dd8086
    if (!n)
Packit dd8086
      return ret;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/* Return the number of seconds (discarding frame portion) of an MSF */
Packit dd8086
static inline unsigned int
Packit dd8086
msf_seconds(msf_t *msf) 
Packit dd8086
{
Packit dd8086
  return cdio_from_bcd8(msf->m)*60 + cdio_from_bcd8(msf->s);
Packit dd8086
}
Packit dd8086
Packit dd8086
/* 
Packit dd8086
   Compute the CDDB disk ID for an Audio disk.  This is a funny checksum
Packit dd8086
   consisting of the concatenation of 3 things:
Packit dd8086
      the sum of the decimal digits of sizes of all tracks, 
Packit dd8086
      the total length of the disk, and 
Packit dd8086
      the number of tracks.
Packit dd8086
*/
Packit dd8086
static unsigned long
Packit dd8086
cddb_discid()
Packit dd8086
{
Packit dd8086
  int i,t,n=0;
Packit dd8086
  msf_t start_msf;
Packit dd8086
  msf_t msf;
Packit dd8086
  
Packit dd8086
  for (i = 1; i <= num_tracks; i++) {
Packit dd8086
    cdio_get_track_msf(img, i, &msf;;
Packit dd8086
    n += cddb_dec_digit_sum(msf_seconds(&msf));
Packit dd8086
  }
Packit dd8086
Packit dd8086
  cdio_get_track_msf(img, 1, &start_msf);
Packit dd8086
  cdio_get_track_msf(img, CDIO_CDROM_LEADOUT_TRACK, &msf;;
Packit dd8086
  
Packit dd8086
  t = msf_seconds(&msf) - msf_seconds(&start_msf);
Packit dd8086
  
Packit dd8086
  return ((n % 0xff) << 24 | t << 8 | num_tracks);
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* CDIO logging routines */
Packit dd8086
Packit dd8086
static cdio_log_handler_t gl_default_log_handler = NULL;
Packit dd8086
Packit dd8086
static void 
Packit dd8086
_log_handler (cdio_log_level_t level, const char message[])
Packit dd8086
{
Packit dd8086
  if (level == CDIO_LOG_DEBUG && opts.debug_level < 2)
Packit dd8086
    return;
Packit dd8086
Packit dd8086
  if (level == CDIO_LOG_INFO  && opts.debug_level < 1)
Packit dd8086
    return;
Packit dd8086
  
Packit dd8086
  if (level == CDIO_LOG_WARN  && opts.silent)
Packit dd8086
    return;
Packit dd8086
  
Packit dd8086
  gl_default_log_handler (level, message);
Packit dd8086
}
Packit dd8086
Packit dd8086
static void
Packit dd8086
print_analysis(int fs, int num_audio)
Packit dd8086
{
Packit dd8086
  int need_lf;
Packit dd8086
  
Packit dd8086
  switch(fs & FS_MASK) {
Packit dd8086
  case FS_NO_DATA:
Packit dd8086
    if (num_audio > 0)
Packit dd8086
      printf("Audio CD, CDDB disc ID is %08lx\n", cddb_discid());
Packit dd8086
    break;
Packit dd8086
  case FS_ISO_9660:
Packit dd8086
    printf("CD-ROM with ISO 9660 filesystem");
Packit dd8086
    if (fs & JOLIET)
Packit dd8086
      printf(" and joliet extension level %d", joliet_level);
Packit dd8086
    if (fs & ROCKRIDGE)
Packit dd8086
      printf(" and rockridge extensions");
Packit dd8086
    printf("\n");
Packit dd8086
    break;
Packit dd8086
  case FS_ISO_9660_INTERACTIVE:
Packit dd8086
    printf("CD-ROM with CD-RTOS and ISO 9660 filesystem\n");
Packit dd8086
    break;
Packit dd8086
  case FS_HIGH_SIERRA:
Packit dd8086
    printf("CD-ROM with High Sierra filesystem\n");
Packit dd8086
    break;
Packit dd8086
  case FS_INTERACTIVE:
Packit dd8086
    printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : "");
Packit dd8086
    break;
Packit dd8086
  case FS_HFS:
Packit dd8086
    printf("CD-ROM with Macintosh HFS\n");
Packit dd8086
    break;
Packit dd8086
  case FS_ISO_HFS:
Packit dd8086
    printf("CD-ROM with both Macintosh HFS and ISO 9660 filesystem\n");
Packit dd8086
    break;
Packit dd8086
  case FS_UFS:
Packit dd8086
    printf("CD-ROM with Unix UFS\n");
Packit dd8086
    break;
Packit dd8086
  case FS_EXT2:
Packit dd8086
    printf("CD-ROM with Linux second extended filesystem\n");
Packit dd8086
	  break;
Packit dd8086
  case FS_3DO:
Packit dd8086
    printf("CD-ROM with Panasonic 3DO filesystem\n");
Packit dd8086
    break;
Packit dd8086
  case FS_UNKNOWN:
Packit dd8086
    printf("CD-ROM with unknown filesystem\n");
Packit dd8086
    break;
Packit dd8086
  }
Packit dd8086
  switch(fs & FS_MASK) {
Packit dd8086
  case FS_ISO_9660:
Packit dd8086
  case FS_ISO_9660_INTERACTIVE:
Packit dd8086
  case FS_ISO_HFS:
Packit dd8086
    printf("ISO 9660: %i blocks, label `%.32s'\n",
Packit dd8086
	   isofs_size, buffer[0]+40);
Packit dd8086
    break;
Packit dd8086
  }
Packit dd8086
  need_lf = 0;
Packit dd8086
  if (first_data == 1 && num_audio > 0)
Packit dd8086
    need_lf += printf("mixed mode CD   ");
Packit dd8086
  if (fs & XA)
Packit dd8086
    need_lf += printf("XA sectors   ");
Packit dd8086
  if (fs & MULTISESSION)
Packit dd8086
    need_lf += printf("Multisession, offset = %i   ",ms_offset);
Packit dd8086
  if (fs & HIDDEN_TRACK)
Packit dd8086
    need_lf += printf("Hidden Track   ");
Packit dd8086
  if (fs & PHOTO_CD)
Packit dd8086
    need_lf += printf("%sPhoto CD   ", num_audio > 0 ? " Portfolio " : "");
Packit dd8086
  if (fs & CDTV)
Packit dd8086
    need_lf += printf("Commodore CDTV   ");
Packit dd8086
  if (first_data > 1)
Packit dd8086
    need_lf += printf("CD-Plus/Extra   ");
Packit dd8086
  if (fs & BOOTABLE)
Packit dd8086
    need_lf += printf("bootable CD   ");
Packit dd8086
  if (fs & VIDEOCDI && num_audio == 0)
Packit dd8086
    need_lf += printf("Video CD   ");
Packit dd8086
  if (need_lf) puts("");
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* ------------------------------------------------------------------------ */
Packit dd8086
Packit dd8086
/* Our argp parser. */
Packit dd8086
static struct argp argp = { options, parse_opt, args_doc, doc };
Packit dd8086
Packit dd8086
int
Packit dd8086
main(int argc, char *argv[])
Packit dd8086
{
Packit dd8086
Packit dd8086
     
Packit dd8086
  int fs=0;
Packit dd8086
  gl_default_log_handler = cdio_log_set_handler (_log_handler);
Packit dd8086
Packit dd8086
  program_name = strrchr(argv[0],'/');
Packit dd8086
  program_name = program_name ? program_name+1 : argv[0];
Packit dd8086
Packit dd8086
  /* Default option values. */
Packit dd8086
  opts.silent         = false;
Packit dd8086
  opts.debug_level    = 0;
Packit dd8086
  opts.show_tracks    = true;
Packit dd8086
  opts.show_ioctl     = true;
Packit dd8086
  opts.show_analysis  = true;
Packit dd8086
     
Packit dd8086
  /* Parse our arguments; every option seen by `parse_opt' will
Packit dd8086
     be reflected in `arguments'. */
Packit dd8086
  argp_parse (&argp, argc, argv, 0, 0, &opts);
Packit dd8086
     
Packit dd8086
  print_version();
Packit dd8086
  
Packit dd8086
  if (devname==NULL) {
Packit dd8086
    devname=strdup(cdio_get_default_device(img));
Packit dd8086
  }
Packit dd8086
  img = cdio_open (devname, DRIVER_UNKNOWN);
Packit dd8086
  
Packit dd8086
  /* open device */
Packit dd8086
  filehandle = open(devname,O_RDONLY);
Packit dd8086
  if (filehandle == -1) {
Packit dd8086
    err_exit("%s: %s\n", devname, strerror(errno));
Packit dd8086
  }
Packit dd8086
  
Packit dd8086
  first_track_num = cdio_get_first_track_num(img);
Packit dd8086
  num_tracks      = cdio_get_num_tracks(img);
Packit dd8086
Packit dd8086
  if (opts.show_tracks) {
Packit dd8086
    printf(STRONG "Track List (%i - %i)\n" NORMAL, 
Packit dd8086
	   first_track_num, num_tracks);
Packit dd8086
Packit dd8086
    printf(" nr: MSF      LSN      Ctrl Adr  Type\n");
Packit dd8086
  }
Packit dd8086
  
Packit dd8086
  /* Read and possibly print track information. */
Packit dd8086
  for (i = first_track_num; i <= CDIO_CDROM_LEADOUT_TRACK; i++) {
Packit dd8086
    msf_t msf;
Packit dd8086
    
Packit dd8086
    toc[i] = malloc(sizeof(struct cdrom_tocentry));
Packit dd8086
    if (toc[i] == NULL) {
Packit dd8086
      err_exit("out of memory");
Packit dd8086
    }
Packit dd8086
    memset(toc[i],0,sizeof(struct cdrom_tocentry));
Packit dd8086
    toc[i]->cdte_track  = i;
Packit dd8086
    toc[i]->cdte_format = CDROM_MSF;
Packit dd8086
    if (ioctl(filehandle,CDROMREADTOCENTRY,toc[i])) {
Packit dd8086
      err_exit("read TOC entry ioctl failed for track %i, give up\n", i);
Packit dd8086
    }
Packit dd8086
Packit dd8086
    if (!cdio_get_track_msf(img, i, &msf)) {
Packit dd8086
      err_exit("cdio_track_msf for track %i failed, I give up.\n", i);
Packit dd8086
    }
Packit dd8086
Packit dd8086
    if (opts.show_tracks) {
Packit dd8086
      printf("%3d: %2.2x:%2.2x:%2.2x (%06d) 0x%x  0x%x  %s%s\n",
Packit dd8086
	     (int) i, 
Packit dd8086
	     msf.m, msf.s, msf.f, 
Packit dd8086
	     cdio_msf_to_lsn(&msf),
Packit dd8086
	     (int)toc[i]->cdte_ctrl,
Packit dd8086
	     (int)toc[i]->cdte_adr,
Packit dd8086
	     track_format2str[cdio_get_track_format(img, i)],
Packit dd8086
	     CDIO_CDROM_LEADOUT_TRACK == i ? " (leadout)" : "");
Packit dd8086
    }
Packit dd8086
    
Packit dd8086
    if (i == CDIO_CDROM_LEADOUT_TRACK)
Packit dd8086
      break;
Packit dd8086
    if (TRACK_FORMAT_DATA == cdio_get_track_format(img, i)) {
Packit dd8086
      num_data++;
Packit dd8086
      if (-1 == first_data)
Packit dd8086
	first_data = i;
Packit dd8086
    } else {
Packit dd8086
      num_audio++;
Packit dd8086
      if (-1 == first_audio)
Packit dd8086
	first_audio = i;
Packit dd8086
    }
Packit dd8086
    /* skip to leadout */
Packit dd8086
    if (i == num_tracks)
Packit dd8086
      i = CDIO_CDROM_LEADOUT_TRACK-1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (opts.show_ioctl) {
Packit dd8086
    printf(STRONG "What ioctl's report...\n" NORMAL);
Packit dd8086
  
Packit dd8086
    /* get mcn */
Packit dd8086
    printf("Get MCN     : "); fflush(stdout);
Packit dd8086
    if (ioctl(filehandle,CDROM_GET_MCN, &mcn))
Packit dd8086
      printf("FAILED\n");
Packit dd8086
    else
Packit dd8086
      printf("%s\n",mcn.medium_catalog_number);
Packit dd8086
    
Packit dd8086
    /* get disk status */
Packit dd8086
    printf("disc status : "); fflush(stdout);
Packit dd8086
    switch (ioctl(filehandle,CDROM_DISC_STATUS,0)) {
Packit dd8086
    case CDS_NO_INFO: printf("no info\n"); break;
Packit dd8086
    case CDS_NO_DISC: printf("no disc\n"); break;
Packit dd8086
    case CDS_AUDIO:   printf("audio\n"); break;
Packit dd8086
    case CDS_DATA_1:  printf("data mode 1\n"); break;
Packit dd8086
    case CDS_DATA_2:  printf("data mode 2\n"); break;
Packit dd8086
    case CDS_XA_2_1:  printf("XA mode 1\n"); break;
Packit dd8086
    case CDS_XA_2_2:  printf("XA mode 2\n"); break;
Packit dd8086
    default:          printf("unknown (failed?)\n");
Packit dd8086
    }
Packit dd8086
    
Packit dd8086
    /* get multisession */
Packit dd8086
    printf("multisession: "); fflush(stdout);
Packit dd8086
    ms.addr_format = CDROM_LBA;
Packit dd8086
    if (ioctl(filehandle,CDROMMULTISESSION,&ms))
Packit dd8086
      printf("FAILED\n");
Packit dd8086
    else
Packit dd8086
      printf("%d%s\n",ms.addr.lba,ms.xa_flag?" XA":"");
Packit dd8086
    
Packit dd8086
    /* get audio status from subchnl */
Packit dd8086
    printf("audio status: "); fflush(stdout);
Packit dd8086
    sub.cdsc_format = CDROM_MSF;
Packit dd8086
    if (ioctl(filehandle,CDROMSUBCHNL,&sub))
Packit dd8086
      printf("FAILED\n");
Packit dd8086
    else {
Packit dd8086
      switch (sub.cdsc_audiostatus) {
Packit dd8086
      case CDROM_AUDIO_INVALID:    printf("invalid\n");   break;
Packit dd8086
      case CDROM_AUDIO_PLAY:       printf("playing");     break;
Packit dd8086
      case CDROM_AUDIO_PAUSED:     printf("paused");      break;
Packit dd8086
      case CDROM_AUDIO_COMPLETED:  printf("completed\n"); break;
Packit dd8086
      case CDROM_AUDIO_ERROR:      printf("error\n");     break;
Packit dd8086
      case CDROM_AUDIO_NO_STATUS:  printf("no status\n"); break;
Packit dd8086
      default:                     printf("Oops: unknown\n");
Packit dd8086
      }
Packit dd8086
      if (sub.cdsc_audiostatus == CDROM_AUDIO_PLAY ||
Packit dd8086
	  sub.cdsc_audiostatus == CDROM_AUDIO_PAUSED) {
Packit dd8086
	printf(" at: %02d:%02d abs / %02d:%02d track %d\n",
Packit dd8086
	       sub.cdsc_absaddr.msf.minute,
Packit dd8086
	       sub.cdsc_absaddr.msf.second,
Packit dd8086
	       sub.cdsc_reladdr.msf.minute,
Packit dd8086
	       sub.cdsc_reladdr.msf.second,
Packit dd8086
	       sub.cdsc_trk);
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  
Packit dd8086
  if (opts.show_analysis) {
Packit dd8086
    printf(STRONG "try to find out what sort of CD this is\n" NORMAL);
Packit dd8086
    
Packit dd8086
    /* try to find out what sort of CD we have */
Packit dd8086
    if (0 == num_data) {
Packit dd8086
      /* no data track, may be a "real" audio CD or hidden track CD */
Packit dd8086
      
Packit dd8086
      msf_t msf;
Packit dd8086
      cdio_get_track_msf(img, 1, &msf;;
Packit dd8086
      start_track = cdio_msf_to_lsn(&msf;;
Packit dd8086
      
Packit dd8086
      /* CD-I/Ready says start_track <= 30*75 then CDDA */
Packit dd8086
      if (start_track > 100 /* 100 is just a guess */) {
Packit dd8086
	fs = guess_filesystem(0, false);
Packit dd8086
	if ((fs & FS_MASK) != FS_UNKNOWN)
Packit dd8086
	  fs |= HIDDEN_TRACK;
Packit dd8086
	else {
Packit dd8086
	  fs &= ~FS_MASK; /* del filesystem info */
Packit dd8086
	  printf("Oops: %i unused sectors at start, but hidden track check failed.\n",start_track);
Packit dd8086
	}
Packit dd8086
      }
Packit dd8086
      print_analysis(fs, num_audio);
Packit dd8086
    } else {
Packit dd8086
      /* we have data track(s) */
Packit dd8086
      
Packit dd8086
      for (j = 2, i = first_data; i <= num_tracks; i++) {
Packit dd8086
	msf_t msf;
Packit dd8086
	track_format_t track_format = cdio_get_track_format(img, i);
Packit dd8086
      
Packit dd8086
	cdio_get_track_msf(img, i, &msf;;
Packit dd8086
Packit dd8086
	switch ( track_format ) {
Packit dd8086
	case TRACK_FORMAT_AUDIO:
Packit dd8086
	case TRACK_FORMAT_ERROR:
Packit dd8086
	  break;
Packit dd8086
	case TRACK_FORMAT_CDI:
Packit dd8086
	case TRACK_FORMAT_XA:
Packit dd8086
	case TRACK_FORMAT_DATA: 
Packit dd8086
	case TRACK_FORMAT_PSX: 
Packit dd8086
	  ;
Packit dd8086
	}
Packit dd8086
	
Packit dd8086
	start_track = (i == 1) ? 0 : cdio_msf_to_lsn(&msf;;
Packit dd8086
	
Packit dd8086
	/* save the start of the data area */
Packit dd8086
	if (i == first_data) 
Packit dd8086
	  data_start = start_track;
Packit dd8086
	
Packit dd8086
	/* skip tracks which belong to the current walked session */
Packit dd8086
	if (start_track < data_start + isofs_size)
Packit dd8086
	  continue;
Packit dd8086
	
Packit dd8086
	fs = guess_filesystem(start_track, cdio_get_track_green(img, i));
Packit dd8086
Packit dd8086
	if (i > 1) {
Packit dd8086
	  /* track is beyond last session -> new session found */
Packit dd8086
	  ms_offset = start_track;
Packit dd8086
	  printf("session #%d starts at track %2i, LSN: %6i,"
Packit dd8086
		 " ISO 9660 blocks: %6i\n",
Packit dd8086
		 j++, i, start_track, isofs_size);
Packit dd8086
	  printf("ISO 9660: %i blocks, label `%.32s'\n",
Packit dd8086
		 isofs_size, buffer[0]+40);
Packit dd8086
	  fs |= MULTISESSION;
Packit dd8086
	} else {
Packit dd8086
	  print_analysis(fs, num_audio);
Packit dd8086
	}
Packit dd8086
	
Packit dd8086
	if (!(((fs & FS_MASK) == FS_ISO_9660 ||
Packit dd8086
	       (fs & FS_MASK) == FS_ISO_HFS ||
Packit dd8086
	       /* (fs & FS_MASK) == FS_ISO_9660_INTERACTIVE) && (fs & XA))) */
Packit dd8086
	       (fs & FS_MASK) == FS_ISO_9660_INTERACTIVE)))
Packit dd8086
	  break;	/* no method for non-iso9660 multisessions */
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  
Packit dd8086
  
Packit dd8086
  myexit(EXIT_SUCCESS);
Packit dd8086
  /* Not reached:*/
Packit dd8086
  return(EXIT_SUCCESS);
Packit dd8086
}