diff --git a/THANKS b/THANKS index 39eaa3d0..66d6766e 100644 --- a/THANKS +++ b/THANKS @@ -1,6 +1,3 @@ -Geoff Bailey - FreeBSD debugging & testing - Burkhard Plaum some GNU/Linux and CD-Text patches @@ -20,6 +17,9 @@ Frank Endres Frantisek Dvorak : bug reports and miscellaneous fixes +Geoff Bailey + FreeBSD debugging & testing + Heiner FreeBSD CAM support and FreeBSD debugging & testing @@ -35,13 +35,13 @@ Justin F. Hallett KO Myung-Hun OS2 Driver -Leon Merten Lohse - Redo CD-Text handling - Kris Verbeeck : CDDB library support from libcddb http://libcddb.sourceforge.net Gentoo ebuild-file +Leon Merten Lohse + Redo CD-Text handling + Manfred Tremmel : RPM spec file and inclusion of libcdio into http://packman.links2linux.de/ @@ -55,6 +55,9 @@ Nicolas Boullis Patrick Guimond CD-Extra audio data boundaries +Pete Batard + UDF support + Peter Hartley Cross-compiling to mingw32 diff --git a/configure.ac b/configure.ac index 0344025a..b4c306f4 100644 --- a/configure.ac +++ b/configure.ac @@ -322,14 +322,14 @@ case $host_os in ## Don't use AIX driver until starts to really work ## cd_drivers="${cd_drivers}, AIX" ## AC_DEFINE([HAVE_AIX_CDROM], [1], - ## [Define to 1 if you have AIX CD-ROM support]) + ## [Define 1 if you have AIX CD-ROM support]) ;; darwin6*|darwin7*|darwin8*|darwin9*) AC_CHECK_HEADERS(IOKit/IOKitLib.h CoreFoundation/CFBase.h, [have_iokit_h="yes"]) if test "x$have_iokit_h" = "xyes" ; then AC_DEFINE([HAVE_DARWIN_CDROM], [1], - [Define to 1 if you have Darwin OS X-type CD-ROM support]) + [Define 1 if you have Darwin OS X-type CD-ROM support]) DARWIN_PKG_LIB_HACK="-Wl,-framework,CoreFoundation -Wl,-framework,IOKit" dnl Prior to Mac OS X 10.4 (Tiger), DiskArbitration was a private framework. @@ -341,7 +341,7 @@ case $host_os in LIBS="$ac_save_LIBS" AC_MSG_RESULT([$have_diskarbitration_framework]) if test "x$have_diskarbitration_framework" = "xyes"; then - AC_DEFINE([HAVE_DISKARBITRATION], 1, [Define to 1 if you have the Apple DiskArbitration framework]) + AC_DEFINE([HAVE_DISKARBITRATION], 1, [Define 1 if you have the Apple DiskArbitration framework]) DARWIN_PKG_LIB_HACK="$DARWIN_PKG_LIB_HACK -Wl,-framework,DiskArbitration" fi @@ -359,9 +359,9 @@ case $host_os in struct cdrom_generic_command test; int has_timeout=sizeof(test.timeout);], [AC_DEFINE([HAVE_LINUX_CDROM_TIMEOUT], [1], - [Define to 1 if timeout is in cdrom_generic_command struct])]) + [Define 1 if timeout is in cdrom_generic_command struct])]) AC_DEFINE([HAVE_LINUX_CDROM], [1], - [Define to 1 if you have Linux-type CD-ROM support]) + [Define 1 if you have Linux-type CD-ROM support]) cd_drivers="${cd_drivers}, GNU/Linux" fi ;; @@ -369,7 +369,7 @@ int has_timeout=sizeof(test.timeout);], AC_CHECK_HEADERS(dvd.h, [have_bsdi_dvd_h="yes"]) if test "x$have_bsdi_dvd_h" = "xyes" ; then AC_DEFINE([HAVE_BSDI_CDROM], [1], - [Define to 1 if you have BSDI-type CD-ROM support]) + [Define 1 if you have BSDI-type CD-ROM support]) LIBS="$LIBS -ldvd -lcdrom" LIBCDIO_LIBS="$LIBCDIO_LIBS -lcdrom" cd_drivers="${cd_drivers}, BSDI" @@ -377,27 +377,27 @@ int has_timeout=sizeof(test.timeout);], ;; sunos*|sun*|solaris*) AC_DEFINE([HAVE_SOLARIS_CDROM], [1], - [Define to 1 if you have Solaris CD-ROM support]) + [Define 1 if you have Solaris CD-ROM support]) cd_drivers="${cd_drivers}, Solaris" ;; cygwin*) AC_DEFINE([CYGWIN], [1], - [Define to 1 if you are compiling using cygwin]) + [Define 1 if you are compiling using cygwin]) AC_DEFINE([HAVE_WIN32_CDROM], [1], - [Define to 1 if you have MinGW CD-ROM support]) + [Define 1 if you have MinGW CD-ROM support]) LIBS="$LIBS -lwinmm" LT_NO_UNDEFINED="-no-undefined" cd_drivers="${cd_drivers}, MinGW" AC_DEFINE([NEED_TIMEZONEVAR], [1], - [Define to 1 if you need timezone defined to get timzone + [Define 1 if you need timezone defined to get timzone defined as a variable. In cygwin it is a function too]) ;; mingw*) AC_CHECK_HEADERS(windows.h) AC_DEFINE([MINGW32], [1], - [Define to 1 if you are compiling using MinGW]) + [Define 1 if you are compiling using MinGW]) AC_DEFINE([HAVE_WIN32_CDROM], [1], - [Define to 1 if you have MinGW CD-ROM support]) + [Define 1 if you have MinGW CD-ROM support]) LIBS="$LIBS -lwinmm" LT_NO_UNDEFINED="-no-undefined" cd_drivers="${cd_drivers}, MinGW " @@ -415,19 +415,19 @@ int has_timeout=sizeof(test.timeout);], ;; freebsd4.*|freebsd5.*|freebsd[6-9]*|dragonfly*|kfreebsd*) AC_DEFINE([HAVE_FREEBSD_CDROM], [1], - [Define to 1 if you have FreeBSD CD-ROM support]) + [Define 1 if you have FreeBSD CD-ROM support]) LIBS="$LIBS -lcam" cd_drivers="${cd_drivers}, FreeBSD " ;; netbsd*) AC_DEFINE([HAVE_NETBSD_CDROM], [1], - [Define to 1 if you have NetBSD CD-ROM support]) + [Define 1 if you have NetBSD CD-ROM support]) # LIBS="$LIBS -lcam" cd_drivers="${cd_drivers}, NetBSD " ;; os2*) AC_DEFINE([HAVE_OS2_CDROM], [1], - [Define to 1 if you have OS/2 CD-ROM support]) + [Define 1 if you have OS/2 CD-ROM support]) LT_NO_UNDEFINED="-no-undefined" LDFLAGS="$LDFLAGS -Zbin-files" cd_drivers="${cd_drivers}, OS2 " @@ -483,7 +483,7 @@ AC_CHECK_FUNCS( [chdir drand48 fseeko fseeko64 ftruncate geteuid getgid \ # check for timegm() support AC_CHECK_FUNC(timegm, AC_DEFINE(HAVE_TIMEGM,1, - [Define to 1 if timegm is available])) + [Define 1 if timegm is available])) AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE(HAVE_TM_GMTOFF, 1, @@ -525,7 +525,7 @@ if test "x${enable_joliet}" != "xno"; then fi if test "x$joliet_supported" = "xyes"; then AC_DEFINE(HAVE_JOLIET, [1], - [Define to 1 if you want ISO-9660 Joliet extension support. + [Define 1 if you want ISO-9660 Joliet extension support. On most platform, this requires libiconv to be installed.]) HAVE_JOLIET=1 else @@ -539,7 +539,7 @@ AC_ARG_ENABLE(rock, enable_rock=$enableval, enable_rock=no) if test "x${enable_rock}" != "xno"; then AC_DEFINE(HAVE_ROCK, [1], - [Define to 1 if you want ISO-9660 Rock-Ridge extension support.]) + [Define 1 if you want ISO-9660 Rock-Ridge extension support.]) HAVE_ROCK=1 fi AC_SUBST(HAVE_ROCK) @@ -672,7 +672,7 @@ AC_CONFIG_FILES([test/check_iso.sh], [chmod +x test/check_iso.sh]) AC_CONFIG_FILES([test/check_nrg.sh], [chmod +x test/check_nrg.sh]) AC_OUTPUT -AC_MSG_RESULT([ +AC_MSG_NOTICE([ Using CD-ROM drivers : $cd_drivers Building cd-info : $(test "x$enable_cd_info" = "xyes" && echo yes || echo no) Building cd-read : $(test "x$enable_cd_read" = "xyes" && echo yes || echo no) diff --git a/example/.gitignore b/example/.gitignore index 258fcf94..b020928b 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -14,6 +14,7 @@ /discid /drives /eject +/extract /isofile /isofile2 /isofuzzy diff --git a/example/Makefile.am b/example/Makefile.am index 4087c856..d340f7f0 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -23,7 +23,7 @@ SUBDIRS = C++ endif if BUILD_EXAMPLES noinst_PROGRAMS = audio cdchange cdtext device discid drives eject \ - isofile isofile2 isofuzzy isolist isolsn \ + extract isofile isofile2 isofuzzy isolist isolsn \ mmc1 mmc2 mmc2a mmc3 tracks \ sample3 sample4 udf1 udffile cdio-eject endif @@ -51,6 +51,9 @@ drives_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) eject_DEPENDENCIES = $(LIBCDIO_DEPS) eject_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) +extract_DEPENDENCIES = $(LIBISO9660_LIBS) $(LIBUDF_LIBS) $(LIBCDIO_DEPS) +extract_LDADD = $(LIBISO9660_LIBS) $(LIBUDF_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) + cdio_eject_DEPENDENCIES = $(LIBCDIO_DEPS) cdio_eject_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) diff --git a/example/README b/example/README index 86b85bf2..4a675ba3 100644 --- a/example/README +++ b/example/README @@ -41,6 +41,8 @@ drives.c: A program to show drivers installed and what the default eject.c: A program eject a CD from a CD-ROM drive and then close the door again. +extract.c: Extract the full contents of either an UDF or ISO9660 image file. + isofile.c: A program to show using libiso9660 to extract a file from an ISO-9660 image. diff --git a/example/extract.c b/example/extract.c new file mode 100644 index 00000000..9cfd0efc --- /dev/null +++ b/example/extract.c @@ -0,0 +1,300 @@ +/* + Copyright (C) 2012 Pete Batard + Based on samples copyright (c) 2003-2011 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Extract the full contents of either an UDF or ISO9660 image file. + TODO: timestamp preservation, file permissions, Unicode + */ + +/* To handle files > 2 GB, we may need the Large File Support settings + defined in config.h. Comes first, as stdio.h depends on it. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#define __CDIO_CONFIG_H__ 1 +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif + +#if defined(_WIN32) +#include +#else +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#define _mkdir(a) mkdir(a, S_IRWXU) +#endif + +#if !defined(HAVE_SNPRINTF) +/* Fallback to unsafe 'sprintf' */ +#define snprintf(str, size, format, ...) sprintf(str, format, __VA_ARGS__) +#endif + +#include +#include +#include +#include + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define print_vd_info(title, fn) \ + if (fn(p_iso, &psz_str)) { \ + printf(title ": %s\n", psz_str); \ + } \ + free(psz_str); \ + psz_str = NULL; + +const char *psz_extract_dir; + +static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const char *psz_path) +{ + FILE *fd = NULL; + int i_length; + char* psz_fullpath; + const char* psz_basename; + udf_dirent_t *p_udf_dirent2; + uint8_t buf[UDF_BLOCKSIZE]; + int64_t i_read, i_file_length; + + if ((p_udf_dirent == NULL) || (psz_path == NULL)) + return 1; + + while (udf_readdir(p_udf_dirent)) { + psz_basename = udf_get_filename(p_udf_dirent); + i_length = 3 + strlen(psz_path) + strlen(psz_basename) + strlen(psz_extract_dir); + psz_fullpath = (char*)calloc(sizeof(char), i_length); + if (psz_fullpath == NULL) { + fprintf(stderr, "Error allocating file name\n"); + goto out; + } + i_length = snprintf(psz_fullpath, i_length, "%s%s/%s", psz_extract_dir, psz_path, psz_basename); + if (i_length < 0) { + goto out; + } + printf("Extracting: %s\n", psz_fullpath); + if (udf_is_dir(p_udf_dirent)) { + _mkdir(psz_fullpath); + p_udf_dirent2 = udf_opendir(p_udf_dirent); + if (p_udf_dirent2 != NULL) { + if (udf_extract_files(p_udf, p_udf_dirent2, &psz_fullpath[strlen(psz_extract_dir)])) + goto out; + } + } else { + fd = fopen(psz_fullpath, "wb"); + if (fd == NULL) { + fprintf(stderr, " Unable to create file\n"); + goto out; + } + i_file_length = udf_get_file_length(p_udf_dirent); + while (i_file_length > 0) { + memset(buf, 0, UDF_BLOCKSIZE); + i_read = udf_read_block(p_udf_dirent, buf, 1); + if (i_read < 0) { + fprintf(stderr, " Error reading UDF file %s\n", &psz_fullpath[strlen(psz_extract_dir)]); + goto out; + } + fwrite(buf, (size_t)MIN(i_file_length, i_read), 1, fd); + if (ferror(fd)) { + fprintf(stderr, " Error writing file: %s\n", strerror(errno)); + goto out; + } + i_file_length -= i_read; + } + fclose(fd); + fd = NULL; + } + free(psz_fullpath); + } + return 0; + +out: + if (fd != NULL) + fclose(fd); + free(psz_fullpath); + return 1; +} + +static int iso_extract_files(iso9660_t* p_iso, const char *psz_path) +{ + FILE *fd = NULL; + int i_length, r = 1; + char psz_fullpath[4096], *psz_basename; + const char *psz_iso_name = &psz_fullpath[strlen(psz_extract_dir)]; + unsigned char buf[ISO_BLOCKSIZE]; + CdioListNode_t* p_entnode; + iso9660_stat_t *p_statbuf; + CdioList_t* p_entlist; + size_t i; + lsn_t lsn; + int64_t i_file_length; + + if ((p_iso == NULL) || (psz_path == NULL)) + return 1; + + i_length = snprintf(psz_fullpath, sizeof(psz_fullpath), "%s%s/", psz_extract_dir, psz_path); + if (i_length < 0) + return 1; + psz_basename = &psz_fullpath[i_length]; + + p_entlist = iso9660_ifs_readdir(p_iso, psz_path); + if (!p_entlist) + return 1; + + _CDIO_LIST_FOREACH (p_entnode, p_entlist) { + p_statbuf = (iso9660_stat_t*) _cdio_list_node_data(p_entnode); + /* Eliminate . and .. entries */ + if ( (strcmp(p_statbuf->filename, ".") == 0) + || (strcmp(p_statbuf->filename, "..") == 0) ) + continue; + iso9660_name_translate(p_statbuf->filename, psz_basename); + if (p_statbuf->type == _STAT_DIR) { + _mkdir(psz_fullpath); + if (iso_extract_files(p_iso, psz_iso_name)) + goto out; + } else { + printf("Extracting: %s\n", psz_fullpath); + fd = fopen(psz_fullpath, "wb"); + if (fd == NULL) { + fprintf(stderr, " Unable to create file\n"); + goto out; + } + i_file_length = p_statbuf->size; + for (i = 0; i_file_length > 0; i++) { + memset(buf, 0, ISO_BLOCKSIZE); + lsn = p_statbuf->lsn + i; + if (iso9660_iso_seek_read(p_iso, buf, lsn, 1) != ISO_BLOCKSIZE) { + fprintf(stderr, " Error reading ISO9660 file %s at LSN %lu\n", + psz_iso_name, (long unsigned int)lsn); + goto out; + } + fwrite(buf, (size_t)MIN(i_file_length, ISO_BLOCKSIZE), 1, fd); + if (ferror(fd)) { + fprintf(stderr, " Error writing file: %s\n", strerror(errno)); + goto out; + } + i_file_length -= ISO_BLOCKSIZE; + } + fclose(fd); + fd = NULL; + } + } + r = 0; + +out: + if (fd != NULL) + fclose(fd); + _cdio_list_free(p_entlist, true); + return r; +} + +int main(int argc, char** argv) +{ + iso9660_t* p_iso = NULL; + udf_t* p_udf = NULL; + udf_dirent_t* p_udf_root; + char *psz_str = NULL; + char vol_id[UDF_VOLID_SIZE] = ""; + char volset_id[UDF_VOLSET_ID_SIZE+1] = ""; + int r = 0; + + cdio_loglevel_default = CDIO_LOG_DEBUG; + + if (argc < 3) { + fprintf(stderr, "Usage: extract \n"); + return 1; + } + + /* Warn if LFS doesn't appear to be enabled */ + if (sizeof(off_t) < 8) { + fprintf(stderr, "INFO: Large File Support not detected (required for files >2GB)\n"); + } + + psz_extract_dir = argv[2]; + if (_mkdir(psz_extract_dir) == 0) { + printf("Creating directory: %s\n", psz_extract_dir); + } else if (errno != EEXIST) { + fprintf(stderr, "Unable to create extraction directory %s\n", psz_extract_dir); + return 1; + } + + /* First try to open as UDF - fallback to ISO if it failed */ + p_udf = udf_open(argv[1]); + if (p_udf == NULL) + goto try_iso; + + p_udf_root = udf_get_root(p_udf, true, 0); + if (p_udf_root == NULL) { + fprintf(stderr, "Couldn't locate UDF root directory\n"); + goto out; + } + vol_id[0] = 0; volset_id[0] = 0; + + /* Show basic UDF Volume info */ + if (udf_get_volume_id(p_udf, vol_id, sizeof(vol_id)) > 0) + fprintf(stderr, "Volume id: %s\n", vol_id); + if (udf_get_volume_id(p_udf, volset_id, sizeof(volset_id)) >0 ) { + volset_id[UDF_VOLSET_ID_SIZE]='\0'; + fprintf(stderr, "Volume set id: %s\n", volset_id); + } + fprintf(stderr, "Partition number: %d\n", udf_get_part_number(p_udf)); + + /* Recursively extract files */ + r = udf_extract_files(p_udf, p_udf_root, ""); + + goto out; + +try_iso: + p_iso = iso9660_open(argv[1]); + if (p_iso == NULL) { + fprintf(stderr, "Unable to open image '%s'.\n", argv[1]); + r = 1; + goto out; + } + + /* Show basic ISO9660 info from the Primary Volume Descriptor. */ + print_vd_info("Application", iso9660_ifs_get_application_id); + print_vd_info("Preparer ", iso9660_ifs_get_preparer_id); + print_vd_info("Publisher ", iso9660_ifs_get_publisher_id); + print_vd_info("System ", iso9660_ifs_get_system_id); + print_vd_info("Volume ", iso9660_ifs_get_volume_id); + print_vd_info("Volume Set ", iso9660_ifs_get_volumeset_id); + + r = iso_extract_files(p_iso, ""); + +out: + if (p_iso != NULL) + iso9660_close(p_iso); + if (p_udf != NULL) + udf_close(p_udf); + + return r; +} diff --git a/example/udffile.c b/example/udffile.c index 8f3d09b5..ffbc0119 100644 --- a/example/udffile.c +++ b/example/udffile.c @@ -117,8 +117,8 @@ main(int argc, const char *argv[]) } { - long unsigned int i_file_length = udf_get_file_length(p_udf_file); - const unsigned int i_blocks = CEILING(i_file_length, UDF_BLOCKSIZE); + uint64_t i_file_length = udf_get_file_length(p_udf_file); + const unsigned int i_blocks = (unsigned int) CEILING(i_file_length, UDF_BLOCKSIZE); unsigned int i; for (i = 0; i < i_blocks ; i++) { char buf[UDF_BLOCKSIZE] = {'\0',};