pax_global_header00006660000000000000000000000064140325645660014525gustar00rootroot0000000000000052 comment=a35513813819efadca82c4b90edbe1407b1b9e05 libudfread-1.1.2/000077500000000000000000000000001403256456600136275ustar00rootroot00000000000000libudfread-1.1.2/.gitignore000066400000000000000000000005411403256456600156170ustar00rootroot00000000000000*.[oa] *~ *.lo *.la *.pc *.tar.* /aclocal.m4 /autom4te.cache/ /build-aux/ /compile /config.guess /config.h /config.h.in /config.log /config.status /config.sub /configure /libtool /m4/libtool.m4 /m4/ltoptions.m4 /m4/ltsugar.m4 /m4/ltversion.m4 /m4/lt~obsolete.m4 /Makefile /Makefile.in /src/udfread-version.h /stamp-h1 .dirstamp .deps .libs udfcat udfls libudfread-1.1.2/.gitlab-ci.yml000066400000000000000000000022521403256456600162640ustar00rootroot00000000000000stages: - build variables: GIT_SUBMODULE_STRATEGY: normal build-debian: image: registry.videolan.org/vlc-debian-unstable:20200529132440 stage: build tags: - docker - amd64 script: - ./bootstrap - mkdir build - cd build - ../configure - make -j $(getconf _NPROCESSORS_ONLN) build-macos: stage: build tags: - catalina - amd64 script: - ./bootstrap - mkdir build - cd build - ../configure - make -j $(getconf _NPROCESSORS_ONLN) build-win64: image: registry.videolan.org/vlc-debian-win64:20201106143728 stage: build tags: - docker - amd64 script: - ./bootstrap - mkdir build - cd build - ../configure --host=x86_64-w64-mingw32 - make -j $(getconf _NPROCESSORS_ONLN) build-win32: image: registry.videolan.org/vlc-debian-win32:20201106141924 stage: build tags: - docker - amd64 script: - ./bootstrap - mkdir build - cd build - ../configure --host=i686-w64-mingw32 - make -j $(getconf _NPROCESSORS_ONLN) libudfread-1.1.2/COPYING000066400000000000000000000636421403256456600146750ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! libudfread-1.1.2/ChangeLog000066400000000000000000000006761403256456600154120ustar00rootroot000000000000002021-04-05: Version 1.1.2 - Fix handling of non-ASCII .iso filenames in Windows. 2020-06-30: Version 1.1.1 - Fix pkg-config file to be named libudfread.pc instead of udfread.pc 2020-06-30: Version 1.1.0 - Add functions to open directories/files from already open directory. - Fix possible memory corruption with inline files. - Improve error resilience and stability. - Add support for Volume Descriptor Pointer. 2017-06-07: Version 1.0.0 libudfread-1.1.2/Makefile.am000066400000000000000000000015141403256456600156640ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 SET_FEATURES = -D_ISOC99_SOURCE -D_POSIX_C_SOURCE=200809L -D_REENTRANT SET_INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src -Isrc AM_CFLAGS = $(SET_FEATURES) $(SET_INCLUDES) lib_LTLIBRARIES = libudfread.la libudfread_la_SOURCES = \ src/default_blockinput.h \ src/default_blockinput.c \ src/ecma167.h \ src/ecma167.c \ src/udfread.c \ src/udfread-version.c libudfread_la_LDFLAGS = -version-info $(UDFREAD_LTVERSION) -export-symbols-regex "(^udfread_.*)" pkgincludedir = $(includedir)/udfread pkginclude_HEADERS = \ src/udfread.h \ src/udfread-version.h \ src/blockinput.h pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = src/libudfread.pc noinst_PROGRAMS = \ udfls \ udfcat udfls_SOURCES = examples/udfls.c udfls_LDADD = libudfread.la udfcat_SOURCES = examples/udfcat.c udfcat_LDADD = libudfread.la libudfread-1.1.2/bootstrap000077500000000000000000000000331403256456600155660ustar00rootroot00000000000000#!/bin/sh autoreconf -vif libudfread-1.1.2/configure.ac000066400000000000000000000061621403256456600161220ustar00rootroot00000000000000dnl library version number m4_define([udfread_major], 1) m4_define([udfread_minor], 1) m4_define([udfread_micro], 2) m4_define([udfread_version],[udfread_major.udfread_minor.udfread_micro]) dnl shared library version (.so version) dnl dnl - If there are no interface changes, increase revision. dnl - If interfaces have been added, increase current and age. Set revision to 0. dnl - If interfaces have been changed or removed, increase current and set age and revision to 0. dnl dnl Library file name will be libbluray.so.(current-age).age.revision dnl m4_define([lt_current], 1) m4_define([lt_revision], 0) m4_define([lt_age], 1) dnl initilization AC_INIT([libudfread], udfread_version, [http://www.videolan.org/developers/libudfread.html]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_HOST AM_INIT_AUTOMAKE([foreign tar-ustar dist-bzip2 no-dist-gzip subdir-objects]) AC_CONFIG_HEADERS(config.h) dnl Enable silent rules only when available (automake 1.11 or later). m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) dnl os type case "${host_os}" in "") SYS=unknown ;; *mingw32* | *cygwin* | *wince* | *mingwce*) case "${host_os}" in *wince* | *mingwce* | *mingw32ce*) SYS=mingwce ;; *mingw32*) SYS=mingw32 ;; esac ;; *) SYS="${host_os}" ;; esac dnl configure options AC_ARG_ENABLE([optimizations], [AS_HELP_STRING([--disable-optimizations], [disable optimizations @<:@default=enabled@:>@])]) AC_ARG_ENABLE([extra-warnings], [AS_HELP_STRING([--disable-extra-warnings], [set extra warnings @<:@default=enabled@:>@])]) dnl required programs AC_PROG_CC AM_PROG_CC_C_O LT_INIT dnl required system services AC_SYS_LARGEFILE dnl required headers AC_CHECK_HEADERS([unistd.h fcntl.h pthread.h]) dnl required functions AS_IF([test "${SYS}" != "mingw32"], [ AC_CHECK_FUNC([pread],, [AC_MSG_ERROR("Function pread not found.")])]) dnl compiler warnings CC_CHECK_CFLAGS_APPEND([-Wall -Wsign-compare -Wextra]) CC_CHECK_CFLAGS_APPEND([-std=c99 -pedantic]) CC_CHECK_CFLAGS_APPEND([-Wdisabled-optimization -Wpointer-arith ]dnl [-Wredundant-decls -Wcast-qual -Wwrite-strings -Wtype-limits -Wundef ]dnl [-Wmissing-prototypes -Wshadow]) CC_CHECK_CFLAGS_APPEND([-Werror=implicit-function-declaration ]dnl [-Werror-implicit-function-declaration], [break;]) AS_IF([test "x$enable_extra_warnings" != "xno"], [ CC_CHECK_CFLAGS_APPEND([-Wextra -Winline]) ]) AS_IF([test "x$enable_optimizations" != "xno"], [ CC_CHECK_CFLAGS_APPEND([-O3 -fomit-frame-pointer]) ]) dnl export library version number UDFREAD_VERSION_MAJOR=udfread_major() AC_SUBST(UDFREAD_VERSION_MAJOR) UDFREAD_VERSION_MINOR=udfread_minor() AC_SUBST(UDFREAD_VERSION_MINOR) UDFREAD_VERSION_MICRO=udfread_micro() AC_SUBST(UDFREAD_VERSION_MICRO) AC_DEFINE([HAVE_UDFREAD_VERSION_H], 1, ["Define to 1 if you have udfread-version.h header file."]) dnl export library (.so) version UDFREAD_LTVERSION="lt_current():lt_revision():lt_age()" AC_SUBST(UDFREAD_LTVERSION) dnl generate output files AC_CONFIG_FILES([Makefile src/libudfread.pc src/udfread-version.h]) AC_OUTPUT libudfread-1.1.2/examples/000077500000000000000000000000001403256456600154455ustar00rootroot00000000000000libudfread-1.1.2/examples/udfcat.c000066400000000000000000000043031403256456600170570ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014-2015 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include "udfread.h" #include #include #include static void _cat(UDFFILE *fp) { uint8_t buf[2048]; int64_t got = 0; ssize_t result; while ((result = udfread_file_read(fp, buf, sizeof(buf))) > 0) { fwrite(buf, 1, (size_t)result, stdout); got += result; } if (result < 0) { fprintf(stderr, "udfread_file_read(offset=%"PRId64") failed\n", udfread_file_tell(fp)); } fprintf(stderr, "wrote %"PRId64" bytes of %"PRId64"\n", got, udfread_file_size(fp)); } int main(int argc, const char *argv[]) { udfread *udf; UDFFILE *fp; if (argc < 3) { fprintf(stderr, "usage: udfcat \n" " image path to UDF filesystem image (raw device or image file)\n" " file path to file inside UDF filesystem\n"); return -1; } udf = udfread_init(); if (!udf) { fprintf(stderr, "udfread_init() failed\n"); return -1; } if (udfread_open(udf, argv[1]) < 0) { fprintf(stderr, "udfread_open(%s) failed\n", argv[1]); udfread_close(udf); return -1; } fp = udfread_file_open(udf, argv[2]); if (!fp) { fprintf(stderr, "udfread_file_open(%s) failed\n", argv[2]); udfread_close(udf); return -1; } _cat(fp); udfread_file_close(fp); udfread_close(udf); } libudfread-1.1.2/examples/udfls.c000066400000000000000000000061531403256456600167330ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014-2015 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include #include #include #include #include #include "udfread.h" static int _lsdir_at(UDFDIR *dir, const char *path, int depth) { struct udfread_dirent dirent; while (udfread_readdir(dir, &dirent)) { if (!strcmp(dirent.d_name, ".") || !strcmp(dirent.d_name, "..")) continue; if (dirent.d_type == UDF_DT_DIR) { UDFDIR *child; char *next_dir; printf("\t\t %s%s\n", path, dirent.d_name); next_dir = (char*)malloc(strlen(path) + strlen(dirent.d_name) + 2); if (!next_dir) { fprintf(stderr, "out of memory\n"); continue; } sprintf(next_dir, "%s%s/", path, dirent.d_name); child = udfread_opendir_at(dir, dirent.d_name); if (!child) { fprintf(stderr, "error opening directory %s\n", dirent.d_name); continue; } _lsdir_at(child, next_dir, depth + 1); udfread_closedir(child); free(next_dir); } else { UDFFILE *fp; fp = udfread_file_openat(dir, dirent.d_name); if (!fp) { fprintf(stderr, "error opening file '%s%s'\n", path, dirent.d_name); continue; } printf("%16" PRId64 " %s%s\n", udfread_file_size(fp), path, dirent.d_name); udfread_file_close(fp); } } return 0; } int main(int argc, const char *argv[]) { udfread *udf; UDFDIR *root; if (argc < 2) { fprintf(stderr, "usage: udfls \n" " path path to UDF filesystem image (raw device or image file)\n"); return -1; } udf = udfread_init(); if (!udf) { fprintf(stderr, "udfread_init() failed\n"); return -1; } if (udfread_open(udf, argv[1]) < 0) { fprintf(stderr, "udfread_open(%s) failed\n", argv[1]); udfread_close(udf); return -1; } printf("Volume ID: %s\n", udfread_get_volume_id(udf)); root = udfread_opendir(udf, "/"); if (!root) { fprintf(stderr, "error opening root directory\n"); } else { _lsdir_at(root, "/", 0); udfread_closedir(root); } udfread_close(udf); return 1; } libudfread-1.1.2/m4/000077500000000000000000000000001403256456600141475ustar00rootroot00000000000000libudfread-1.1.2/m4/attributes.m4000066400000000000000000000226661403256456600166130ustar00rootroot00000000000000dnl Macros to check the presence of generic (non-typed) symbols. dnl Copyright (c) 2006-2007 Diego Pettenò dnl Copyright (c) 2006-2007 xine project dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2, or (at your option) dnl any later version. dnl dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA dnl 02110-1301, USA. dnl dnl As a special exception, the copyright owners of the dnl macro gives unlimited permission to copy, distribute and modify the dnl configure scripts that are the output of Autoconf when processing the dnl Macro. You need not follow the terms of the GNU General Public dnl License when using or distributing such scripts, even though portions dnl of the text of the Macro appear in them. The GNU General Public dnl License (GPL) does govern all other use of the material that dnl constitutes the Autoconf Macro. dnl dnl This special exception to the GPL applies to versions of the dnl Autoconf Macro released by this project. When you make and dnl distribute a modified version of the Autoconf Macro, you may extend dnl this special exception to the GPL to apply to your modified version as dnl well. dnl Check if the flag is supported by compiler dnl CC_CHECK_CFLAGS_SILENT([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) AC_DEFUN([CC_CHECK_CFLAGS_SILENT], [ AC_CACHE_VAL(AS_TR_SH([cc_cv_cflags_$1]), [ac_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $1" AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 0; }])], [eval "AS_TR_SH([cc_cv_cflags_$1])='yes'"], [eval "AS_TR_SH([cc_cv_cflags_$1])='no'"]) CFLAGS="$ac_save_CFLAGS" ]) AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], [$2], [$3]) ]) dnl Check if the flag is supported by compiler (cacheable) dnl CC_CHECK_CFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) AC_DEFUN([CC_CHECK_CFLAGS], [ AC_CACHE_CHECK([if $CC supports $1 flag], AS_TR_SH([cc_cv_cflags_$1]), CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here! ) AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], [$2], [$3]) ]) dnl CC_CHECK_CFLAG_APPEND(FLAG, [action-if-found], [action-if-not-found]) dnl Check for CFLAG and appends them to CFLAGS if supported AC_DEFUN([CC_CHECK_CFLAG_APPEND], [ AC_CACHE_CHECK([if $CC supports $1 flag], AS_TR_SH([cc_cv_cflags_$1]), CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here! ) AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], [CFLAGS="$CFLAGS $1"; $2], [$3]) ]) dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not]) AC_DEFUN([CC_CHECK_CFLAGS_APPEND], [ for flag in $1; do CC_CHECK_CFLAG_APPEND($flag, [$2], [$3]) done ]) dnl Check if the flag is supported by linker (cacheable) dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) AC_DEFUN([CC_CHECK_LDFLAGS], [ AC_CACHE_CHECK([if $CC supports $1 flag], AS_TR_SH([cc_cv_ldflags_$1]), [ac_save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $1" AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 1; }])], [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"], [eval "AS_TR_SH([cc_cv_ldflags_$1])="]) LDFLAGS="$ac_save_LDFLAGS" ]) AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes], [$2], [$3]) ]) dnl Check for a -Werror flag or equivalent. -Werror is the GCC dnl and ICC flag that tells the compiler to treat all the warnings dnl as fatal. We usually need this option to make sure that some dnl constructs (like attributes) are not simply ignored. dnl dnl Other compilers don't support -Werror per se, but they support dnl an equivalent flag: dnl - Sun Studio compiler supports -errwarn=%all AC_DEFUN([CC_CHECK_WERROR], [ AC_CACHE_CHECK( [for $CC way to treat warnings as errors], [cc_cv_werror], [CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror], [CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])]) ]) ]) AC_DEFUN([CC_CHECK_ATTRIBUTE], [ AC_REQUIRE([CC_CHECK_WERROR]) AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))], AS_TR_SH([cc_cv_attribute_$1]), [ac_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $cc_cv_werror" AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])], [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"], [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"]) CFLAGS="$ac_save_CFLAGS" ]) AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes], [AC_DEFINE( AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1, [Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))] ) $4], [$5]) ]) AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [ CC_CHECK_ATTRIBUTE( [constructor],, [extern void foo(); void __attribute__((constructor)) ctor() { foo(); }], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_DESTRUCTOR], [ CC_CHECK_ATTRIBUTE( [destructor],, [extern void foo(); void __attribute__((destructor)) dtor() { foo(); }], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_FORMAT], [ CC_CHECK_ATTRIBUTE( [format], [format(printf, n, n)], [void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [ CC_CHECK_ATTRIBUTE( [format_arg], [format_arg(printf)], [char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [ CC_CHECK_ATTRIBUTE( [visibility_$1], [visibility("$1")], [void __attribute__((visibility("$1"))) $1_function() { }], [$2], [$3]) ]) AC_DEFUN([CC_ATTRIBUTE_NONNULL], [ CC_CHECK_ATTRIBUTE( [nonnull], [nonnull()], [void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_UNUSED], [ CC_CHECK_ATTRIBUTE( [unused], , [void some_function(void *foo, __attribute__((unused)) void *bar);], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [ CC_CHECK_ATTRIBUTE( [sentinel], , [void some_function(void *foo, ...) __attribute__((sentinel));], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [ CC_CHECK_ATTRIBUTE( [deprecated], , [void some_function(void *foo, ...) __attribute__((deprecated));], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_ALIAS], [ CC_CHECK_ATTRIBUTE( [alias], [weak, alias], [void other_function(void *foo) { } void some_function(void *foo) __attribute__((weak, alias("other_function")));], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_MALLOC], [ CC_CHECK_ATTRIBUTE( [malloc], , [void * __attribute__((malloc)) my_alloc(int n);], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_PACKED], [ CC_CHECK_ATTRIBUTE( [packed], , [struct astructure { char a; int b; long c; void *d; } __attribute__((packed)); char assert@<:@(sizeof(struct astructure) == (sizeof(char)+sizeof(int)+sizeof(long)+sizeof(void*)))-1@:>@;], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_CONST], [ CC_CHECK_ATTRIBUTE( [const], , [int __attribute__((const)) twopow(int n) { return 1 << n; } ], [$1], [$2]) ]) AC_DEFUN([CC_FLAG_VISIBILITY], [ AC_REQUIRE([CC_CHECK_WERROR]) AC_CACHE_CHECK([if $CC supports -fvisibility=hidden], [cc_cv_flag_visibility], [cc_flag_visibility_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $cc_cv_werror" CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden], cc_cv_flag_visibility='yes', cc_cv_flag_visibility='no') CFLAGS="$cc_flag_visibility_save_CFLAGS"]) AS_IF([test "x$cc_cv_flag_visibility" = "xyes"], [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1, [Define this if the compiler supports the -fvisibility flag]) $1], [$2]) ]) AC_DEFUN([CC_FUNC_EXPECT], [ AC_REQUIRE([CC_CHECK_WERROR]) AC_CACHE_CHECK([if compiler has __builtin_expect function], [cc_cv_func_expect], [ac_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $cc_cv_werror" AC_COMPILE_IFELSE( [int some_function() { int a = 3; return (int)__builtin_expect(a, 3); }], [cc_cv_func_expect=yes], [cc_cv_func_expect=no]) CFLAGS="$ac_save_CFLAGS" ]) AS_IF([test "x$cc_cv_func_expect" = "xyes"], [AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1, [Define this if the compiler supports __builtin_expect() function]) $1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [ AC_REQUIRE([CC_CHECK_WERROR]) AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported], [cc_cv_attribute_aligned], [ac_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $cc_cv_werror" for cc_attribute_align_try in 64 32 16 8 4 2; do AC_COMPILE_IFELSE([ int main() { static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0; return c; }], [cc_cv_attribute_aligned=$cc_attribute_align_try; break]) done CFLAGS="$ac_save_CFLAGS" ]) if test "x$cc_cv_attribute_aligned" != "x"; then AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned], [Define the highest alignment supported]) fi ]) libudfread-1.1.2/src/000077500000000000000000000000001403256456600144165ustar00rootroot00000000000000libudfread-1.1.2/src/blockinput.h000066400000000000000000000030421403256456600167400ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014-2015 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #ifndef UDFREAD_BLOCKINPUT_H_ #define UDFREAD_BLOCKINPUT_H_ #ifdef __cplusplus extern "C" { #endif #include /** * @file udfread/blockinput.h * external API header */ #ifndef UDF_BLOCK_SIZE # define UDF_BLOCK_SIZE 2048 #endif typedef struct udfread_block_input udfread_block_input; struct udfread_block_input { /* Close input. Optional. */ int (*close) (udfread_block_input *); /* Read block(s) from input. Mandatory. */ int (*read) (udfread_block_input *, uint32_t lba, void *buf, uint32_t nblocks, int flags); /* Input size in blocks. Optional. */ uint32_t (*size) (udfread_block_input *); }; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* UDFREAD_BLOCKINPUT_H_ */ libudfread-1.1.2/src/default_blockinput.c000066400000000000000000000111411403256456600204360ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014-2017 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #if HAVE_CONFIG_H #include "config.h" #endif #include "default_blockinput.h" #include "blockinput.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef _WIN32 #include #ifndef HAVE_UNISTD_H #include #endif #include # undef lseek # define lseek _lseeki64 # undef off_t # define off_t int64_t #endif #ifdef __ANDROID__ # undef lseek # define lseek lseek64 # undef off_t # define off_t off64_t #endif #ifdef _WIN32 static ssize_t pread(int fd, void *buf, size_t count, off_t offset) { OVERLAPPED ov; DWORD got; HANDLE handle; handle = (HANDLE)(intptr_t)_get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { return -1; } memset(&ov, 0, sizeof(ov)); ov.Offset = (DWORD)offset; ov.OffsetHigh = (offset >> 32); if (!ReadFile(handle, buf, count, &got, &ov)) { return -1; } return got; } #elif defined (NEED_PREAD_IMPL) #include static ssize_t pread_impl(int fd, void *buf, size_t count, off_t offset) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; ssize_t result; pthread_mutex_lock(&lock); if (lseek(fd, offset, SEEK_SET) != offset) { result = -1; } else { result = read(fd, buf, count); } pthread_mutex_unlock(&lock); return result; } #define pread(a,b,c,d) pread_impl(a,b,c,d) #endif /* _WIN32 || NEED_PREAD_IMPL */ typedef struct default_block_input { udfread_block_input input; int fd; } default_block_input; static int _def_close(udfread_block_input *p_gen) { default_block_input *p = (default_block_input *)p_gen; int result = -1; if (p) { if (p->fd >= 0) { result = close(p->fd); } free(p); } return result; } static uint32_t _def_size(udfread_block_input *p_gen) { default_block_input *p = (default_block_input *)p_gen; off_t pos; pos = lseek(p->fd, 0, SEEK_END); if (pos < 0) { return 0; } return (uint32_t)(pos / UDF_BLOCK_SIZE); } static int _def_read(udfread_block_input *p_gen, uint32_t lba, void *buf, uint32_t nblocks, int flags) { default_block_input *p = (default_block_input *)p_gen; size_t bytes, got; off_t pos; (void)flags; bytes = (size_t)nblocks * UDF_BLOCK_SIZE; got = 0; pos = (off_t)lba * UDF_BLOCK_SIZE; while (got < bytes) { ssize_t ret = pread(p->fd, ((char*)buf) + got, bytes - got, pos + (off_t)got); if (ret <= 0) { if (ret < 0 && errno == EINTR) { continue; } if (got < UDF_BLOCK_SIZE) { return ret; } break; } got += (size_t)ret; } return got / UDF_BLOCK_SIZE; } #ifdef _WIN32 static int _open_win32(const char *path, int flags) { wchar_t *wpath; int wlen, fd; wlen = MultiByteToWideChar (CP_UTF8, 0, path, -1, NULL, 0); if (wlen < 1) { return -1; } wpath = (wchar_t*)malloc(sizeof(wchar_t) * wlen); if (!wpath) { return -1; } if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, wlen)) { free(wpath); return -1; } fd = _wopen(wpath, flags); free(wpath); return fd; } #endif udfread_block_input *block_input_new(const char *path) { default_block_input *p = (default_block_input*)calloc(1, sizeof(default_block_input)); if (!p) { return NULL; } #ifdef _WIN32 p->fd = _open_win32(path, O_RDONLY | O_BINARY); #else p->fd = open(path, O_RDONLY); #endif if(p->fd < 0) { free(p); return NULL; } p->input.close = _def_close; p->input.read = _def_read; p->input.size = _def_size; return &p->input; } libudfread-1.1.2/src/default_blockinput.h000066400000000000000000000020111403256456600204370ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #ifndef UDFREAD_DEFAULT_BLOCKINPUT_H_ #define UDFREAD_DEFAULT_BLOCKINPUT_H_ #include "blockinput.h" udfread_block_input *block_input_new(const char *path); #endif /* UDFREAD_DEFAULT_BLOCKINPUT_H_ */ libudfread-1.1.2/src/ecma167.c000066400000000000000000000261511403256456600157320ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014-2015 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #if HAVE_CONFIG_H #include "config.h" #endif #include "ecma167.h" #include #include #include #include #define ecma_error(...) fprintf(stderr, "ecma: " __VA_ARGS__) /* * Part 1: General */ /* fixed-length dstring, ECMA 1/7.2.12 */ static uint8_t _decode_dstring(const uint8_t *p, size_t field_length, uint8_t *str) { size_t length; if (field_length < 1) { return 0; } field_length--; length = _get_u8(p + field_length); if (length > field_length) { length = field_length; } memcpy(str, p, length); return (uint8_t)length; } /* Extent Descriptor (ECMA 167, 3/7.1) */ static void _decode_extent_ad(const uint8_t *p, struct extent_ad *ad) { ad->length = _get_u32(p + 0); ad->lba = _get_u32(p + 4); } /* Entity Identifier (ECMA 167, 1/7.4) */ void decode_entity_id(const uint8_t *p, struct entity_id *eid) { memcpy(eid->identifier, p + 1, 23); memcpy(eid->identifier_suffix, p + 24, 8); } /* * Part 3: Volume Structure */ /* Descriptor Tag (ECMA 167, 3/7.2) */ enum tag_identifier decode_descriptor_tag(const uint8_t *buf) { uint16_t id; uint8_t checksum = 0; int i; id = _get_u16(buf + 0); /* descriptor tag is 16 bytes */ /* calculate tag checksum */ for (i = 0; i < 4; i++) { checksum = (uint8_t)(checksum + buf[i]); } for (i = 5; i < 16; i++) { checksum = (uint8_t)(checksum + buf[i]); } if (checksum != buf[4]) { return ECMA_TAG_INVALID; } return (enum tag_identifier)id; } /* Primary Volume Descriptor (ECMA 167, 3/10.1) */ void decode_primary_volume(const uint8_t *p, struct primary_volume_descriptor *pvd) { pvd->volume_identifier_length = _decode_dstring(p + 24, 32, pvd->volume_identifier); memcpy(pvd->volume_set_identifier, p + 72, 128); } /* Anchor Volume Descriptor Pointer (ECMA 167 3/10.2) */ void decode_avdp(const uint8_t *p, struct anchor_volume_descriptor *avdp) { /* Main volume descriptor sequence extent */ _decode_extent_ad(p + 16, &avdp->mvds); /* Reserve (backup) volume descriptor sequence extent */ _decode_extent_ad(p + 24, &avdp->rvds); } /* Volume Descriptor Pointer (ECMA 167 3/10.3) */ void decode_vdp(const uint8_t *p, struct volume_descriptor_pointer *vdp) { _decode_extent_ad(p + 20, &vdp->next_extent); } /* Partition Descriptor (ECMA 167 3/10.5) */ void decode_partition(const uint8_t *p, struct partition_descriptor *pd) { pd->number = _get_u16(p + 22); pd->start_block = _get_u32(p + 188); pd->num_blocks = _get_u32(p + 192); } /* Logical Volume Descriptor (ECMA 167 3/10.6) */ void decode_logical_volume(const uint8_t *p, struct logical_volume_descriptor *lvd) { size_t map_size; lvd->block_size = _get_u32(p + 212); decode_entity_id(p + 216, &lvd->domain_id); memcpy(lvd->contents_use, p + 248, 16); lvd->partition_map_lable_length = _get_u32(p + 264); lvd->num_partition_maps = _get_u32(p + 268); /* XXX cut long maps */ map_size = lvd->partition_map_lable_length; if (map_size > sizeof(lvd->partition_map_table)) { map_size = sizeof(lvd->partition_map_table); } /* input size is one block (2048 bytes) */ if (map_size > 2048 - 440) { map_size = 2048 - 440; } memcpy(lvd->partition_map_table, p + 440, map_size); } /* * Part 4: File Structure */ /* File Set Descriptor (ECMA 167 4/14.1) */ void decode_file_set_descriptor(const uint8_t *p, struct file_set_descriptor *fsd) { decode_long_ad(p + 400, &fsd->root_icb); } /* File Identifier (ECMA 167 4/14.4) */ size_t decode_file_identifier(const uint8_t *p, size_t size, struct file_identifier *fi) { size_t l_iu; /* length of implementation use field */ if (size < 38) { ecma_error("decode_file_identifier: not enough data\n"); return 0; } fi->characteristic = _get_u8(p + 18); fi->filename_len = _get_u8(p + 19); decode_long_ad(p + 20, &fi->icb); l_iu = _get_u16(p + 36); if (size < 38 + l_iu + fi->filename_len) { ecma_error("decode_file_identifier: not enough data\n"); return 0; } if (fi->filename_len) { memcpy(fi->filename, p + 38 + l_iu, fi->filename_len); } fi->filename[fi->filename_len] = 0; /* ECMA 167, 4/14.4 * padding size 4 * ip((L_FI+L_IU+38+3)/4) - (L_FI+L_IU+38) * => padded to dwords */ return 4 * ((38 + (size_t)fi->filename_len + l_iu + 3) / 4); } /* ICB Tag (ECMA 167 4/14.6) */ struct icb_tag { uint8_t file_type; uint16_t strategy_type; uint16_t flags; }; static void _decode_icb_tag(const uint8_t *p, struct icb_tag *tag) { tag->strategy_type = _get_u16(p + 4); tag->file_type = _get_u8 (p + 11); tag->flags = _get_u16(p + 18); } /* Allocation Descriptors */ #define AD_LENGTH_MASK 0x3fffffff #define AD_TYPE(length) ((length) >> 30) /* Short Allocation Descriptor (ECMA 167, 4/14.14.1) */ static void _decode_short_ad(const uint8_t *buf, uint16_t partition, struct long_ad *ad) { uint32_t u32 = _get_u32(buf + 0); ad->extent_type = AD_TYPE(u32); ad->length = u32 & AD_LENGTH_MASK; ad->lba = _get_u32(buf + 4); ad->partition = partition; } /* Long Allocation Descriptor (ECMA 167, 4/14.14.2) */ void decode_long_ad(const uint8_t *buf, struct long_ad *ad) { uint32_t u32 = _get_u32(buf + 0); ad->extent_type = AD_TYPE(u32); ad->length = u32 & AD_LENGTH_MASK; ad->lba = _get_u32(buf + 4); ad->partition = _get_u16(buf + 8); } /* Exrtended Allocation Descriptor (ECMA 167, 4/14.14.3) */ static void _decode_extended_ad(const uint8_t *buf, struct long_ad *ad) { uint32_t u32 = _get_u32(buf + 0); ad->extent_type = AD_TYPE(u32); ad->length = u32 & AD_LENGTH_MASK; ad->lba = _get_u32(buf + 12); ad->partition = _get_u16(buf + 16); } /* File Entry */ static void _decode_file_ads(const uint8_t *p, int ad_type, uint16_t partition, struct long_ad *ad, unsigned num_ad) { uint32_t i; for (i = 0; i < num_ad; i++) { switch (ad_type) { case 0: _decode_short_ad(p, partition, &ad[i]); p += 8; break; case 1: decode_long_ad(p, &ad[i]); p += 16; break; case 2: _decode_extended_ad(p, &ad[i]); p += 20; break; } } } static struct file_entry *_decode_file_entry(const uint8_t *p, size_t size, uint16_t partition, uint32_t l_ad, uint32_t p_ad) { struct file_entry *fe; struct icb_tag tag; uint32_t num_ad; int content_inline = 0; if (p_ad + l_ad > size) { ecma_error("decode_file_entry: not enough data\n"); return NULL; } _decode_icb_tag(p + 16, &tag); if (tag.strategy_type != 4) { /* UDF (2.): only ICB strategy types 4 and 4096 shall be recorded */ ecma_error("decode_file_entry: unsupported icb strategy type %d\n", tag.strategy_type); return NULL; } switch (tag.flags & 7) { case 0: num_ad = l_ad / 8; break; case 1: num_ad = l_ad / 16; break; case 2: num_ad = l_ad / 20; break; case 3: num_ad = 0; content_inline = 1; break; default: ecma_error("decode_file_entry: unsupported icb flags: 0x%x\n", tag.flags); return NULL; } if (num_ad < 1) { fe = (struct file_entry *)calloc(1, sizeof(struct file_entry) + l_ad); } else { fe = (struct file_entry *)calloc(1, sizeof(struct file_entry) + sizeof(struct long_ad) * (num_ad - 1)); } if (!fe) { return NULL; } fe->file_type = tag.file_type; fe->length = _get_u64(p + 56); fe->ad_type = tag.flags & 7; if (content_inline) { /* data of small files can be embedded in file entry */ /* copy embedded file data */ fe->content_inline = 1; fe->u.data.information_length = l_ad; memcpy(fe->u.data.content, p + p_ad, l_ad); } else { fe->u.ads.num_ad = num_ad; _decode_file_ads(p + p_ad, fe->ad_type, partition, &fe->u.ads.ad[0], num_ad); } return fe; } int decode_allocation_extent(struct file_entry **p_fe, const uint8_t *p, size_t size, uint16_t partition) { struct file_entry *fe = *p_fe; uint32_t l_ad, num_ad; l_ad = _get_u32(p + 20); if (size < 24 || size - 24 < l_ad) { ecma_error("decode_allocation_extent: invalid allocation extent (l_ad)\n"); return -1; } switch (fe->ad_type) { case 0: num_ad = l_ad / 8; break; case 1: num_ad = l_ad / 16; break; case 2: num_ad = l_ad / 20; break; default: return -1; } if (num_ad < 1) { ecma_error("decode_allocation_extent: empty allocation extent\n"); return 0; } fe = (struct file_entry *)realloc(*p_fe, sizeof(struct file_entry) + sizeof(struct long_ad) * (fe->u.ads.num_ad + num_ad)); if (!fe) { return -1; } *p_fe = fe; /* decode new allocation descriptors */ _decode_file_ads(p + 24, fe->ad_type, partition, &fe->u.ads.ad[fe->u.ads.num_ad], num_ad); fe->u.ads.num_ad += num_ad; return 0; } /* File Entry (ECMA 167, 4/14.9) */ struct file_entry *decode_file_entry(const uint8_t *p, size_t size, uint16_t partition) { uint32_t l_ea, l_ad; l_ea = _get_u32(p + 168); l_ad = _get_u32(p + 172); /* check for integer overflow */ if ((uint64_t)l_ea + (uint64_t)l_ad + (uint64_t)176 >= (uint64_t)1<<32) { ecma_error("invalid file entry\n"); return NULL; } return _decode_file_entry(p, size, partition, l_ad, 176 + l_ea); } /* Extended File Entry (ECMA 167, 4/14.17) */ struct file_entry *decode_ext_file_entry(const uint8_t *p, size_t size, uint16_t partition) { uint32_t l_ea, l_ad; l_ea = _get_u32(p + 208); l_ad = _get_u32(p + 212); /* check for integer overflow */ if ((uint64_t)l_ea + (uint64_t)l_ad + (uint64_t)216 >= (uint64_t)1<<32) { ecma_error("invalid extended file entry\n"); return NULL; } return _decode_file_entry(p, size, partition, l_ad, 216 + l_ea); } void free_file_entry(struct file_entry **p_fe) { if (p_fe) { free(*p_fe); *p_fe = NULL; } } libudfread-1.1.2/src/ecma167.h000066400000000000000000000160411403256456600157340ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014-2015 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #ifndef UDFREAD_ECMA167_H_ #define UDFREAD_ECMA167_H_ #include /* *int_t */ #include /* size_t */ /* * Minimal implementation of ECMA-167: * Volume and File Structure for Write-Once and Rewritable * Media using Non-Sequential Recording for Information Interchange * * Based on 3rd Edition, June 1997 * http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-167.pdf * */ /* * Part 1: General */ /* Numerical Values (ECMA 167, 1/7.1) */ static inline uint32_t _get_u8(const uint8_t *p) { return (uint32_t)p[0]; } static inline uint32_t _get_u16(const uint8_t *p) { return _get_u8(p) | (_get_u8(p + 1) << 8); } static inline uint32_t _get_u32(const uint8_t *p) { return _get_u16(p) | (_get_u16(p + 2) << 16); } static inline uint64_t _get_u64(const uint8_t *p) { return (uint64_t)_get_u32(p) | ((uint64_t)_get_u32(p + 4) << 32); } /* Entity Identifier (ECMA 167, 1/7.4) */ struct entity_id { uint8_t identifier[23]; uint8_t identifier_suffix[8]; }; void decode_entity_id(const uint8_t *p, struct entity_id *eid); /* * Part 3: Volume Structure */ /* Extent Descriptor (ECMA 167, 3/7.1) */ struct extent_ad { uint32_t lba; uint32_t length; /* in bytes */ }; /* Descriptor Tag (ECMA 167, 3/7.2) */ enum tag_identifier { /* ECMA 167, 3/7.2.1) */ ECMA_PrimaryVolumeDescriptor = 1, ECMA_AnchorVolumeDescriptorPointer = 2, ECMA_VolumeDescriptorPointer = 3, ECMA_PartitionDescriptor = 5, ECMA_LogicalVolumeDescriptor = 6, ECMA_TerminatingDescriptor = 8, /* ECMA 167, 4/7.2.1 */ ECMA_FileSetDescriptor = 256, ECMA_FileIdentifierDescriptor = 257, ECMA_AllocationExtentDescriptor = 258, ECMA_FileEntry = 261, ECMA_ExtendedFileEntry = 266, ECMA_TAG_INVALID = -1, /* checksum failed */ }; enum tag_identifier decode_descriptor_tag(const uint8_t *buf); /* Primary Volume Descriptor (ECMA 167, 3/10.1) */ struct primary_volume_descriptor { uint8_t volume_identifier[31]; uint8_t volume_identifier_length; uint8_t volume_set_identifier[128]; }; void decode_primary_volume(const uint8_t *p, struct primary_volume_descriptor *pvd); /* Anchor Volume Descriptor (ECMA 167, 3/10.2) */ struct anchor_volume_descriptor { struct extent_ad mvds; /* Main Volume Descriptor Sequence extent */ struct extent_ad rvds; /* Reserve Volume Descriptor Sequence extent */ }; void decode_avdp(const uint8_t *p, struct anchor_volume_descriptor *avdp); /* Volume Descriptor Pointer (ECMA 167, 3/10.3) */ struct volume_descriptor_pointer { struct extent_ad next_extent; /* Next Volume Descriptor Sequence Extent */ }; void decode_vdp(const uint8_t *p, struct volume_descriptor_pointer *vdp); /* Partition Descriptor (ECMA 167, 3/10.5) */ struct partition_descriptor { uint16_t number; uint32_t start_block; uint32_t num_blocks; }; void decode_partition(const uint8_t *p, struct partition_descriptor *pd); /* Logical Volume Descriptor (ECMA 167, 3/10.6) */ struct logical_volume_descriptor { uint32_t block_size; struct entity_id domain_id; uint8_t contents_use[16]; uint32_t num_partition_maps; uint32_t partition_map_lable_length; uint8_t partition_map_table[2048]; }; void decode_logical_volume(const uint8_t *p, struct logical_volume_descriptor *lvd); /* * Part 4: File Structure */ enum { ECMA_AD_EXTENT_NORMAL = 0, /* allocated and recorded file data */ ECMA_AD_EXTENT_NOT_RECORDED = 1, ECMA_AD_EXTENT_NOT_ALLOCATED = 2, ECMA_AD_EXTENT_AD = 3, /* pointer to next extent of allocation descriptors */ }; /* Short/Long/Extended Allocation Descriptor (ECMA 167, 4/14.14.[1,2,3]) */ struct long_ad { uint32_t lba; /* start block, relative to partition start */ uint32_t length; /* in bytes */ uint16_t partition; uint8_t extent_type; }; void decode_long_ad(const uint8_t *p, struct long_ad *ad); /* File Set Descriptor (ECMA 167 4/14.1) */ struct file_set_descriptor { struct long_ad root_icb; }; void decode_file_set_descriptor(const uint8_t *p, struct file_set_descriptor *fsd); /* File Identifier (ECMA 167 4/14.4) */ enum { CHAR_FLAG_HIDDEN = 0x01, CHAR_FLAG_DIR = 0x02, CHAR_FLAG_DELETED = 0x04, CHAR_FLAG_PARENT = 0x08, }; struct file_identifier { struct long_ad icb; uint8_t characteristic; /* CHAR_FLAG_* */ uint8_t filename_len; uint8_t filename[256]; }; size_t decode_file_identifier(const uint8_t *p, size_t size, struct file_identifier *fi); /* File Entry (ECMA 167, 4/14.9) */ /* Extended File Entry (ECMA 167, 4/14.17) */ enum { /* ECMA 167, 14.6.6 File Type */ ECMA_FT_UNSPECIFIED = 0, ECMA_FT_INDIRECT = 3, ECMA_FT_DIR = 4, ECMA_FT_BYTESTREAM = 5, /* random-access byte stream - regular file - udf 2.60, 2.3.5.2 */ ECMA_FT_TERMINAL_ENTRY = 11, ECMA_FT_SYMLINK = 12, }; struct file_entry { uint64_t length; /* in bytes */ uint8_t file_type; /* ECMA_FT_* */ uint8_t content_inline; /* 1 if file data is embedded in file entry */ uint8_t ad_type; /* from icb_flags; used when parsing allocation extents */ union { /* "normal" file */ struct { uint32_t num_ad; struct long_ad ad[1]; /* Most files have only single extent, files in 3D BDs can have 100+. */ } ads; /* inline file */ struct { uint32_t information_length; /* recorded information length, may be different than file length */ uint8_t content[1]; /* content of small files is embedded here */ } data; } u; }; struct file_entry *decode_file_entry (const uint8_t *p, size_t size, uint16_t partition); struct file_entry *decode_ext_file_entry(const uint8_t *p, size_t size, uint16_t partition); void free_file_entry (struct file_entry **p_fe); int decode_allocation_extent(struct file_entry **p_fe, const uint8_t *p, size_t size, uint16_t partition); #endif /* UDFREAD_ECMA167_H_ */ libudfread-1.1.2/src/libudfread.pc.in000066400000000000000000000003271403256456600174520ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libudfread Description: UDF filesystem access library Version: @VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -ludfread libudfread-1.1.2/src/udfread-version.c000066400000000000000000000021221403256456600176540ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #if HAVE_CONFIG_H #include "config.h" #endif #include "udfread-version.h" /* * Library version */ void udfread_get_version(int *major, int *minor, int *micro) { *major = UDFREAD_VERSION_MAJOR; *minor = UDFREAD_VERSION_MINOR; *micro = UDFREAD_VERSION_MICRO; } libudfread-1.1.2/src/udfread-version.h.in000066400000000000000000000030601403256456600202700ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #ifndef UDFREAD_VERSION_H_ #define UDFREAD_VERSION_H_ #define UDFREAD_VERSION_CODE(major, minor, micro) \ (((major) * 10000) + \ ((minor) * 100) + \ ((micro) * 1)) #define UDFREAD_VERSION_MAJOR @UDFREAD_VERSION_MAJOR@ #define UDFREAD_VERSION_MINOR @UDFREAD_VERSION_MINOR@ #define UDFREAD_VERSION_MICRO @UDFREAD_VERSION_MICRO@ #define UDFREAD_VERSION_STRING "@UDFREAD_VERSION_MAJOR@.@UDFREAD_VERSION_MINOR@.@UDFREAD_VERSION_MICRO@" #define UDFREAD_VERSION \ UDFREAD_VERSION_CODE(UDFREAD_VERSION_MAJOR, UDFREAD_VERSION_MINOR, UDFREAD_VERSION_MICRO) /** * Get library version * */ void udfread_get_version(int *major, int *minor, int *micro); #endif /* UDFREAD_VERSION_H_ */ libudfread-1.1.2/src/udfread.c000066400000000000000000001342621403256456600162040ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014-2015 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #if HAVE_CONFIG_H #include "config.h" #endif #include "udfread.h" #ifdef HAVE_UDFREAD_VERSION_H #include "udfread-version.h" #endif #include "blockinput.h" #include "default_blockinput.h" #include "ecma167.h" #include #include #include #ifdef _WIN32 #define strtok_r strtok_s #endif /* * Logging */ #include static int enable_log = 0; static int enable_trace = 0; #define udf_error(...) do { fprintf(stderr, "udfread ERROR: " __VA_ARGS__); } while (0) #define udf_log(...) do { if (enable_log) fprintf(stderr, "udfread LOG : " __VA_ARGS__); } while (0) #define udf_trace(...) do { if (enable_trace) fprintf(stderr, "udfread TRACE: " __VA_ARGS__); } while (0) /* * atomic operations */ #if defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || (defined (__clang__) && (defined (__x86_64__) || defined (__i386__))) # define atomic_pointer_compare_and_exchange(atomic, oldval, newval) \ __sync_bool_compare_and_swap((atomic), (oldval), (newval)) #elif defined(_WIN32) #include static int atomic_pointer_compare_and_exchange(void *atomic, void *oldval, void *newval) { static int init = 0; static CRITICAL_SECTION cs = {0}; if (!init) { init = 1; InitializeCriticalSection(&cs); } int result; EnterCriticalSection(&cs); result = *(void**)atomic == oldval; if (result) { *(void**)atomic = newval; } LeaveCriticalSection(&cs); return result; } #elif defined(HAVE_PTHREAD_H) #include static int atomic_pointer_compare_and_exchange(void *atomic, void *oldval, void *newval) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; int result; pthread_mutex_lock(&lock); result = *(void**)atomic == oldval; if (result) { *(void**)atomic = newval; } pthread_mutex_unlock(&lock); return result; } #else # error no atomic operation support #endif /* * utils */ static char *_str_dup(const char *s) { size_t len = strlen(s); char *p = (char *)malloc(len + 1); if (p) { memcpy(p, s, len + 1); } else { udf_error("out of memory\n"); } return p; } static void *_safe_realloc(void *p, size_t s) { void *result = realloc(p, s); if (!result) { udf_error("out of memory\n"); free(p); } return result; } /* * Decoding */ /* * outputs Modified UTF-8 (MUTF-8). * The null character (U+0000) uses the two-byte overlong encoding 11000000 10000000 (hexadecimal C0 80), instead of 00000000 (hexadecimal 00). * * - not strictly UTF-8 compilant, but works with C str*() functions and Java, while \0 bytes in middle of strings won't. */ #define utf16lo_to_mutf8(out, out_pos, out_size, ch) \ do { \ if (ch != 0 && ch < 0x80) { \ out[out_pos++] = (uint8_t)ch; \ } else { \ out_size++; \ out = (uint8_t *)_safe_realloc(out, out_size);\ if (!out) return NULL; \ \ out[out_pos++] = 0xc0 | (ch >> 6); \ out[out_pos++] = 0x80 | (ch & 0x3f); \ } \ } while (0) #define utf16_to_mutf8(out, out_pos, out_size, ch) \ do { \ if (ch < 0x7ff) { \ utf16lo_to_mutf8(out, out_pos, out_size, ch); \ } else { \ out_size += 2; \ out = (uint8_t *)_safe_realloc(out, out_size); \ if (!out) return NULL; \ \ out[out_pos++] = 0xe0 | (ch >> 12); \ out[out_pos++] = 0x80 | ((ch >> 6) & 0x3f); \ out[out_pos++] = 0x80 | (ch & 0x3f); \ \ } \ } while (0) /* Strings, CS0 (UDF 2.1.1) */ static char *_cs0_to_mutf8(const uint8_t *cs0, size_t size) { size_t out_pos = 0; size_t out_size = size; size_t i; uint8_t *out; if (size < 1) { /* empty string */ return calloc(1, 1); } out = (uint8_t *)malloc(size); if (!out) { udf_error("out of memory\n"); return NULL; } switch (cs0[0]) { case 8: /*udf_trace("string in utf-8\n");*/ for (i = 1; i < size; i++) { utf16lo_to_mutf8(out, out_pos, out_size, cs0[i]); } break; case 16: for (i = 1; i < size - 1; i+=2) { uint16_t ch = cs0[i + 1] | (cs0[i] << 8); utf16_to_mutf8(out, out_pos, out_size, ch); } break; default: udf_error("unregonized string encoding %u\n", cs0[0]); free(out); return NULL; } out[out_pos] = 0; return (char*)out; } /* Domain Identifiers, UDF 2.1.5.2 */ static const char lvd_domain_id[] = "*OSTA UDF Compliant"; static const char meta_domain_id[] = "*UDF Metadata Partition"; static int _check_domain_identifier(const struct entity_id *eid, const char *value) { return (!memcmp(value, eid->identifier, strlen(value))) ? 0 : -1; } /* Additional File Types (UDF 2.60, 2.3.5.2) */ enum udf_file_type { UDF_FT_METADATA = 250, UDF_FT_METADATA_MIRROR = 251, }; /* * Block access * * read block(s) from absolute lba */ static uint32_t _read_blocks(udfread_block_input *input, uint32_t lba, void *buf, uint32_t nblocks, int flags) { int result; if (!input || (int)nblocks < 1) { return 0; } result = input->read(input, lba, buf, nblocks, flags); return result < 0 ? 0 : (uint32_t)result; } static int _read_descriptor_block(udfread_block_input *input, uint32_t lba, uint8_t *buf) { if (_read_blocks(input, lba, buf, 1, 0) == 1) { return decode_descriptor_tag(buf); } return -1; } /* * Disc probing */ static int _probe_volume(udfread_block_input *input) { /* Volume Recognition (ECMA 167 2/8, UDF 2.60 2.1.7) */ static const char bea[] = {'\0', 'B', 'E', 'A', '0', '1', '\1'}; static const char nsr_02[] = {'\0', 'N', 'S', 'R', '0', '2', '\1'}; static const char nsr_03[] = {'\0', 'N', 'S', 'R', '0', '3', '\1'}; static const char tea[] = {'\0', 'T', 'E', 'A', '0', '1', '\1'}; static const char nul[] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0'}; uint8_t buf[UDF_BLOCK_SIZE]; uint32_t lba; int bea_seen = 0; for (lba = 16; lba < 256; lba++) { if (_read_blocks(input, lba, buf, 1, 0) == 1) { /* Terminating Extended Area Descriptor */ if (!memcmp(buf, tea, sizeof(tea))) { udf_error("ECMA 167 Volume Recognition failed (no NSR descriptor)\n"); return -1; } if (!memcmp(buf, nul, sizeof(nul))) { break; } if (!memcmp(buf, bea, sizeof(bea))) { udf_trace("ECMA 167 Volume, BEA01\n"); bea_seen = 1; } if (bea_seen) { if (!memcmp(buf, nsr_02, sizeof(nsr_02))) { udf_trace("ECMA 167 Volume, NSR02\n"); return 0; } if (!memcmp(buf, nsr_03, sizeof(nsr_03))) { udf_trace("ECMA 167 Volume, NSR03\n"); return 0; } } } } udf_error("ECMA 167 Volume Recognition failed\n"); return -1; } static int _read_avdp(udfread_block_input *input, struct anchor_volume_descriptor *avdp) { uint8_t buf[UDF_BLOCK_SIZE]; int tag_id; uint32_t lba = 256; /* * Find Anchor Volume Descriptor Pointer. * It is in block 256, last block or (last block - 256) * (UDF 2.60, 2.2.3) */ /* try block 256 */ tag_id = _read_descriptor_block(input, lba, buf); if (tag_id != ECMA_AnchorVolumeDescriptorPointer) { /* try last block */ if (!input->size) { udf_error("Can't find Anchor Volume Descriptor Pointer\n"); return -1; } lba = input->size(input) - 1; tag_id = _read_descriptor_block(input, lba, buf); if (tag_id != ECMA_AnchorVolumeDescriptorPointer) { /* try last block - 256 */ lba -= 256; tag_id = _read_descriptor_block(input, lba, buf); if (tag_id != ECMA_AnchorVolumeDescriptorPointer) { udf_error("Can't find Anchor Volume Descriptor Pointer\n"); return -1; } } } udf_log("Found Anchor Volume Descriptor Pointer from lba %u\n", lba); decode_avdp(buf, avdp); return 1; } /* * Volume structure */ /* Logical Partitions from Logical Volume Descriptor */ struct udf_partitions { uint32_t num_partition; struct { uint16_t number; uint32_t lba; uint32_t mirror_lba; } p[2]; }; struct volume_descriptor_set { struct partition_descriptor pd; struct primary_volume_descriptor pvd; struct logical_volume_descriptor lvd; }; static int _search_vds(udfread_block_input *input, int part_number, const struct extent_ad *loc, struct volume_descriptor_set *vds) { struct volume_descriptor_pointer vdp; uint8_t buf[UDF_BLOCK_SIZE]; int tag_id; uint32_t lba; uint32_t end_lba; int have_part = 0, have_lvd = 0, have_pvd = 0; memset(vds, 0, sizeof(*vds)); next_extent: udf_trace("reading Volume Descriptor Sequence at lba %u, len %u bytes\n", loc->lba, loc->length); end_lba = loc->lba + loc->length / UDF_BLOCK_SIZE; /* parse Volume Descriptor Sequence */ for (lba = loc->lba; lba < end_lba; lba++) { tag_id = _read_descriptor_block(input, lba, buf); switch (tag_id) { case ECMA_VolumeDescriptorPointer: decode_vdp(buf, &vdp); loc = &vdp.next_extent; goto next_extent; case ECMA_PrimaryVolumeDescriptor: udf_log("Primary Volume Descriptor in lba %u\n", lba); decode_primary_volume(buf, &vds->pvd); have_pvd = 1; break; case ECMA_LogicalVolumeDescriptor: udf_log("Logical volume descriptor in lba %u\n", lba); decode_logical_volume(buf, &vds->lvd); have_lvd = 1; break; case ECMA_PartitionDescriptor: udf_log("Partition Descriptor in lba %u\n", lba); if (!have_part) { decode_partition(buf, &vds->pd); have_part = (part_number < 0 || part_number == vds->pd.number); udf_log(" partition %u at lba %u, %u blocks\n", vds->pd.number, vds->pd.start_block, vds->pd.num_blocks); } break; case ECMA_TerminatingDescriptor: udf_trace("Terminating Descriptor in lba %u\n", lba); return (have_part && have_lvd) ? 0 : -1; } if (have_part && have_lvd && have_pvd) { /* got everything interesting, skip rest blocks */ return 0; } } return (have_part && have_lvd) ? 0 : -1; } static int _read_vds(udfread_block_input *input, int part_number, struct volume_descriptor_set *vds) { struct anchor_volume_descriptor avdp; /* Find Anchor Volume Descriptor */ if (_read_avdp(input, &avdp) < 0) { return -1; } // XXX we could read part of descriptors from main area and rest from backup if both are partially corrupted ... /* try to read Main Volume Descriptor Sequence */ if (!_search_vds(input, part_number, &avdp.mvds, vds)) { return 0; } /* try to read Backup Volume Descriptor */ if (!_search_vds(input, part_number, &avdp.rvds, vds)) { return 0; } udf_error("failed reading Volume Descriptor Sequence\n"); return -1; } static int _validate_logical_volume(const struct logical_volume_descriptor *lvd, struct long_ad *fsd_loc) { if (lvd->block_size != UDF_BLOCK_SIZE) { udf_error("incompatible block size %u\n", lvd->block_size); return -1; } /* UDF 2.60 2.1.5.2 */ if (_check_domain_identifier(&lvd->domain_id, lvd_domain_id) < 0) { udf_error("unknown Domain ID in Logical Volume Descriptor: %1.22s\n", lvd->domain_id.identifier); return -1; } else { /* UDF 2.60 2.1.5.3 */ uint16_t rev = _get_u16(lvd->domain_id.identifier_suffix); udf_log("Found UDF %x.%02x Logical Volume\n", rev >> 8, rev & 0xff); /* UDF 2.60 2.2.4.4 */ /* location of File Set Descriptors */ decode_long_ad(lvd->contents_use, fsd_loc); udf_log("File Set Descriptor location: partition %u lba %u (len %u)\n", fsd_loc->partition, fsd_loc->lba, fsd_loc->length); } return 0; } static int _map_metadata_partition(udfread_block_input *input, struct udf_partitions *part, uint32_t lba, uint32_t mirror_lba, const struct partition_descriptor *pd) { struct file_entry *fe; uint8_t buf[UDF_BLOCK_SIZE]; int tag_id; unsigned int i; /* resolve metadata partition location (it is virtual partition inside another partition) */ udf_trace("Reading metadata file entry: lba %u, mirror lba %u\n", lba, mirror_lba); for (i = 0; i < 2; i++) { if (i == 0) { tag_id = _read_descriptor_block(input, pd->start_block + lba, buf); } else { tag_id = _read_descriptor_block(input, pd->start_block + mirror_lba, buf); } if (tag_id != ECMA_ExtendedFileEntry) { udf_error("read metadata file %u: unexpected tag %d\n", i, tag_id); continue; } fe = decode_ext_file_entry(buf, UDF_BLOCK_SIZE, pd->number); if (!fe) { udf_error("parsing metadata file entry %u failed\n", i); continue; } if (fe->content_inline) { udf_error("invalid metadata file (content inline)\n"); } else if (!fe->u.ads.num_ad) { udf_error("invalid metadata file (no allocation descriptors)\n"); } else if (fe->file_type == UDF_FT_METADATA) { part->p[1].lba = pd->start_block + fe->u.ads.ad[0].lba; udf_log("metadata file at lba %u\n", part->p[1].lba); } else if (fe->file_type == UDF_FT_METADATA_MIRROR) { part->p[1].mirror_lba = pd->start_block + fe->u.ads.ad[0].lba; udf_log("metadata mirror file at lba %u\n", part->p[1].mirror_lba); } else { udf_error("unknown metadata file type %u\n", fe->file_type); } free_file_entry(&fe); } if (!part->p[1].lba && part->p[1].mirror_lba) { /* failed reading primary location, must use mirror */ part->p[1].lba = part->p[1].mirror_lba; part->p[1].mirror_lba = 0; } return part->p[1].lba ? 0 : -1; } static int _parse_udf_partition_maps(udfread_block_input *input, struct udf_partitions *part, const struct volume_descriptor_set *vds) { /* parse partition maps * There should be one type1 partition. * There may be separate metadata partition. * metadata partition is virtual partition that is mapped to metadata file. */ const uint8_t *map = vds->lvd.partition_map_table; const uint8_t *end = map + vds->lvd.partition_map_lable_length; unsigned int i; int num_type1_partition = 0; udf_log("Partition map count: %u\n", vds->lvd.num_partition_maps); if (vds->lvd.partition_map_lable_length > sizeof(vds->lvd.partition_map_table)) { udf_error("partition map table too big !\n"); end -= vds->lvd.partition_map_lable_length - sizeof(vds->lvd.partition_map_table); } for (i = 0; i < vds->lvd.num_partition_maps && map + 2 < end; i++) { /* Partition map, ECMA 167 3/10.7 */ uint8_t type = _get_u8(map + 0); uint8_t len = _get_u8(map + 1); uint16_t ref; if (len < 2) { udf_error("invalid partition map length %d\n", (int)len); break; } udf_trace("map %u: type %u\n", i, type); if (map + len > end) { udf_error("partition map table too short !\n"); break; } if (type == 1) { /* ECMA 167 Type 1 partition map */ if (len != 6) { udf_error("invalid type 1 partition map length %d\n", (int)len); break; } ref = _get_u16(map + 4); udf_log("partition map: %u: type 1 partition, ref %u\n", i, ref); if (num_type1_partition) { udf_error("more than one type1 partitions not supported\n"); } else if (ref != vds->pd.number) { udf_error("Logical partition %u refers to another physical partition %u (expected %u)\n", i, ref, vds->pd.number); } else { part->num_partition = 1; part->p[0].number = i; part->p[0].lba = vds->pd.start_block; part->p[0].mirror_lba = 0; /* no mirror for data partition */ num_type1_partition++; } } else if (type == 2) { /* Type 2 partition map, UDF 2.60 2.2.18 */ if (len != 64) { udf_error("invalid type 2 partition map length %d\n", (int)len); break; } struct entity_id type_id; decode_entity_id(map + 4, &type_id); if (!_check_domain_identifier(&type_id, meta_domain_id)) { /* Metadata Partition, UDF 2.60 2.2.10 */ uint32_t lba, mirror_lba; ref = _get_u16(map + 38); lba = _get_u32(map + 40); mirror_lba = _get_u32(map + 44); if (ref != vds->pd.number) { udf_error("metadata file partition %u != %u\n", ref, vds->pd.number); } if (!_map_metadata_partition(input, part, lba, mirror_lba, &vds->pd)) { part->num_partition = 2; part->p[1].number = i; udf_log("partition map: %u: metadata partition, ref %u. lba %u, mirror %u\n", i, ref, part->p[1].lba, part->p[1].mirror_lba); } } else { udf_log("%u: unsupported type 2 partition\n", i); } } map += len; } return num_type1_partition ? 0 : -1; } /* * Cached directory data */ struct udf_file_identifier { char *filename; /* MUTF-8 */ struct long_ad icb; /* location of file entry */ uint8_t characteristic; /* CHAR_FLAG_* */ }; struct udf_dir { uint32_t num_entries; struct udf_file_identifier *files; struct udf_dir **subdirs; }; static void _free_dir(struct udf_dir **pp) { if (pp && *pp) { struct udf_dir *p = *pp; uint32_t i; if (p->subdirs) { for (i = 0; i < p->num_entries; i++) { _free_dir(&(p->subdirs[i])); } free(p->subdirs); } if (p->files) { for (i = 0; i < p->num_entries; i++) { free(p->files[i].filename); } free(p->files); } free(p); *pp = NULL; } } /* * */ struct udfread { udfread_block_input *input; /* Volume partitions */ struct udf_partitions part; /* cached directory tree */ struct udf_dir *root_dir; char *volume_identifier; char volume_set_identifier[128]; }; udfread *udfread_init(void) { /* set up logging */ if (getenv("UDFREAD_LOG")) { enable_log = 1; } if (getenv("UDFREAD_TRACE")) { enable_trace = 1; enable_log = 1; } #ifdef HAVE_UDFREAD_VERSION_H udf_log("libudfread " UDFREAD_VERSION_STRING "\n"); #endif return (udfread *)calloc(1, sizeof(udfread)); } /* * Metadata */ static int _partition_index(udfread *udf, uint16_t partition_number) { if (partition_number == udf->part.p[0].number) { return 0; } else if (udf->part.num_partition > 1 && partition_number == udf->part.p[1].number) { return 1; } udf_error("unknown partition %u\n", partition_number); return -1; } /* read metadata blocks. If read fails, try from mirror (if available). */ static int _read_metadata_blocks(udfread *udf, uint8_t *buf, const struct long_ad *loc) { int tag_id; uint32_t lba, i, got; int part_idx; udf_trace("reading metadata from part %u lba %u\n", loc->partition, loc->lba); part_idx = _partition_index(udf, loc->partition); if (part_idx < 0) { return -1; } /* read first block. Parse and check tag. */ lba = udf->part.p[part_idx].lba + loc->lba; tag_id = _read_descriptor_block(udf->input, lba, buf); if (tag_id < 0) { /* try mirror */ if (udf->part.p[part_idx].mirror_lba) { udf_log("read metadata from lba %u failed, trying mirror\n", lba); lba = udf->part.p[part_idx].mirror_lba + loc->lba; tag_id = _read_descriptor_block(udf->input, lba, buf); } if (tag_id < 0) { udf_error("read metadata from lba %u failed\n", lba); return -1; } } /* read following blocks without tag parsing and checksum validation */ for (i = 1; i <= (loc->length - 1) / UDF_BLOCK_SIZE; i++) { lba = udf->part.p[part_idx].lba + loc->lba + i; buf += UDF_BLOCK_SIZE; got = _read_blocks(udf->input, lba, buf, 1, 0); if (got != 1) { if (udf->part.p[part_idx].mirror_lba) { udf_log("read metadata from lba %u failed, trying mirror\n", lba); lba = udf->part.p[part_idx].mirror_lba + loc->lba + i; got = _read_blocks(udf->input, lba, buf, 1, 0); } if (got != 1) { udf_error("read metadata from lba %u failed\n", lba); return -1; } } } return tag_id; } static uint8_t *_read_metadata(udfread *udf, const struct long_ad *icb, int *tag_id) { uint32_t num_blocks = (icb->length + UDF_BLOCK_SIZE - 1) / UDF_BLOCK_SIZE; uint8_t *buf; if (num_blocks < 1) { return NULL; } buf = (uint8_t *)malloc(num_blocks * UDF_BLOCK_SIZE); if (!buf) { udf_error("out of memory\n"); return NULL; } *tag_id = _read_metadata_blocks(udf, buf, icb); if (*tag_id < 0) { udf_log("reading icb blocks failed\n"); free(buf); return NULL; } return buf; } static struct file_entry *_read_file_entry(udfread *udf, const struct long_ad *icb) { struct file_entry *fe = NULL; uint8_t *buf; int tag_id; udf_trace("file entry size %u bytes\n", icb->length); buf = _read_metadata(udf, icb, &tag_id); if (!buf) { udf_error("reading file entry failed\n"); return NULL; } switch (tag_id) { case ECMA_FileEntry: fe = decode_file_entry(buf, UDF_BLOCK_SIZE, icb->partition); break; case ECMA_ExtendedFileEntry: fe = decode_ext_file_entry(buf, UDF_BLOCK_SIZE, icb->partition); break; default: udf_error("_read_file_entry: unknown tag %d\n", tag_id); break; } free(buf); /* read possible additional allocation extents */ if (fe && !fe->content_inline) { while (fe->u.ads.num_ad > 0 && fe->u.ads.ad[fe->u.ads.num_ad - 1].extent_type == ECMA_AD_EXTENT_AD) { /* drop pointer to this extent from the end of AD list */ fe->u.ads.num_ad--; icb = &fe->u.ads.ad[fe->u.ads.num_ad]; udf_log("_read_file_entry: reading allocation extent @%u\n", icb->lba); buf = _read_metadata(udf, icb, &tag_id); if (!buf) { udf_error("_read_file_entry: reading allocation extent @%u failed\n", icb->lba); break; } if (tag_id != ECMA_AllocationExtentDescriptor) { free(buf); udf_error("_read_file_entry: unexpected tag %d (expected ECMA_AllocationExtentDescriptor)\n", tag_id); break; } if (decode_allocation_extent(&fe, buf, icb->length, icb->partition) < 0) { free(buf); udf_error("_read_file_entry: decode_allocation_extent() failed\n"); break; } /* failure before this point will cause an error when reading the file past extent point. (extent ad is left in file ad list). */ free(buf); } } return fe; } static int _parse_dir(const uint8_t *data, uint32_t length, struct udf_dir *dir) { struct file_identifier fid; const uint8_t *p = data; const uint8_t *end = data + length; int tag_id; if (length < 16) { return 0; } while (p < end - 16) { size_t used; if (dir->num_entries == UINT32_MAX) { return 0; } tag_id = decode_descriptor_tag(p); if (tag_id != ECMA_FileIdentifierDescriptor) { udf_error("unexpected tag %d in directory file\n", tag_id); return -1; } dir->files = (struct udf_file_identifier *)_safe_realloc(dir->files, sizeof(dir->files[0]) * (dir->num_entries + 1)); if (!dir->files) { return -1; } used = decode_file_identifier(p, (size_t)(end - p), &fid); if (used == 0) { /* not enough data. keep the entries we already have. */ break; } p += used; if (fid.characteristic & CHAR_FLAG_PARENT) { continue; } if (fid.filename_len < 1) { continue; } dir->files[dir->num_entries].characteristic = fid.characteristic; dir->files[dir->num_entries].icb = fid.icb; dir->files[dir->num_entries].filename = _cs0_to_mutf8(fid.filename, fid.filename_len); if (!dir->files[dir->num_entries].filename) { continue; } /* Skip empty file identifiers. * Not strictly compilant (?), \0 is allowed in * ECMA167 file identifier. */ if (!dir->files[dir->num_entries].filename[0]) { udf_error("skipping empty file identifier\n"); free(dir->files[dir->num_entries].filename); continue; } dir->num_entries++; } return 0; } static struct udf_dir *_read_dir_file(udfread *udf, const struct long_ad *loc) { struct udf_dir *dir = NULL; uint8_t *data; int tag_id; udf_trace("directory size %u bytes\n", loc->length); data = _read_metadata(udf, loc, &tag_id); if (!data) { udf_error("reading directory file failed\n"); return NULL; } dir = (struct udf_dir *)calloc(1, sizeof(struct udf_dir)); if (dir) { if (_parse_dir(data, loc->length, dir) < 0) { _free_dir(&dir); } } free(data); return dir; } static struct udf_dir *_read_dir(udfread *udf, const struct long_ad *icb) { struct file_entry *fe; struct udf_dir *dir = NULL; fe = _read_file_entry(udf, icb); if (!fe) { udf_error("error reading directory file entry\n"); return NULL; } if (fe->file_type != ECMA_FT_DIR) { udf_error("directory file type is not directory\n"); free_file_entry(&fe); return NULL; } if (fe->content_inline) { dir = (struct udf_dir *)calloc(1, sizeof(struct udf_dir)); if (dir) { if (_parse_dir(&fe->u.data.content[0], fe->u.data.information_length, dir) < 0) { udf_error("failed parsing inline directory file\n"); _free_dir(&dir); } } } else if (fe->u.ads.num_ad == 0) { udf_error("empty directory file"); } else { if (fe->u.ads.num_ad > 1) { udf_error("unsupported fragmented directory file\n"); } dir = _read_dir_file(udf, &fe->u.ads.ad[0]); } free_file_entry(&fe); return dir; } static int _read_root_dir(udfread *udf, const struct long_ad *fsd_loc) { struct file_set_descriptor fsd; uint8_t buf[UDF_BLOCK_SIZE]; int tag_id = -1; struct long_ad loc = *fsd_loc; udf_trace("reading root directory fsd from part %u lba %u\n", fsd_loc->partition, fsd_loc->lba); /* search for File Set Descriptor from the area described by fsd_loc */ loc.length = UDF_BLOCK_SIZE; for (; loc.lba <= fsd_loc->lba + (fsd_loc->length - 1) / UDF_BLOCK_SIZE; loc.lba++) { tag_id = _read_metadata_blocks(udf, buf, &loc); if (tag_id == ECMA_FileSetDescriptor) { break; } if (tag_id == ECMA_TerminatingDescriptor) { break; } udf_error("unhandled tag %d in File Set Descriptor area\n", tag_id); } if (tag_id != ECMA_FileSetDescriptor) { udf_error("didn't find File Set Descriptor\n"); return -1; } decode_file_set_descriptor(buf, &fsd); udf_log("root directory in part %u lba %u\n", fsd.root_icb.partition, fsd.root_icb.lba); /* read root directory from location given in File Set Descriptor */ udf->root_dir = _read_dir(udf, &fsd.root_icb); if (!udf->root_dir) { udf_error("error reading root directory\n"); return -1; } return 0; } static struct udf_dir *_read_subdir(udfread *udf, struct udf_dir *dir, uint32_t index) { if (!(dir->files[index].characteristic & CHAR_FLAG_DIR)) { return NULL; } if (!dir->subdirs) { struct udf_dir **subdirs = (struct udf_dir **)calloc(sizeof(struct udf_dir *), dir->num_entries); if (!subdirs) { udf_error("out of memory\n"); return NULL; } if (!atomic_pointer_compare_and_exchange(&dir->subdirs, NULL, subdirs)) { free(subdirs); } } if (!dir->subdirs[index]) { struct udf_dir *subdir = _read_dir(udf, &dir->files[index].icb); if (!subdir) { return NULL; } if (!atomic_pointer_compare_and_exchange(&dir->subdirs[index], NULL, subdir)) { _free_dir(&subdir); } } return dir->subdirs[index]; } static int _scan_dir(const struct udf_dir *dir, const char *filename, uint32_t *index) { uint32_t i; for (i = 0; i < dir->num_entries; i++) { if (!strcmp(filename, dir->files[i].filename)) { *index = i; return 0; } } udf_log("file %s not found\n", filename); return -1; } static int _find_file(udfread *udf, const char *path, struct udf_dir **p_dir, const struct udf_file_identifier **p_fid) { const struct udf_file_identifier *fid = NULL; struct udf_dir *current_dir; char *tmp_path, *save_ptr, *token; current_dir = udf->root_dir; if (!current_dir) { return -1; } tmp_path = _str_dup(path); if (!tmp_path) { return -1; } token = strtok_r(tmp_path, "/\\", &save_ptr); if (token == NULL) { udf_trace("_find_file: requested root dir\n"); } while (token) { uint32_t index; if (_scan_dir(current_dir, token, &index) < 0) { udf_log("_find_file: entry %s not found\n", token); goto error; } fid = ¤t_dir->files[index]; token = strtok_r(NULL, "/\\", &save_ptr); if (fid->characteristic & CHAR_FLAG_DIR) { current_dir = _read_subdir(udf, current_dir, index); if (!current_dir) { goto error; } } else if (token) { udf_log("_find_file: entry %s not found (parent is file, not directory)\n", token); goto error; } else { // found a file, make sure we won't return directory data current_dir = NULL; } } if (p_fid) { if (!fid) { udf_log("no file identifier found for %s\n", path); goto error; } *p_fid = fid; } if (p_dir) { *p_dir = current_dir; } free(tmp_path); return 0; error: free(tmp_path); return -1; } /* * Volume access API */ int udfread_open_input(udfread *udf, udfread_block_input *input/*, int partition*/) { struct volume_descriptor_set vds; struct long_ad fsd_location; if (!udf || !input || !input->read) { return -1; } if (_probe_volume(input) < 0) { return -1; } /* read Volume Descriptor Sequence */ if (_read_vds(input, 0, &vds) < 0) { return -1; } /* validate logical volume structure */ if (_validate_logical_volume(&vds.lvd, &fsd_location) < 0) { return -1; } /* Volume Identifier. CS0, UDF 2.1.1 */ udf->volume_identifier = _cs0_to_mutf8(vds.pvd.volume_identifier, vds.pvd.volume_identifier_length); if (udf->volume_identifier) { udf_log("Volume Identifier: %s\n", udf->volume_identifier); } memcpy(udf->volume_set_identifier, vds.pvd.volume_set_identifier, 128); /* map partitions */ if (_parse_udf_partition_maps(input, &udf->part, &vds) < 0) { return -1; } /* Read root directory */ udf->input = input; if (_read_root_dir(udf, &fsd_location) < 0) { udf->input = NULL; return -1; } return 0; } int udfread_open(udfread *udf, const char *path) { udfread_block_input *input; int result; if (!path) { return -1; } input = block_input_new(path); if (!input) { return -1; } result = udfread_open_input(udf, input); if (result < 0) { if (input->close) { input->close(input); } } return result; } void udfread_close(udfread *udf) { if (udf) { if (udf->input) { if (udf->input->close) { udf->input->close(udf->input); } udf->input = NULL; } _free_dir(&udf->root_dir); free(udf->volume_identifier); free(udf); } } const char *udfread_get_volume_id(udfread *udf) { if (udf) { return udf->volume_identifier; } return NULL; } size_t udfread_get_volume_set_id (udfread *udf, void *buffer, size_t size) { if (udf) { if (size > sizeof(udf->volume_set_identifier)) { size = sizeof(udf->volume_set_identifier); } memcpy(buffer, udf->volume_set_identifier, size); return sizeof(udf->volume_set_identifier); } return 0; } /* * Directory access API */ struct udfread_dir { udfread *udf; struct udf_dir *dir; uint32_t current_file; }; static UDFDIR *_new_udfdir(udfread *udf, struct udf_dir *dir) { UDFDIR *result; if (!dir) { return NULL; } result = (UDFDIR *)calloc(1, sizeof(UDFDIR)); if (result) { result->dir = dir; result->udf = udf; } return result; } UDFDIR *udfread_opendir(udfread *udf, const char *path) { struct udf_dir *dir = NULL; if (!udf || !udf->input || !path) { return NULL; } if (_find_file(udf, path, &dir, NULL) < 0) { return NULL; } return _new_udfdir(udf, dir); } UDFDIR *udfread_opendir_at(UDFDIR *p, const char *name) { struct udf_dir *dir = NULL; uint32_t index; if (!p || !name) { return NULL; } if (_scan_dir(p->dir, name, &index) < 0) { udf_log("udfread_opendir_at: entry %s not found\n", name); return NULL; } dir = _read_subdir(p->udf, p->dir, index); return _new_udfdir(p->udf, dir); } struct udfread_dirent *udfread_readdir(UDFDIR *p, struct udfread_dirent *entry) { const struct udf_file_identifier *fi; if (!p || !entry || !p->dir) { return NULL; } if (p->current_file >= p->dir->num_entries) { return NULL; } fi = &p->dir->files[p->current_file]; entry->d_name = fi->filename; if (fi->characteristic & CHAR_FLAG_PARENT) { entry->d_type = UDF_DT_DIR; entry->d_name = ".."; } else if (fi->characteristic & CHAR_FLAG_DIR) { entry->d_type = UDF_DT_DIR; } else { entry->d_type = UDF_DT_REG; } p->current_file++; return entry; } void udfread_rewinddir(UDFDIR *p) { if (p) { p->current_file = 0; } } void udfread_closedir(UDFDIR *p) { free(p); } /* * File access API */ struct udfread_file { udfread *udf; struct file_entry *fe; /* byte stream access */ uint64_t pos; uint8_t *block; int block_valid; void *block_mem; }; static UDFFILE *_file_open(udfread *udf, const char *path, const struct udf_file_identifier *fi) { struct file_entry *fe; UDFFILE *result; if (fi->characteristic & CHAR_FLAG_DIR) { udf_log("error opening file %s (is directory)\n", path); return NULL; } fe = _read_file_entry(udf, &fi->icb); if (!fe) { udf_error("error reading file entry for %s\n", path); return NULL; } result = (UDFFILE *)calloc(1, sizeof(UDFFILE)); if (!result) { free_file_entry(&fe); return NULL; } result->udf = udf; result->fe = fe; return result; } UDFFILE *udfread_file_open(udfread *udf, const char *path) { const struct udf_file_identifier *fi = NULL; if (!udf || !udf->input || !path) { return NULL; } if (_find_file(udf, path, NULL, &fi) < 0) { return NULL; } return _file_open(udf, path, fi); } UDFFILE *udfread_file_openat(UDFDIR *dir, const char *name) { uint32_t index; if (!dir || !name) { return NULL; } if (_scan_dir(dir->dir, name, &index) < 0) { udf_log("udfread_file_openat: entry %s not found\n", name); return NULL; } return _file_open(dir->udf, name, &dir->dir->files[index]); } int64_t udfread_file_size(UDFFILE *p) { if (p) { return (int64_t)p->fe->length; } return -1; } void udfread_file_close(UDFFILE *p) { if (p) { free_file_entry(&p->fe); free(p->block_mem); free(p); } } /* * block access */ static uint32_t _file_lba(UDFFILE *p, uint32_t file_block, uint32_t *extent_length) { const struct file_entry *fe; unsigned int i; uint32_t ad_size; fe = p->fe; for (i = 0; i < fe->u.ads.num_ad; i++) { const struct long_ad *ad = &fe->u.ads.ad[0]; ad_size = (ad[i].length + UDF_BLOCK_SIZE - 1) / UDF_BLOCK_SIZE; if (file_block < ad_size) { if (ad[i].extent_type != ECMA_AD_EXTENT_NORMAL) { if (ad[i].extent_type == ECMA_AD_EXTENT_AD) { udf_error("unsupported allocation descriptor: extent type %u\n", ad[i].extent_type); } return 0; } if (!ad[i].lba) { /* empty file / no allocated space */ return 0; } if (ad[i].partition != p->udf->part.p[0].number) { udf_error("file partition %u != %u\n", ad[i].partition, p->udf->part.p[0].number); } if (extent_length) { *extent_length = ad_size - file_block; } return p->udf->part.p[0].lba + ad[i].lba + file_block; } file_block -= ad_size; } return 0; } static int _file_lba_exists(UDFFILE *p) { if (!p) { return 0; } if (p->fe->content_inline) { udf_error("can't map lba for inline file\n"); return 0; } return 1; } uint32_t udfread_file_lba(UDFFILE *p, uint32_t file_block) { if (!_file_lba_exists(p)) { return 0; } return _file_lba(p, file_block, NULL); } uint32_t udfread_read_blocks(UDFFILE *p, void *buf, uint32_t file_block, uint32_t num_blocks, int flags) { uint32_t i; if (!num_blocks || !buf) { return 0; } if (!_file_lba_exists(p)) { return 0; } for (i = 0; i < num_blocks; ) { uint32_t extent_length = 0; uint32_t lba; uint8_t *block = (uint8_t *)buf + UDF_BLOCK_SIZE * i; lba = _file_lba(p, file_block + i, &extent_length); udf_trace("map block %u to lba %u\n", file_block + i, lba); if (!lba) { /* unallocated/unwritten block or EOF */ uint32_t file_blocks = (udfread_file_size(p) + UDF_BLOCK_SIZE - 1) / UDF_BLOCK_SIZE; if (file_block + i < file_blocks) { udf_trace("zero-fill unallocated / unwritten block %u\n", file_block + i); memset(block, 0, UDF_BLOCK_SIZE); i++; continue; } udf_error("block %u outside of file (size %u blocks)\n", file_block + i, file_blocks); break; } if (extent_length > num_blocks - i) { extent_length = num_blocks - i; } extent_length = _read_blocks(p->udf->input, lba, block, extent_length, flags); if (extent_length < 1) { break; } i += extent_length; } return i; } /* * byte stream */ static ssize_t _read(UDFFILE *p, void *buf, size_t bytes) { /* start from middle of block ? * maximal file size, i.e. position, is 2^32 * block size */ size_t pos_off = p->pos % UDF_BLOCK_SIZE; uint32_t file_block = (uint32_t)(p->pos / UDF_BLOCK_SIZE); if (pos_off) { size_t chunk_size = UDF_BLOCK_SIZE - pos_off; if (!p->block_valid) { if (udfread_read_blocks(p, p->block, file_block, 1, 0) != 1) { return -1; } p->block_valid = 1; } if (chunk_size > bytes) { chunk_size = bytes; } memcpy(buf, p->block + pos_off, chunk_size); p->pos += (uint64_t)chunk_size; return (ssize_t)chunk_size; } /* read full block(s) ? */ if (bytes >= UDF_BLOCK_SIZE) { uint32_t num_blocks = bytes / UDF_BLOCK_SIZE; num_blocks = udfread_read_blocks(p, buf, file_block, num_blocks, 0); if (num_blocks < 1) { return -1; } p->pos += num_blocks * UDF_BLOCK_SIZE; return num_blocks * UDF_BLOCK_SIZE; } /* read beginning of a block */ if (udfread_read_blocks(p, p->block, file_block, 1, 0) != 1) { return -1; } p->block_valid = 1; memcpy(buf, p->block, bytes); p->pos += bytes; return (ssize_t)bytes; } static ssize_t _read_inline(UDFFILE *p, void *buf, size_t bytes) { uint64_t information_length = p->fe->u.data.information_length; size_t pad_size = 0; if (p->pos + bytes > information_length) { udf_log("read hits padding in inline file\n"); if (p->pos > information_length) { pad_size = bytes; } else { pad_size = (size_t)(p->pos + bytes - information_length); } memset((char*)buf + bytes - pad_size, 0, pad_size); } if (pad_size < bytes) { memcpy(buf, &p->fe->u.data.content[p->pos], bytes - pad_size); } p->pos = p->pos + bytes; return (ssize_t)bytes; } #define ALIGN(p, align) \ (uint8_t *)( ((uintptr_t)(p) + ((align)-1)) & ~((uintptr_t)((align)-1))) ssize_t udfread_file_read(UDFFILE *p, void *buf, size_t bytes) { uint8_t *bufpt = (uint8_t *)buf; /* sanity checks */ if (!p || !buf) { return -1; } if ((ssize_t)bytes < 0 || (int64_t)bytes < 0) { return -1; } if (p->pos >= p->fe->length) { return 0; } /* limit range to file size */ if (p->pos + bytes > p->fe->length) { bytes = (size_t)(p->fe->length - p->pos); } /* small files may be stored inline in file entry */ if (p->fe->content_inline) { return _read_inline(p, buf, bytes); } /* allocate temp storage for input block */ if (!p->block) { p->block_mem = malloc(2 * UDF_BLOCK_SIZE); if (!p->block_mem) { return -1; } p->block = ALIGN(p->block_mem, UDF_BLOCK_SIZE); } /* read chunks */ while (bytes > 0) { ssize_t r = _read(p, bufpt, bytes); if (r < 0) { if (bufpt != buf) { /* got some bytes */ break; } /* got nothing */ return -1; } bufpt += r; bytes -= (size_t)r; } return (intptr_t)bufpt - (intptr_t)buf; } int64_t udfread_file_tell(UDFFILE *p) { if (p) { return (int64_t)p->pos; } return -1; } int64_t udfread_file_seek(UDFFILE *p, int64_t pos, int whence) { if (!p) { return -1; } switch (whence) { case UDF_SEEK_CUR: pos = udfread_file_tell(p) + pos; break; case UDF_SEEK_END: pos = udfread_file_size(p) + pos; break; case UDF_SEEK_SET: break; default: return -1; } if (pos >= 0 && pos <= udfread_file_size(p)) { p->pos = (uint64_t)pos; p->block_valid = 0; return udfread_file_tell(p); } return -1; } libudfread-1.1.2/src/udfread.h000066400000000000000000000162371403256456600162120ustar00rootroot00000000000000/* * This file is part of libudfread * Copyright (C) 2014-2015 VLC authors and VideoLAN * * Authors: Petri Hintukainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #ifndef UDFREAD_H_ #define UDFREAD_H_ #ifdef __cplusplus extern "C" { #endif #include /* *int_t */ #include /* *size_t */ /** * @file udfread/udfread.h * external API header */ /* * NOTE: * * UDF filesystem file identifiers may contain nul bytes (0x00). * * In libudfread API file and directory names are encoded as Modified UTF-8 (MUTF-8). * The null character (U+0000) uses two-byte overlong encoding 11000000 10000000 * (hexadecimal C0 80) instead of 00000000 (hexadecimal 00). */ /* * UDF volume access */ /* opaque handle for UDF volume */ typedef struct udfread udfread; struct udfread_block_input; /** * Initialize UDF reader * * @return allocated udfread object, NULL if error */ udfread *udfread_init (void); /** * Open UDF image * * @param p udfread object * @param input UDF image access functions * @return 0 on success, < 0 on error */ int udfread_open_input (udfread *, struct udfread_block_input *input); /** * Open UDF image * * @param p udfread object * @param path path to device or image file * @return 0 on success, < 0 on error */ int udfread_open (udfread *, const char *path); /** * Close UDF image * * @param p udfread object */ void udfread_close (udfread *); /** * Get UDF Volume Identifier * * @param p udfread object * @return Volume ID as null-terminated MUTF-8 string, NULL if error. Returned pointer is valid until udfread_close(). */ const char *udfread_get_volume_id (udfread *); /** * Get UDF Volume Set Identifier * * @param p udfread object * @param buffer buffer to receive volume set id * @param size buffer size * @return Volume set id size, 0 if error */ size_t udfread_get_volume_set_id (udfread *, void *buffer, size_t size); /* * Directory access */ /* File types for d_type */ enum { UDF_DT_UNKNOWN = 0, UDF_DT_DIR, UDF_DT_REG, }; /* Directory stream entry */ struct udfread_dirent { unsigned int d_type; /* UDF_DT_* */ const char *d_name; /* MUTF-8 */ }; /* opaque handle for directory stream */ typedef struct udfread_dir UDFDIR; /** * Open directory stream * * @param p udfread object * @param path path to the directory (MUTF-8) * @return directory stream handle on the directory, or NULL if it could not be opened. */ UDFDIR *udfread_opendir (udfread *, const char *path); /** * Open directory stream * * Directory name may contain special chars (/, \, ...). * * @param dir parent directory stream handle * @param name name of the directory to open from dir (MUTF-8) * @return directory stream handle on the directory, or NULL if it could not be opened. */ UDFDIR *udfread_opendir_at(UDFDIR *dir, const char *name); /** * Read directory stream * * Read a directory entry from directory stream. Return a pointer to * udfread_dirent struct describing the entry, or NULL for EOF or error. * * @param p directory stream * @param entry storege space for directory entry * @return next directory stream entry, or NULL if EOF or error. */ struct udfread_dirent *udfread_readdir (UDFDIR *, struct udfread_dirent *entry); /** * Rewind directory stream * * Rewind directory stream to the beginning of the directory. * * @param p directory stream */ void udfread_rewinddir (UDFDIR *); /** * Close directory stream * * @param p directory stream */ void udfread_closedir (UDFDIR *); /* * File access */ /** * The length of one Logical Block */ #ifndef UDF_BLOCK_SIZE # define UDF_BLOCK_SIZE 2048 #endif /* opaque handle for open file */ typedef struct udfread_file UDFFILE; /** * Open a file * * Allowed separator chars are \ and /. * Path to the file is always absolute (relative to disc image root). * Path may begin with single separator char. * Path may not contain "." or ".." directory components. * * @param p udfread object * @param path path to the file (MUTF-8) * @return file object, or NULL if it could not be opened. */ UDFFILE *udfread_file_open (udfread *, const char *path); /** * Open a file from directory * * File name may contain special chars (/, \, ...). * * @param dir parent directory stream handle * @param name name of the file (MUTF-8) * @return file object, or NULL if it could not be opened. */ UDFFILE *udfread_file_openat (UDFDIR *dir, const char *name); /** * Close file object * * @param p file object */ void udfread_file_close (UDFFILE *); /** * Get file size * * @param p file object * @return file size, -1 on error */ int64_t udfread_file_size (UDFFILE *); /* * Block access */ /** * Get file block address * * Convert file block number to absolute block address. * * @param p file object * @param file_block file block number * @return absolute block address, 0 on error */ uint32_t udfread_file_lba (UDFFILE *, uint32_t file_block); /** * Read blocks from a file * * @param p file object * @param buf buffer for data * @param file_block file block number * @param num_blocks number of blocks to read * @return number of blocks read, 0 on error */ uint32_t udfread_read_blocks (UDFFILE *, void *buf, uint32_t file_block, uint32_t num_blocks, int flags); /* * Byte streams */ enum { UDF_SEEK_SET = 0, UDF_SEEK_CUR = 1, UDF_SEEK_END = 2, }; /** * Read bytes from a file * * Reads the given number of bytes from the file and increment the * current read position by number of bytes read. * * @param p file object * @param buf buffer for data * @param bytes number of bytes to read * @return number of bytes read, 0 on EOF, -1 on error */ ssize_t udfread_file_read (UDFFILE *, void *buf, size_t bytes); /** * Get current read position of a file * * @param p file object * @return current read position of the file, -1 on error */ int64_t udfread_file_tell (UDFFILE *); /** * Set read position of a file * * New read position is calculated from offset according to the directive whence as follows: * UDF_SEEK_SET The offset is set to offset bytes. * UDF_SEEK_CUR The offset is set to its current location plus offset bytes. * UDF_SEEK_END The offset is set to the size of the file plus offset bytes. * * @param p file object * @param pos byte offset * @param whence directive * @return current read position of the file, -1 on error */ int64_t udfread_file_seek (UDFFILE *, int64_t pos, int whence); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* UDFREAD_H_ */