Blame example/extract.c

Packit dd8086
/*
Packit dd8086
  Copyright (C) 2012 Pete Batard <pete@akeo.ie>
Packit dd8086
  Based on samples copyright (c) 2003-2011, 2017 Rocky Bernstein <rocky@gnu.org>
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
/* Extract the full contents of either an UDF or ISO9660 image file.
Packit dd8086
   TODO: timestamp preservation, file permissions, Unicode
Packit dd8086
 */
Packit dd8086
Packit dd8086
/* To handle files > 2 GB, we may need the Large File Support settings
Packit dd8086
   defined in config.h. Comes first, as stdio.h depends on it. */
Packit dd8086
#ifdef HAVE_CONFIG_H
Packit dd8086
#include "config.h"
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STDIO_H
Packit dd8086
#include <stdio.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_STRING_H
Packit dd8086
#include <string.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_ERRNO_H
Packit dd8086
#include <errno.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_STDLIB_H
Packit dd8086
#include <stdlib.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#if defined(_WIN32)
Packit dd8086
#include <direct.h>
Packit dd8086
#else
Packit dd8086
#ifdef HAVE_SYS_STAT_H
Packit dd8086
#include <sys/stat.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_SYS_TYPES_H
Packit dd8086
#include <sys/types.h>
Packit dd8086
#endif
Packit dd8086
#define _mkdir(a) mkdir(a, S_IRWXU)
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#if !defined(HAVE_SNPRINTF)
Packit dd8086
/* Fallback to unsafe 'sprintf' */
Packit dd8086
#define snprintf(str, size, format, ...) sprintf(str, format, __VA_ARGS__)
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include <cdio/cdio.h>
Packit dd8086
#include <cdio/logging.h>
Packit dd8086
#include <cdio/iso9660.h>
Packit dd8086
#include <cdio/udf.h>
Packit dd8086
Packit dd8086
#ifndef MIN
Packit dd8086
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#define print_vd_info(title, fn)     \
Packit dd8086
  if (fn(p_iso, &psz_str)) {         \
Packit dd8086
    printf(title ": %s\n", psz_str); \
Packit dd8086
  }                                  \
Packit dd8086
  free(psz_str);                     \
Packit dd8086
  psz_str = NULL;
