|
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 |
}
|