Packit dd8086
Packit dd8086
static const char *psz_extract_dir;
Packit dd8086
static uint8_t i_joliet_level = 0;
Packit dd8086
Packit dd8086
static void log_handler (cdio_log_level_t level, const char *message)
Packit dd8086
{
Packit dd8086
  switch(level) {
Packit dd8086
  case CDIO_LOG_DEBUG:
Packit dd8086
  case CDIO_LOG_INFO:
Packit dd8086
    return;
Packit dd8086
  default:
Packit dd8086
    printf("cdio %d message: %s\n", level, message);
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const char *psz_path)
Packit dd8086
{
Packit dd8086
  FILE *fd = NULL;
Packit dd8086
  int i_length;
Packit dd8086
  char* psz_fullpath;
Packit dd8086
  const char* psz_basename;
Packit dd8086
  udf_dirent_t *p_udf_dirent2;
Packit dd8086
  uint8_t buf[UDF_BLOCKSIZE];
Packit dd8086
  int64_t i_read, i_file_length;
Packit dd8086
Packit dd8086
  if ((p_udf_dirent == NULL) || (psz_path == NULL))
Packit dd8086
    return 1;
Packit dd8086
Packit dd8086
  while (udf_readdir(p_udf_dirent)) {
Packit dd8086
    psz_basename = udf_get_filename(p_udf_dirent);
Packit dd8086
    i_length = 3 + strlen(psz_path) + strlen(psz_basename) + strlen(psz_extract_dir);
Packit dd8086
    psz_fullpath = (char*)calloc(sizeof(char), i_length);
Packit dd8086
    if (psz_fullpath == NULL) {
Packit dd8086
      fprintf(stderr, "Error allocating file name\n");
Packit dd8086
      goto out;
Packit dd8086
    }
Packit dd8086
    i_length = snprintf(psz_fullpath, i_length, "%s%s/%s", psz_extract_dir, psz_path, psz_basename);
Packit dd8086
    if (i_length < 0) {
Packit dd8086
      goto out;
Packit dd8086
    }
Packit dd8086
    printf("-- Extracting: %s\n", psz_fullpath);
Packit dd8086
    if (udf_is_dir(p_udf_dirent)) {
Packit dd8086
      int rc = _mkdir(psz_fullpath);
Packit dd8086
      if (0 == rc) {
Packit dd8086
	p_udf_dirent2 = udf_opendir(p_udf_dirent);
Packit dd8086
	if (p_udf_dirent2 != NULL) {
Packit dd8086
	  if (udf_extract_files(p_udf, p_udf_dirent2,
Packit dd8086
				&psz_fullpath[strlen(psz_extract_dir)]))
Packit dd8086
	    goto out;
Packit dd8086
	}
Packit dd8086
      } else if (-1 == rc) {
Packit dd8086
        fprintf(stderr, "  Unable to create make directory %s:\n%s\n",
Packit dd8086
		psz_fullpath, strerror(errno));
Packit dd8086
      } else {
Packit dd8086
        fprintf(stderr, "  Unable to create make directory %s;(rc=%d)\n",
Packit dd8086
		psz_fullpath, rc);
Packit dd8086
      }
Packit dd8086
    } else {
Packit dd8086
      fd = fopen(psz_fullpath, "wb");
Packit dd8086
      if (fd == NULL) {
Packit dd8086
        fprintf(stderr, "  Unable to create file %s\n", psz_fullpath);
Packit dd8086
        goto out;
Packit dd8086
      }
Packit dd8086
      i_file_length = udf_get_file_length(p_udf_dirent);
Packit dd8086
      while (i_file_length > 0) {
Packit dd8086
        memset(buf, 0, UDF_BLOCKSIZE);
Packit dd8086
        i_read = udf_read_block(p_udf_dirent, buf, 1);
Packit dd8086
        if (i_read < 0) {
Packit dd8086
          fprintf(stderr, "  Error reading UDF file %s\n", &psz_fullpath[strlen(psz_extract_dir)]);
Packit dd8086
          goto out;
Packit dd8086
        }
Packit dd8086
        fwrite(buf, (size_t)MIN(i_file_length, i_read), 1, fd);
Packit dd8086
        if (ferror(fd)) {
Packit dd8086
          fprintf(stderr, "  Error writing file %s: %s\n", psz_fullpath,
Packit dd8086
                  strerror(errno));
Packit dd8086
          goto out;
Packit dd8086
        }
Packit dd8086
        i_file_length -= i_read;
Packit dd8086
      }
Packit dd8086
      fclose(fd);
Packit dd8086
      fd = NULL;
Packit dd8086
    }
Packit dd8086
    free(psz_fullpath);
Packit dd8086
  }
Packit dd8086
  return 0;
Packit dd8086
Packit dd8086
out:
Packit dd8086
  if (fd != NULL)
Packit dd8086
    fclose(fd);
Packit dd8086
  free(psz_fullpath);
Packit dd8086
  return 1;
Packit dd8086
}
Packit dd8086
Packit dd8086
static int iso_extract_files(iso9660_t* p_iso, const char *psz_path)
Packit dd8086
{
Packit dd8086
  FILE *fd = NULL;
Packit dd8086
  int i_length, r = 1;
Packit dd8086
  char psz_fullpath[4096], *psz_basename;
Packit dd8086
  const char *psz_iso_name = &psz_fullpath[strlen(psz_extract_dir)];
Packit dd8086
  unsigned char buf[ISO_BLOCKSIZE];
Packit dd8086
  CdioListNode_t *p_entnode;
Packit dd8086
  iso9660_stat_t *p_statbuf;
Packit dd8086
  CdioISO9660FileList_t* p_entlist;
Packit dd8086
  size_t i;
Packit dd8086
  lsn_t lsn;
Packit dd8086
  int64_t i_file_length;
Packit dd8086
Packit dd8086
  if ((p_iso == NULL) || (psz_path == NULL))
Packit dd8086
    return 1;
Packit dd8086
Packit dd8086
  i_length = snprintf(psz_fullpath, sizeof(psz_fullpath), "%s%s/", psz_extract_dir, psz_path);
Packit dd8086
  if (i_length < 0)
Packit dd8086
    return 1;
Packit dd8086
  psz_basename = &psz_fullpath[i_length];
Packit dd8086
Packit dd8086
  p_entlist = iso9660_ifs_readdir(p_iso, psz_path);
Packit dd8086
  if (!p_entlist) {
Packit dd8086
    printf("Could not access %s\n", psz_path);
Packit dd8086
    return 1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  _CDIO_LIST_FOREACH (p_entnode, p_entlist) {
Packit dd8086
    p_statbuf = (iso9660_stat_t*) _cdio_list_node_data(p_entnode);
Packit dd8086
    /* Eliminate . and .. entries */
Packit dd8086
    if ( (strcmp(p_statbuf->filename, ".") == 0)
Packit dd8086
      || (strcmp(p_statbuf->filename, "..") == 0) )
Packit dd8086
      continue;
Packit dd8086
    iso9660_name_translate_ext(p_statbuf->filename, psz_basename, i_joliet_level);
Packit dd8086
    if (p_statbuf->type == _STAT_DIR) {
Packit dd8086
      _mkdir(psz_fullpath);
Packit dd8086
      if (iso_extract_files(p_iso, psz_iso_name))
Packit dd8086
        goto out;
Packit dd8086
    } else {
Packit dd8086
      printf("Extracting: %s\n", psz_fullpath);
Packit dd8086
      fd = fopen(psz_fullpath, "wb");
Packit dd8086
      if (fd == NULL) {
Packit dd8086
        fprintf(stderr, "  Unable to create file\n");
Packit dd8086
        goto out;
Packit dd8086
      }
Packit dd8086
      i_file_length = p_statbuf->size;
Packit dd8086
      for (i = 0; i_file_length > 0; i++) {
Packit dd8086
        memset(buf, 0, ISO_BLOCKSIZE);
Packit dd8086
        lsn = p_statbuf->lsn + i;
Packit dd8086
        if (iso9660_iso_seek_read(p_iso, buf, lsn, 1) != ISO_BLOCKSIZE) {
Packit dd8086
          fprintf(stderr, "  Error reading ISO9660 file %s at LSN %lu\n",
Packit dd8086
            psz_iso_name, (long unsigned int)lsn);
Packit dd8086
          goto out;
Packit dd8086
        }
Packit dd8086
        fwrite(buf, (size_t)MIN(i_file_length, ISO_BLOCKSIZE), 1, fd);
Packit dd8086
        if (ferror(fd)) {
Packit dd8086
	  fprintf(stderr, "  Error writing file %s: %s\n", psz_iso_name,
Packit dd8086
		  strerror(errno));
Packit dd8086
          goto out;
Packit dd8086
        }
Packit dd8086
        i_file_length -= ISO_BLOCKSIZE;
Packit dd8086
      }
Packit dd8086
      fclose(fd);
Packit dd8086
      fd = NULL;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  r = 0;
Packit dd8086
Packit dd8086
out:
Packit dd8086
  if (fd != NULL)
Packit dd8086
    fclose(fd);
Packit dd8086
  iso9660_filelist_free(p_entlist);
Packit dd8086
  return r;
Packit dd8086
}
Packit dd8086
Packit dd8086
int main(int argc, char** argv)
Packit dd8086
{
Packit dd8086
  iso9660_t* p_iso = NULL;
Packit dd8086
  udf_t* p_udf = NULL;
Packit dd8086
  udf_dirent_t* p_udf_root;
Packit dd8086
  char *psz_str = NULL;
Packit dd8086
  char vol_id[UDF_VOLID_SIZE] = "";
Packit dd8086
  char volset_id[UDF_VOLSET_ID_SIZE+1] = "";
Packit dd8086
  int r = 0;
Packit dd8086
Packit dd8086
  cdio_log_set_handler (log_handler);
Packit dd8086
Packit dd8086
  if (argc < 3) {
Packit dd8086
    fprintf(stderr, "Usage: extract <iso_image> <extraction_dir>\n");
Packit dd8086
    return 1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Warn if LFS doesn't appear to be enabled */
Packit dd8086
  if (sizeof(off_t) < 8) {
Packit dd8086
    fprintf(stderr, "INFO: Large File Support not detected (required for files >2GB)\n");
Packit dd8086
  }
Packit dd8086
Packit dd8086
  psz_extract_dir = argv[2];
Packit dd8086
  if (_mkdir(psz_extract_dir) == 0) {
Packit dd8086
    printf("-- Creating directory: %s\n", psz_extract_dir);
Packit dd8086
  } else if (errno != EEXIST) {
Packit dd8086
    fprintf(stderr, "Unable to create extraction directory %s\n", psz_extract_dir);
Packit dd8086
    return 1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* First try to open as UDF - fallback to ISO if it failed */
Packit dd8086
  p_udf = udf_open(argv[1]);
Packit dd8086
  if (p_udf == NULL)
Packit dd8086
    goto try_iso;
Packit dd8086
Packit dd8086
  p_udf_root = udf_get_root(p_udf, true, 0);
Packit dd8086
  if (p_udf_root == NULL) {
Packit dd8086
    fprintf(stderr, "Couldn't locate UDF root directory\n");
Packit dd8086
    goto out;
Packit dd8086
  }
Packit dd8086
  vol_id[0] = 0; volset_id[0] = 0;
Packit dd8086
Packit dd8086
  /* Show basic UDF Volume info */
Packit dd8086
  if (udf_get_volume_id(p_udf, vol_id, sizeof(vol_id)) > 0)
Packit dd8086
    printf("-- Volume id: %s\n", vol_id);
Packit dd8086
  if (udf_get_volume_id(p_udf, volset_id, sizeof(volset_id)) >0 ) {
Packit dd8086
    volset_id[UDF_VOLSET_ID_SIZE]='\0';
Packit dd8086
    printf("-- Volume set id: %s\n", volset_id);
Packit dd8086
  }
Packit dd8086
  printf("-- Partition number: %d\n", udf_get_part_number(p_udf));
Packit dd8086
Packit dd8086
  /* Recursively extract files */
Packit dd8086
  r = udf_extract_files(p_udf, p_udf_root, "");
Packit dd8086
Packit dd8086
  goto out;
Packit dd8086
Packit dd8086
try_iso:
Packit dd8086
  p_iso = iso9660_open_ext(argv[1], ISO_EXTENSION_ALL);
Packit dd8086
  if (p_iso == NULL) {
Packit dd8086
    fprintf(stderr, "Unable to open image '%s'.\n", argv[1]);
Packit dd8086
    r = 1;
Packit dd8086
    goto out;
Packit dd8086
  }
Packit dd8086
  i_joliet_level = iso9660_ifs_get_joliet_level(p_iso);
Packit dd8086
Packit dd8086
  /* Show basic ISO9660 info from the Primary Volume Descriptor. */
Packit dd8086
  print_vd_info("Application", iso9660_ifs_get_application_id);
Packit dd8086
  print_vd_info("Preparer   ", iso9660_ifs_get_preparer_id);
Packit dd8086
  print_vd_info("Publisher  ", iso9660_ifs_get_publisher_id);
Packit dd8086
  print_vd_info("System     ", iso9660_ifs_get_system_id);
Packit dd8086
  print_vd_info("Volume     ", iso9660_ifs_get_volume_id);
Packit dd8086
  print_vd_info("Volume Set ", iso9660_ifs_get_volumeset_id);
Packit dd8086
Packit dd8086
  r = iso_extract_files(p_iso, "");
Packit dd8086
Packit dd8086
out:
Packit dd8086
  if (p_iso != NULL)
Packit dd8086
    iso9660_close(p_iso);
Packit dd8086
  if (p_udf != NULL)
Packit dd8086
    udf_close(p_udf);
Packit dd8086
Packit dd8086
  return r;
Packit dd8086
}