pax_global_header00006660000000000000000000000064142116531300014506gustar00rootroot0000000000000052 comment=7a041e6f5077dcb7f4789e7ce2d792820cb58b98 drpm-0.5.1/000077500000000000000000000000001421165313000124535ustar00rootroot00000000000000drpm-0.5.1/.gitignore000066400000000000000000000001351421165313000144420ustar00rootroot00000000000000# directories build pkg tmp html latex # file types *~ *.gch *.o *.so *.so.* *.out *.x86_64 drpm-0.5.1/CMakeLists.txt000066400000000000000000000051461421165313000152210ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.5) project(drpm C) set(DRPM_MAJOR_VERSION 0) set(DRPM_MINOR_VERSION 5) set(DRPM_PATCH_VERSION 1) set(DRPM_VERSION ${DRPM_MAJOR_VERSION}.${DRPM_MINOR_VERSION}.${DRPM_PATCH_VERSION}) include(GNUInstallDirs) option(ENABLE_TESTS "Build and run tests?" ON) option(WITH_ZSTD "Build with zstd support" ON) find_package(PkgConfig REQUIRED) find_package(ZLIB REQUIRED) find_package(BZip2 REQUIRED) find_package(LibLZMA REQUIRED) pkg_check_modules(RPM rpm REQUIRED) pkg_check_modules(LIBCRYPTO libcrypto REQUIRED) if(WITH_ZSTD) pkg_check_modules(ZSTD REQUIRED libzstd) endif() if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) include (CheckCCompilerFlag) CHECK_C_COMPILER_FLAG(-fvisibility=hidden DRPM_HAVE_VISIBILITY) if (DRPM_HAVE_VISIBILITY) set(VISIBILITY_FLAG -fvisibility=hidden) endif() set(CMAKE_C_FLAGS "${VISIBILITY_FLAG} ${CMAKE_C_FLAGS}" ) endif () add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) set(CPACK_PACKAGE_NAME ${CMAKE_PROJECT_NAME}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A small library for fetching information from deltarpm packages") set(CPACK_PACKAGE_CONTACT "Matej Chalk ") set(CPACK_PACKAGE_VERSION_MAJOR ${DRPM_MAJOR_VERSION}) set(CPACK_PACKAGE_VERSION_MINOR ${DRPM_MINOR_VERSION}) set(CPACK_PACKAGE_VERSION_PATCH ${DRPM_PATCH_VERSION}) set(CPACK_SOURCE_IGNORE_FILES "/tmp/" "/build/" "/pkg/" "/html/" "/latex/" "drpm.spec" "\\\\.git" ".*~" ".*\\\\.o") set(CPACK_SOURCE_GENERATOR "TBZ2") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") include(CPack) set(DRPM_SOURCES drpm.c drpm_apply.c drpm_block.c drpm_compstrm.c drpm_decompstrm.c drpm_deltarpm.c drpm_diff.c drpm_make.c drpm_options.c drpm_read.c drpm_rpm.c drpm_search.c drpm_utils.c drpm_write.c) set(DRPM_LINK_LIBRARIES ${ZLIB_LIBRARIES} ${BZIP2_LIBRARIES} ${LIBLZMA_LIBRARIES} ${RPM_LIBRARIES} ${LIBCRYPTO_LIBRARIES}) if(HAVE_LZLIB_DEVEL) list(APPEND DRPM_LINK_LIBRARIES lz) endif() if(WITH_ZSTD) list(APPEND DRPM_LINK_LIBRARIES ${ZSTD_LIBRARIES}) endif() add_subdirectory(src) add_subdirectory(doc) if(ENABLE_TESTS) pkg_check_modules(CMOCKA cmocka REQUIRED) enable_testing() add_subdirectory(test) endif() configure_file(drpm.pc.in ${CMAKE_CURRENT_BINARY_DIR}/drpm.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/drpm.pc DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig) drpm-0.5.1/COPYING000066400000000000000000000636421421165313000135210ustar00rootroot00000000000000 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! drpm-0.5.1/LICENSE.BSD000066400000000000000000000024771421165313000141010ustar00rootroot00000000000000Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. drpm-0.5.1/doc/000077500000000000000000000000001421165313000132205ustar00rootroot00000000000000drpm-0.5.1/doc/CMakeLists.txt000066400000000000000000000005551421165313000157650ustar00rootroot00000000000000find_package(Doxygen) if(DOXYGEN_FOUND) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM ) endif() drpm-0.5.1/doc/Doxyfile.in000066400000000000000000000246321421165313000153420ustar00rootroot00000000000000# Doxyfile 1.8.6 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "drpm" PROJECT_NUMBER = PROJECT_BRIEF = "A library for making, reading and applying deltarpm packages" PROJECT_LOGO = OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@ CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_PACKAGE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = NO SORT_BRIEF_DOCS = YES SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../src/drpm.h INPUT_ENCODING = UTF-8 FILE_PATTERNS = RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = NO REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = NO HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO GENERATE_QHP = NO QCH_FILE = QHP_NAMESPACE = org.doxygen.Project QHP_VIRTUAL_FOLDER = doc QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = SEARCHENGINE = YES SERVER_BASED_SEARCH = NO EXTERNAL_SEARCH = NO SEARCHENGINE_URL = SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = YES LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = YES PAPER_TYPE = a4 EXTRA_PACKAGES = LATEX_HEADER = LATEX_FOOTER = LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = DIA_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO DOT_NUM_THREADS = 0 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png INTERACTIVE_SVG = NO DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = DIAFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES drpm-0.5.1/drpm.pc.in000066400000000000000000000004671421165313000143550ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ includedir=${prefix}/include Name: drpm Description: A library for making, reading and applying deltarpm packages URL: https://github.com/rpm-software-management/drpm Version: @DRPM_VERSION@ Libs: -L${libdir} -ldrpm Cflags: -I${includedir} drpm-0.5.1/drpm.spec000066400000000000000000000105561421165313000143000ustar00rootroot00000000000000%define __cmake_in_source_build 1 # Do not build with zstd for RHEL < 8 %if (0%{?rhel} && 0%{?rhel} < 8) || (0%{?suse_version} && 0%{?suse_version} < 1500) %bcond_with zstd %else %bcond_without zstd %endif Name: drpm Version: 0.5.1 Release: 1%{?dist} Summary: A library for making, reading and applying deltarpm packages # the entire source code is LGPLv2+, except src/drpm_diff.c and src/drpm_search.c which are BSD License: LGPLv2+ and BSD URL: https://github.com/rpm-software-management/%{name} Source: %{url}/releases/download/%{version}/%{name}-%{version}.tar.bz2 BuildRequires: cmake >= 2.8.5 BuildRequires: gcc BuildRequires: rpm-devel BuildRequires: openssl-devel BuildRequires: zlib-devel BuildRequires: bzip2-devel BuildRequires: xz-devel %if 0%{?suse_version} BuildRequires: lzlib-devel %endif %if %{with zstd} BuildRequires: pkgconfig(libzstd) %endif BuildRequires: pkgconfig BuildRequires: doxygen BuildRequires: libcmocka-devel >= 1.0 %ifarch %{ix86} x86_64 ppc ppc64 ppc64le s390x armv7hl aarch64 BuildRequires: valgrind %endif %description The drpm package provides a library for making, reading and applying deltarpms, compatible with the original deltarpm packages. %package devel Summary: C interface for the drpm library Requires: %{name}%{?_isa} = %{version}-%{release} %description devel The drpm-devel package provides a C interface (drpm.h) for the drpm library. %prep %autosetup mkdir build %build pushd build %cmake .. -DWITH_ZSTD:BOOL=%{?with_zstd:ON}%{!?with_zstd:OFF} -DHAVE_LZLIB_DEVEL:BOOL=%{?suse_version:ON}%{!?suse_version:OFF} %make_build make doc popd %install pushd build %make_install popd %check pushd build ctest -VV popd %if (0%{?rhel} && 0%{?rhel} < 8) || 0%{?suse_version} %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %endif %files %{_libdir}/lib%{name}.so.* %license COPYING LICENSE.BSD %files devel %doc build/doc/html/ %{_libdir}/lib%{name}.so %{_includedir}/%{name}.h %{_libdir}/pkgconfig/%{name}.pc %changelog * Tue Mar 08 2022 Pavla Kratochvilova - 0.5.1-1 - Fix SIGSEGV when an errors occurs in `rpm_get_file_info` (RhBug:1968594) - For rpms without any files return file count 0 (RhBug:1968594) * Tue Jun 02 2020 Neal Gompa 0.5.0-1 - Enable zstd support for RHEL 8 - Fix license file entry in files list in spec - Fix a memory leak on invalid input - Hide the internal library symbols * Wed Sep 11 2019 Neal Gompa 0.4.1-1 - Relicense to LGPLv2+ * Wed Aug 14 2019 Neal Gompa 0.4.0-1 - Add support for zstd drpms - CMake cleanups - Make running tests optional - Small spec improvements * Tue May 3 2016 Matej Chalk 0.3.0-3 - Now contains makedeltarpm and applydeltarpm functionality - Added lzlib-devel dependency for OpenSUSE * Tue Apr 12 2016 Igor Gnatenko - 0.3.0-2 - Cleanup spec - Make build out-of-tree - Sync with valgrind arches - Build documentation * Thu Sep 3 2015 Matej Chalk 0.3.0-1 - Bumped minor version (deltarpm creation added) * Tue Aug 4 2015 Matej Chalk 0.2.1-1 - Added openssl dependency * Fri Jul 24 2015 Matej Chalk 0.2.0-2 - Fixed bug in test suite * Tue Jun 23 2015 Matej Chalk 0.2.0-1 - Bumped minor version * Fri Jun 19 2015 Matej Chalk 0.1.3-4 - Memory test only for architectures that have valgrind (#1232157) * Wed Mar 11 2015 Matej Chalk 0.1.3-3 - Added cmocka and valgrind package dependencies * Fri Mar 6 2015 Matej Chalk 0.1.3-2 - Added check section * Fri Feb 13 2015 Matej Chalk 0.1.3-1 - Bumped version to 0.1.3 - Added CMake tool * Fri Dec 19 2014 Matej Chalk 0.1.2-4 - Enabled hardened build * Mon Dec 15 2014 Matej Chalk 0.1.2-3 - Added unversioned .so to package to enable linking with -ldrpm * Thu Dec 11 2014 Matej Chalk 0.1.2-2 - Removed unversioned .so from package - Included copies of both GPLv3 and LGPLv3 * Wed Dec 3 2014 Matej Chalk 0.1.2-1 - Bumped version to 0.1.2 - Added drpm.pc file for pkgconfig tool * Thu Nov 6 2014 Matej Chalk 0.1.1-1 - Bumped version to 0.1.1 * Wed Nov 5 2014 Matej Chalk 0.1.0-1 - Initial RPM release drpm-0.5.1/src/000077500000000000000000000000001421165313000132425ustar00rootroot00000000000000drpm-0.5.1/src/CMakeLists.txt000066400000000000000000000012651421165313000160060ustar00rootroot00000000000000set(DRPM_SOVERSION ${DRPM_MAJOR_VERSION}) set(CMAKE_BUILD_TYPE RelWithDebInfo) if (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) set(ARCH_LESS_64BIT 1) endif() configure_file(config.h.in ${CMAKE_BINARY_DIR}/config.h) add_library(drpm SHARED ${DRPM_SOURCES}) set_source_files_properties(${DRPM_SOURCES} PROPERTIES COMPILE_FLAGS "-std=c99 -pedantic -Wall -Wextra -DHAVE_CONFIG_H -I${CMAKE_BINARY_DIR}" ) target_link_libraries(drpm ${DRPM_LINK_LIBRARIES}) set_target_properties(drpm PROPERTIES VERSION ${DRPM_VERSION} SOVERSION ${DRPM_SOVERSION} ) install(TARGETS drpm LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}) install(FILES drpm.h DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}) drpm-0.5.1/src/config.h.in000066400000000000000000000017301421165313000152660ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2015 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifndef _CONFIG_H_ #define _CONFIG_H_ #cmakedefine ARCH_LESS_64BIT #cmakedefine HAVE_LZLIB_DEVEL #cmakedefine WITH_ZSTD #ifdef ARCH_LESS_64BIT #define _FILE_OFFSET_BITS 64 #endif #define _XOPEN_SOURCE 700 #endif drpm-0.5.1/src/drpm.c000066400000000000000000000723771421165313000143700ustar00rootroot00000000000000/* Authors: Pavel Tobias Matej Chalk Copyright (C) 2014 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include #include #include #include #include #include const char *drpm_strerror(int error) { switch (error) { case DRPM_ERR_OK: return "no error"; case DRPM_ERR_MEMORY: return "memory allocation error"; case DRPM_ERR_ARGS: return "bad arguments"; case DRPM_ERR_IO: return "I/O error"; case DRPM_ERR_FORMAT: return "wrong file format"; case DRPM_ERR_CONFIG: return "misconfigured external library"; case DRPM_ERR_OTHER: return "unspecified/unknown error"; case DRPM_ERR_OVERFLOW: return "file too large"; case DRPM_ERR_PROG: return "internal programming error"; case DRPM_ERR_MISMATCH: return "file changed"; case DRPM_ERR_NOINSTALL: return "old RPM not installed"; default: return "(undefined error value)"; } } /***************************** drpm read ******************************/ int drpm_read(struct drpm **delta_ret, const char *filename) { struct deltarpm delta = {0}; int error = DRPM_ERR_OK; if (filename == NULL || delta_ret == NULL) return DRPM_ERR_ARGS; if ((error = read_deltarpm(&delta, filename)) != DRPM_ERR_OK) goto cleanup; if ((*delta_ret = malloc(sizeof(struct drpm))) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if ((error = deltarpm_to_drpm(&delta, *delta_ret)) != DRPM_ERR_OK) goto cleanup; cleanup: free_deltarpm(&delta); if (error != DRPM_ERR_OK) *delta_ret = NULL; return error; } int drpm_destroy(struct drpm **delta) { if (delta == NULL || *delta == NULL) return DRPM_ERR_ARGS; drpm_free(*delta); free(*delta); *delta = NULL; return DRPM_ERR_OK; } int drpm_get_uint(struct drpm *delta, int tag, unsigned *ret) { if (delta == NULL || ret == NULL) return DRPM_ERR_ARGS; switch (tag) { case DRPM_TAG_VERSION: *ret = (unsigned)delta->version; break; case DRPM_TAG_TYPE: *ret = (unsigned)delta->type; break; case DRPM_TAG_COMP: *ret = (unsigned)delta->comp; break; case DRPM_TAG_TGTSIZE: // for backward compatibility (possible loss) *ret = (unsigned)delta->tgt_size; break; case DRPM_TAG_TGTCOMP: *ret = (unsigned)delta->tgt_comp; break; default: return DRPM_ERR_ARGS; } return DRPM_ERR_OK; } int drpm_get_ulong(struct drpm *delta, int tag, unsigned long *ret) { if (delta == NULL || ret == NULL) return DRPM_ERR_ARGS; switch (tag) { case DRPM_TAG_VERSION: *ret = (unsigned long)delta->version; break; case DRPM_TAG_TYPE: *ret = (unsigned long)delta->type; break; case DRPM_TAG_COMP: *ret = (unsigned long)delta->comp; break; case DRPM_TAG_TGTSIZE: *ret = (unsigned long)delta->tgt_size; break; case DRPM_TAG_TGTCOMP: *ret = (unsigned long)delta->tgt_comp; break; case DRPM_TAG_TGTHEADERLEN: *ret = (unsigned long)delta->tgt_header_len; break; case DRPM_TAG_PAYLOADFMTOFF: *ret = (unsigned long)delta->payload_fmt_off; break; default: return DRPM_ERR_ARGS; } return DRPM_ERR_OK; } int drpm_get_ullong(struct drpm *delta, int tag, unsigned long long *ret) { if (delta == NULL || ret == NULL) return DRPM_ERR_ARGS; switch (tag) { case DRPM_TAG_VERSION: *ret = (unsigned long long)delta->version; break; case DRPM_TAG_TYPE: *ret = (unsigned long long)delta->type; break; case DRPM_TAG_COMP: *ret = (unsigned long long)delta->comp; break; case DRPM_TAG_TGTSIZE: *ret = (unsigned long long)delta->tgt_size; break; case DRPM_TAG_TGTCOMP: *ret = (unsigned long long)delta->tgt_comp; break; case DRPM_TAG_TGTHEADERLEN: *ret = (unsigned long long)delta->tgt_header_len; break; case DRPM_TAG_PAYLOADFMTOFF: *ret = (unsigned long long)delta->payload_fmt_off; break; case DRPM_TAG_EXTDATALEN: *ret = (unsigned long long)delta->ext_data_len; break; case DRPM_TAG_INTDATALEN: *ret = (unsigned long long)delta->int_data_len; break; default: return DRPM_ERR_ARGS; } return DRPM_ERR_OK; } int drpm_get_string(struct drpm *delta, int tag, char **ret) { char *string; if (delta == NULL || ret == NULL) return DRPM_ERR_ARGS; switch (tag) { case DRPM_TAG_FILENAME: string = delta->filename; break; case DRPM_TAG_SEQUENCE: string = delta->sequence; break; case DRPM_TAG_SRCNEVR: string = delta->src_nevr; break; case DRPM_TAG_TGTNEVR: string = delta->tgt_nevr; break; case DRPM_TAG_TGTMD5: string = delta->tgt_md5; break; case DRPM_TAG_TGTCOMPPARAM: string = delta->tgt_comp_param; break; case DRPM_TAG_TGTLEAD: string = delta->tgt_leadsig; break; default: return DRPM_ERR_ARGS; } if (string == NULL) { *ret = NULL; } else { if ((*ret = malloc(strlen(string) + 1)) == NULL) return DRPM_ERR_MEMORY; strcpy(*ret, string); } return DRPM_ERR_OK; } int drpm_get_ulong_array(struct drpm *delta, int tag, unsigned long **ret_array, unsigned long *ret_size) { uint32_t *array; if (delta == NULL || ret_array == NULL || ret_size == NULL) return DRPM_ERR_ARGS; switch (tag) { case DRPM_TAG_ADJELEMS: array = delta->offadj_elems; *ret_size = (unsigned long)delta->offadj_elems_size; break; case DRPM_TAG_INTCOPIES: array = delta->int_copies; *ret_size = (unsigned long)delta->int_copies_size; break; case DRPM_TAG_EXTCOPIES: array = delta->ext_copies; *ret_size = (unsigned long)delta->ext_copies_size; break; default: return DRPM_ERR_ARGS; } if (*ret_size == 0) { *ret_array = NULL; } else { if ((*ret_array = malloc(*ret_size * sizeof(unsigned long))) == NULL) return DRPM_ERR_MEMORY; for (unsigned long i = 0; i < *ret_size; i++) (*ret_array)[i] = (unsigned long)array[i]; } return DRPM_ERR_OK; } /***************************** drpm make ******************************/ int drpm_make(const char *old_rpm_name, const char *new_rpm_name, const char *deltarpm_name, const drpm_make_options *user_opts) { int error = DRPM_ERR_OK; drpm_make_options opts = {0}; const bool rpm_only = (user_opts != NULL && user_opts->rpm_only); const bool alone = (old_rpm_name == NULL || new_rpm_name == NULL); const char *solo_rpm_name = NULL; struct rpm *solo_rpm = NULL; struct rpm *old_rpm = NULL; struct rpm *new_rpm = NULL; unsigned char *old_cpio = NULL; size_t old_cpio_len = 0; unsigned char *old_cpio_tmp; unsigned char *new_cpio = NULL; size_t new_cpio_len = 0; unsigned char *new_cpio_tmp; unsigned char *old_header = NULL; uint32_t old_header_len = 0; unsigned char *new_header = NULL; uint32_t new_header_len = 0; unsigned short payload_format; struct rpm_patches *patches = NULL; struct deltarpm delta = {0}; if (deltarpm_name == NULL || (old_rpm_name == NULL && new_rpm_name == NULL)) return DRPM_ERR_ARGS; if (alone) solo_rpm_name = (old_rpm_name == NULL) ? new_rpm_name : old_rpm_name; if (user_opts == NULL) drpm_make_options_defaults(&opts); else drpm_make_options_copy(&opts, user_opts); if (rpm_only && opts.version < 3) return DRPM_ERR_ARGS; delta.filename = deltarpm_name; delta.type = rpm_only ? DRPM_TYPE_RPMONLY : DRPM_TYPE_STANDARD; delta.version = opts.version; if (!opts.comp_from_rpm) { delta.comp = opts.comp; delta.comp_level = opts.comp_level; } /* no diff to perform for identity rpm-only deltarpms */ if (alone && rpm_only) { if ((error = fill_nodiff_deltarpm(&delta, solo_rpm_name, opts.comp_from_rpm)) != DRPM_ERR_OK) goto cleanup; goto write_files; } if (!rpm_only && (error = patches_read(opts.oldrpmprint, opts.oldpatchrpm, &patches)) != DRPM_ERR_OK) goto cleanup; /* reading RPM(s) (also creating MD5 sums and determining compressor from archive) */ if (alone) { if ((error = rpm_read(&solo_rpm, solo_rpm_name, RPM_ARCHIVE_READ_DECOMP, &delta.tgt_comp, NULL, delta.tgt_md5)) != DRPM_ERR_OK) goto cleanup; } else { if (rpm_only) { if ((delta.sequence = malloc(MD5_DIGEST_LENGTH)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } delta.sequence_len = MD5_DIGEST_LENGTH; } if ((error = rpm_read(&old_rpm, old_rpm_name, RPM_ARCHIVE_READ_DECOMP, NULL, rpm_only ? delta.sequence : NULL, NULL)) != DRPM_ERR_OK || (error = rpm_read(&new_rpm, new_rpm_name, RPM_ARCHIVE_READ_DECOMP, &delta.tgt_comp, NULL, delta.tgt_md5)) != DRPM_ERR_OK) goto cleanup; } /* checking if archive is in CPIO format */ if ((error = rpm_get_payload_format(alone ? solo_rpm : new_rpm, &payload_format)) != DRPM_ERR_OK) goto cleanup; if (payload_format != RPM_PAYLOAD_FORMAT_CPIO) { // deltarpm doesn't support xar (TODO) error = DRPM_ERR_FORMAT; goto cleanup; } /* reading compression level of target RPM */ if ((error = rpm_get_comp_level(alone ? solo_rpm : new_rpm, &delta.tgt_comp_level)) != DRPM_ERR_OK) goto cleanup; /* matching RPM compression if no compression specified by user */ if (opts.comp_from_rpm) { delta.comp = delta.tgt_comp; delta.comp_level = delta.tgt_comp_level; } if (!rpm_only) delta.head.tgt_rpm = alone ? solo_rpm : new_rpm; /* reading source and target NEVRs */ if ((error = rpm_get_nevr(alone ? solo_rpm : old_rpm, &delta.src_nevr)) != DRPM_ERR_OK || (rpm_only && (error = rpm_get_nevr(new_rpm, &delta.head.tgt_nevr)) != DRPM_ERR_OK)) goto cleanup; if (patches != NULL && (error = patches_check_nevr(patches, delta.src_nevr)) != DRPM_ERR_OK) goto cleanup; if ((error = rpm_fetch_lead_and_signature(alone ? solo_rpm : new_rpm, &delta.tgt_leadsig, &delta.tgt_leadsig_len)) != DRPM_ERR_OK) goto cleanup; /* storing size of target RPM file */ delta.tgt_size = rpm_size_full(alone ? solo_rpm : new_rpm); /* creating old_cpio and new_cpio for binary diff */ if (rpm_only) { /* rpm-only deltarpms include RPM headers in diff */ if ((error = rpm_fetch_header(old_rpm, &old_header, &old_header_len)) != DRPM_ERR_OK || (error = rpm_fetch_header(new_rpm, &new_header, &new_header_len)) != DRPM_ERR_OK || (error = rpm_fetch_archive(old_rpm, &old_cpio, &old_cpio_len)) != DRPM_ERR_OK || (error = rpm_fetch_archive(new_rpm, &new_cpio, &new_cpio_len)) != DRPM_ERR_OK) goto cleanup; if ((old_cpio_tmp = realloc(old_cpio, old_header_len + old_cpio_len)) == NULL || (new_cpio_tmp = realloc(new_cpio, new_header_len + new_cpio_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } old_cpio = old_cpio_tmp; new_cpio = new_cpio_tmp; memmove(old_cpio + old_header_len, old_cpio, old_cpio_len); memmove(new_cpio + new_header_len, new_cpio, new_cpio_len); memcpy(old_cpio, old_header, old_header_len); memcpy(new_cpio, new_header, new_header_len); old_cpio_len += old_header_len; new_cpio_len += new_header_len; /* storing size of target header included in diff */ delta.tgt_header_len = new_header_len; } else { /* standard deltarpms parse archive of old RPM based on filesystem data */ if ((error = parse_cpio_from_rpm_filedata(alone ? solo_rpm : old_rpm, &old_cpio, &old_cpio_len, &delta.sequence, &delta.sequence_len, (delta.version >= 3) ? &delta.offadj_elems : NULL, (delta.version >= 3) ? &delta.offadj_elems_count : NULL, patches)) != DRPM_ERR_OK || (error = rpm_fetch_archive(alone ? solo_rpm : new_rpm, &new_cpio, &new_cpio_len)) != DRPM_ERR_OK) goto cleanup; } /* patching and storing offset of payload format tag in header for compatibility with deltarpm */ if ((!rpm_only && (error = rpm_patch_payload_format(delta.head.tgt_rpm, "drpm")) != DRPM_ERR_OK) || (error = rpm_find_payload_format_offset(alone ? solo_rpm : new_rpm, &delta.payload_fmt_off)) != DRPM_ERR_OK) goto cleanup; /* diff algorithm, creating deltarpm diff data */ if ((error = make_diff(old_cpio, old_cpio_len, new_cpio, new_cpio_len, &delta.int_data.ptrs, &delta.int_data_len, &delta.ext_copies, &delta.ext_copies_count, &delta.int_copies, &delta.int_copies_count, opts.addblk ? &delta.add_data : NULL, opts.addblk ? &delta.add_data_len : NULL, opts.addblk_comp, opts.addblk_comp_level)) != DRPM_ERR_OK) goto cleanup; delta.int_data_as_ptrs = true; delta.ext_data_len = old_cpio_len; write_files: if ((error = write_deltarpm(&delta)) != DRPM_ERR_OK) goto cleanup; if (opts.seqfile != NULL) error = write_seqfile(&delta, opts.seqfile); cleanup: free_deltarpm(&delta); rpm_destroy(&old_rpm); if (rpm_only) // preventing double free (delta.head.tgt_rpm) rpm_destroy(&new_rpm); free(old_cpio); free(new_cpio); free(old_header); free(new_header); patches_destroy(&patches); free(opts.seqfile); free(opts.oldrpmprint); free(opts.oldpatchrpm); return error; } /***************************** drpm apply *****************************/ int drpm_apply(const char *old_rpm_name, const char *deltarpm_name, const char *new_rpm_name) { int error = DRPM_ERR_OK; struct deltarpm delta = {0}; const bool from_rpm = (old_rpm_name != NULL); bool rpm_only; struct rpm *old_rpm = NULL; struct rpm *patched_rpm = NULL; unsigned char oldsig_md5[MD5_DIGEST_LENGTH]; unsigned char newsig_md5[MD5_DIGEST_LENGTH]; char *old_rpm_nevr = NULL; struct file_info *files = NULL; size_t file_count = 0; unsigned short digest_algo; struct cpio_file *cpio_files = NULL; size_t cpio_files_len = 0; struct blocks *blks = NULL; int filedesc; MD5_CTX md5; unsigned char md5_digest[MD5_DIGEST_LENGTH]; bool no_full_md5; bool has_md5; const unsigned char empty_md5[MD5_DIGEST_LENGTH] = {0}; struct decompstrm *addblk_strm = NULL; unsigned char *addblk_buf = NULL; unsigned char *buffer = NULL; size_t buffer_len; unsigned char *header = NULL; uint32_t header_size; struct compstrm_wrapper *csw = NULL; const uint32_t *int_copies; uint32_t int_copies_count; size_t int_copy_len; const unsigned char *int_data; const uint32_t *ext_copies; uint32_t ext_copies_count; size_t ext_copy_len; uint64_t ext_offset = 0; uint32_t ext_copies_todo; size_t ext_copies_done = 0; size_t blk_id; unsigned char *comp_data; size_t comp_data_len; if (deltarpm_name == NULL || new_rpm_name == NULL) return DRPM_ERR_ARGS; if ((filedesc = creat(new_rpm_name, CREAT_MODE)) < 0) return DRPM_ERR_IO; /* reading DeltaRPM */ if ((error = read_deltarpm(&delta, deltarpm_name)) != DRPM_ERR_OK) goto cleanup; rpm_only = (delta.type == DRPM_TYPE_RPMONLY); no_full_md5 = (memcmp(empty_md5, delta.tgt_md5, MD5_DIGEST_LENGTH) == 0); if (from_rpm) { /* reading old RPM */ if ((error = rpm_read(&old_rpm, old_rpm_name, RPM_ARCHIVE_READ_DECOMP, NULL, NULL, NULL)) != DRPM_ERR_OK) goto cleanup; if (rpm_only) { /* comparing signature MD5 with DeltaRPM sequence */ if ((error = rpm_signature_get_md5(old_rpm, oldsig_md5, &has_md5)) != DRPM_ERR_OK) goto cleanup; if (!has_md5) { error = DRPM_ERR_FORMAT; goto cleanup; } if (memcmp(delta.sequence, oldsig_md5, MD5_DIGEST_LENGTH) != 0) { error = DRPM_ERR_MISMATCH; goto cleanup; } } } else { // rpm-only deltarpms do not work from filesystem if (rpm_only) return DRPM_ERR_ARGS; // cannot reconstruct source RPMs from filesystem if (rpm_is_sourcerpm(delta.head.tgt_rpm)) return DRPM_ERR_ARGS; /* reading old RPM header from database */ if ((error = rpm_read_header(&old_rpm, delta.src_nevr, NULL)) != DRPM_ERR_OK) goto cleanup; } /* comparing source NEVRs */ if ((error = rpm_get_nevr(old_rpm, &old_rpm_nevr)) != DRPM_ERR_OK) goto cleanup; if (strcmp(delta.src_nevr, old_rpm_nevr) != 0) { error = DRPM_ERR_MISMATCH; goto cleanup; } if (!rpm_only) { /* expanding sequence */ if ((error = rpm_get_file_info(old_rpm, &files, &file_count, NULL)) != DRPM_ERR_OK || (error = rpm_get_digest_algo(old_rpm, &digest_algo)) != DRPM_ERR_OK || (error = expand_sequence(&cpio_files, &cpio_files_len, delta.sequence, delta.sequence_len, files, file_count, digest_algo, DRPM_CHECK_NONE)) != DRPM_ERR_OK) goto cleanup; } /* overwriting old RPM's lead and signature with new RPM's */ patched_rpm = old_rpm; if ((error = rpm_replace_lead_and_signature(patched_rpm, delta.tgt_leadsig, delta.tgt_leadsig_len)) != DRPM_ERR_OK) goto cleanup; if (rpm_only && delta.tgt_comp == DRPM_COMP_NONE && delta.int_copies_count == 0 && delta.ext_copies_count == 0) { /* no-diff DeltaRPM, no need for reconstruction */ if ((error = rpm_write(patched_rpm, new_rpm_name, true, md5_digest, !no_full_md5)) != DRPM_ERR_OK) goto cleanup; goto final_check; } /* creating blocks for reading external data */ if ((error = blocks_create(&blks, delta.ext_data_len, files, cpio_files, cpio_files_len, delta.ext_copies, delta.ext_copies_count, from_rpm ? old_rpm : NULL, rpm_only)) != DRPM_ERR_OK) goto cleanup; /* setting up add block */ if (delta.add_data_len > 0) { if ((error = decompstrm_init(&addblk_strm, -1, NULL, NULL, delta.add_data, delta.add_data_len)) != DRPM_ERR_OK) goto cleanup; if ((addblk_buf = malloc(block_size())) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } } if ((buffer = malloc(block_size())) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if (MD5_Init(&md5) != 1) { error = DRPM_ERR_OTHER; goto cleanup; } /* writing lead and signature of new RPM */ if (write(filedesc, delta.tgt_leadsig, delta.tgt_leadsig_len) != (ssize_t)delta.tgt_leadsig_len) { error = DRPM_ERR_IO; goto cleanup; } if (!no_full_md5 && MD5_Update(&md5, delta.tgt_leadsig, delta.tgt_leadsig_len) != 1) { error = DRPM_ERR_OTHER; goto cleanup; } if (!rpm_only) { /* standard delta -> write out header (rpm-only includes it in diff) */ if ((error = rpm_patch_payload_format(delta.head.tgt_rpm, "cpio")) != DRPM_ERR_OK || (error = rpm_fetch_header(delta.head.tgt_rpm, &header, &header_size)) != DRPM_ERR_OK) goto cleanup; if (write(filedesc, header, header_size) != (ssize_t)header_size) { error = DRPM_ERR_IO; goto cleanup; } if (MD5_Update(&md5, header, header_size) != 1) { error = DRPM_ERR_OTHER; goto cleanup; } } /* compression stream wrapper, makes sure header is uncompressed if included */ if ((error = compstrm_wrapper_init(&csw, delta.tgt_header_len, filedesc, delta.tgt_comp, delta.tgt_comp_level)) != DRPM_ERR_OK) goto cleanup; /* reconstructing from diff data */ int_copies = delta.int_copies; int_copies_count = delta.int_copies_count; ext_copies = delta.ext_copies; ext_copies_count = delta.ext_copies_count; int_data = delta.int_data.bytes; while (int_copies_count--) { ext_copies_todo = *int_copies++; if (ext_copies_todo > ext_copies_count) { error = DRPM_ERR_FORMAT; goto cleanup; } /* performing X external copies before next internal copy */ while (ext_copies_todo--) { ext_offset += (int32_t)*ext_copies++; // adjusting external offset ext_copy_len = *ext_copies++; // length of external copy ext_copies_count--; blk_id = block_id(ext_offset); /* performing external copy */ while (ext_copy_len > 0) { if ((error = blocks_next(blks, buffer, &buffer_len, ext_offset, ext_copy_len, ext_copies_done, blk_id)) != DRPM_ERR_OK) goto cleanup; /* applying add block */ if (delta.add_data_len > 0) { if ((error = decompstrm_read(addblk_strm, buffer_len, addblk_buf)) != DRPM_ERR_OK) goto cleanup; for (size_t i = 0; i < buffer_len; i++) buffer[i] += (signed char)addblk_buf[i]; } if ((error = compstrm_wrapper_write(csw, buffer, buffer_len)) != DRPM_ERR_OK) goto cleanup; ext_copy_len -= buffer_len; ext_offset += buffer_len; blk_id++; } ext_copies_done++; } int_copy_len = *int_copies++; /* performing internal copy */ if ((error = compstrm_wrapper_write(csw, int_data, int_copy_len)) != DRPM_ERR_OK) goto cleanup; int_data += int_copy_len; } if ((error = compstrm_wrapper_finish(csw, &comp_data, &comp_data_len)) != DRPM_ERR_OK) goto cleanup; /* finalizing MD5 of written data */ if (MD5_Update(&md5, comp_data, comp_data_len) != 1 || MD5_Final(md5_digest, &md5) != 1) { error = DRPM_ERR_OTHER; goto cleanup; } final_check: if (no_full_md5) { /* no target MD5 -> only match checksums of header and archive */ if ((error = rpm_signature_get_md5(patched_rpm, newsig_md5, &has_md5)) != DRPM_ERR_OK) goto cleanup; if (has_md5 && memcmp(md5_digest, newsig_md5, MD5_DIGEST_LENGTH) != 0) { error = DRPM_ERR_MISMATCH; goto cleanup; } } else { /* match full MD5 */ if (memcmp(md5_digest, delta.tgt_md5, MD5_DIGEST_LENGTH) != 0) { error = DRPM_ERR_MISMATCH; goto cleanup; } } cleanup: close(filedesc); for (size_t i = 0; i < file_count; i++) { free(files[i].name); free(files[i].md5); free(files[i].linkto); } free(files); free_deltarpm(&delta); rpm_destroy(&old_rpm); free(old_rpm_nevr); blocks_destroy(&blks); decompstrm_destroy(&addblk_strm); compstrm_wrapper_destroy(&csw); free(cpio_files); free(addblk_buf); free(buffer); free(header); free(comp_data); return error; } int drpm_check(const char *deltarpm_name, int check_mode) { int error = DRPM_ERR_OK; struct deltarpm delta = {0}; struct rpm *old_rpm = NULL; char *old_rpm_nevr = NULL; struct file_info *files = NULL; size_t file_count = 0; unsigned short digest_algo; if (deltarpm_name == NULL || (check_mode != DRPM_CHECK_FILESIZES && check_mode != DRPM_CHECK_FULL)) return DRPM_ERR_ARGS; /* reading DeltaRPM */ if ((error = read_deltarpm(&delta, deltarpm_name)) != DRPM_ERR_OK) goto cleanup; /* reading old RPM header from database */ if ((error = rpm_read_header(&old_rpm, delta.src_nevr, NULL)) != DRPM_ERR_OK) goto cleanup; /* checking NEVRs */ if ((error = rpm_get_nevr(old_rpm, &old_rpm_nevr)) != DRPM_ERR_OK) goto cleanup; if (strcmp(delta.src_nevr, old_rpm_nevr) != 0) { error = DRPM_ERR_MISMATCH; goto cleanup; } if (delta.type == DRPM_TYPE_STANDARD) { /* expanding sequence, checking files */ if ((error = rpm_get_file_info(old_rpm, &files, &file_count, NULL)) != DRPM_ERR_OK || (error = rpm_get_digest_algo(old_rpm, &digest_algo)) != DRPM_ERR_OK || (error = expand_sequence(NULL, NULL, delta.sequence, delta.sequence_len, files, file_count, digest_algo, check_mode)) != DRPM_ERR_OK) goto cleanup; } cleanup: for (size_t i = 0; i < file_count; i++) { free(files[i].name); free(files[i].md5); free(files[i].linkto); } free(files); free_deltarpm(&delta); rpm_destroy(&old_rpm); free(old_rpm_nevr); return error; } int drpm_check_sequence(const char *old_rpm_name, const char *sequence, int check_mode) { int error = DRPM_ERR_OK; char *nevr = NULL; unsigned char *seq = NULL; size_t seq_len; char *ptr; ptrdiff_t nevr_len; struct rpm *old_rpm = NULL; unsigned char sigmd5[MD5_DIGEST_LENGTH]; bool has_md5; bool rpm_only; char *old_rpm_nevr = NULL; struct file_info *files = NULL; size_t file_count = 0; unsigned short digest_algo; if (sequence == NULL || (check_mode != DRPM_CHECK_NONE && check_mode != DRPM_CHECK_FILESIZES && check_mode != DRPM_CHECK_FULL) || (old_rpm_name != NULL && check_mode != DRPM_CHECK_NONE)) return DRPM_ERR_ARGS; /* parsing sequence ID into source NEVR and sequence */ ptr = strrchr(sequence, '-'); if (ptr == NULL || ptr == sequence) return DRPM_ERR_FORMAT; nevr_len = ptr - sequence; seq_len = (strlen(++ptr)) / 2; if (seq_len < MD5_DIGEST_LENGTH) { error = DRPM_ERR_FORMAT; goto cleanup; } if ((nevr = malloc(nevr_len + 1)) == NULL || (seq = malloc(seq_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } strncpy(nevr, sequence, nevr_len); nevr[nevr_len] = '\0'; if (parse_hex(seq, ptr) != (ssize_t)seq_len) { error = DRPM_ERR_FORMAT; goto cleanup; } if (old_rpm_name == NULL) { /* reading header from database */ if ((error = rpm_read_header(&old_rpm, nevr, NULL)) != DRPM_ERR_OK) goto cleanup; rpm_only = false; } else { /* reading old RPM */ if ((error = rpm_read(&old_rpm, old_rpm_name, RPM_ARCHIVE_DONT_READ, NULL, NULL, NULL)) != DRPM_ERR_OK || (error = rpm_signature_get_md5(old_rpm, sigmd5, &has_md5)) != DRPM_ERR_OK) goto cleanup; // determining type of delta rpm_only = (seq_len == MD5_DIGEST_LENGTH && has_md5 && memcmp(seq, sigmd5, MD5_DIGEST_LENGTH) == 0); } /* checking NEVRs */ if ((error = rpm_get_nevr(old_rpm, &old_rpm_nevr)) != DRPM_ERR_OK) goto cleanup; if (strcmp(nevr, old_rpm_nevr) != 0) { error = DRPM_ERR_MISMATCH; goto cleanup; } if (!rpm_only) { /* expanding sequence, checking files */ if ((error = rpm_get_file_info(old_rpm, &files, &file_count, NULL)) != DRPM_ERR_OK || (error = rpm_get_digest_algo(old_rpm, &digest_algo)) != DRPM_ERR_OK || (error = expand_sequence(NULL, NULL, seq, seq_len, files, file_count, digest_algo, check_mode)) != DRPM_ERR_OK) goto cleanup; } cleanup: for (size_t i = 0; i < file_count; i++) { free(files[i].name); free(files[i].md5); free(files[i].linkto); } free(files); free(nevr); free(seq); free(old_rpm_nevr); rpm_destroy(&old_rpm); return error; } drpm-0.5.1/src/drpm.h000066400000000000000000000545761421165313000143760ustar00rootroot00000000000000/* Authors: Pavel Tobias Matej Chalk Copyright (C) 2014 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ /** * @file * @author Pavel Tobias * @author Matej Chalk * @date 2014-2016 * @copyright Copyright © 2014-2016 Red Hat, Inc. * This project is released under the GNU Lesser Public License. */ #ifndef _DRPM_H_ #define _DRPM_H_ #ifdef HAVE_CONFIG_H #include #endif #if __GNUC__ >= 4 #define DRPM_VISIBLE __attribute__((visibility("default"))) #else #define DRPM_VISIBLE #endif /** * @defgroup drpmMake DRPM Make * Tools for creating a DeltaRPM file from two RPM files, * providing the same functionality as * [makedeltarpm(8)](http://linux.die.net/man/8/makedeltarpm). * @{ * @defgroup drpmMakeOptions DRPM Make Options * Tools for customizing DeltaRPM creation. * @} * * @defgroup drpmApply DRPM Apply * Tools for applying a DeltaRPM file to re-create a new RPM file * (from an old RPM file or from filesystem data), * providing the same functionality as * [applydeltarpm(8)](http://linux.die.net/man/8/applydeltarpm). * @{ * @defgroup drpmCheck DRPM Check * Tools for checking if the reconstruction is possible * (like applydeltarpm { -c | -C }). * @} * * @defgroup drpmRead DRPM Read * Tools for extracting information from DeltaRPM files. */ /** * @name Errors / Return values * @{ */ #define DRPM_ERR_OK 0 /**< no error */ #define DRPM_ERR_MEMORY 1 /**< memory allocation error */ #define DRPM_ERR_ARGS 2 /**< bad arguments */ #define DRPM_ERR_IO 3 /**< I/O error */ #define DRPM_ERR_FORMAT 4 /**< wrong file format */ #define DRPM_ERR_CONFIG 5 /**< misconfigured external library */ #define DRPM_ERR_OTHER 6 /**< unspecified/unknown error */ #define DRPM_ERR_OVERFLOW 7 /**< file too large */ #define DRPM_ERR_PROG 8 /**< internal programming error */ #define DRPM_ERR_MISMATCH 9 /**< file changed */ #define DRPM_ERR_NOINSTALL 10 /**< old RPM not installed */ /** @} */ /** * @name Delta Types * @{ */ #define DRPM_TYPE_STANDARD 0 /**< standard deltarpm */ #define DRPM_TYPE_RPMONLY 1 /**< rpm-only deltarpm */ /** @} */ /** * @name Compression Types * @{ */ #define DRPM_COMP_NONE 0 /**< no compression */ #define DRPM_COMP_GZIP 1 /**< gzip */ #define DRPM_COMP_BZIP2 2 /**< bzip2 */ #define DRPM_COMP_LZMA 3 /**< lzma */ #define DRPM_COMP_XZ 4 /**< xz */ #ifdef HAVE_LZLIB_DEVEL /** * @brief lzip * * The original deltarpm implementation does not support lzip. * DeltaRPM packages compressed with lzip will work within this API, but * will not be backwards-compatible. * * This compression algorithm is supported because newer versions * of RPM packages may be compressed with lzip. */ #endif #define DRPM_COMP_LZIP 5 /**< lzip */ #define DRPM_COMP_ZSTD 6 /**< zstd */ /** @} */ /** * @name Info Tags * @{ */ #define DRPM_TAG_FILENAME 0 /**< file name */ #define DRPM_TAG_VERSION 1 /**< version */ #define DRPM_TAG_TYPE 2 /**< delta type */ #define DRPM_TAG_COMP 3 /**< compression type */ #define DRPM_TAG_SEQUENCE 4 /**< sequence */ #define DRPM_TAG_SRCNEVR 5 /**< source NEVR (name-epoch:version-release) */ #define DRPM_TAG_TGTNEVR 6 /**< target NEVR (name-epoch:version-release) */ #define DRPM_TAG_TGTSIZE 7 /**< target size */ #define DRPM_TAG_TGTMD5 8 /**< target MD5 */ #define DRPM_TAG_TGTCOMP 9 /**< target compression type */ #define DRPM_TAG_TGTCOMPPARAM 10 /**< target compression parameter block */ #define DRPM_TAG_TGTHEADERLEN 11 /**< target header length */ #define DRPM_TAG_ADJELEMS 12 /**< offset adjustment elements */ #define DRPM_TAG_TGTLEAD 13 /**< lead/signatures of the new rpm */ #define DRPM_TAG_PAYLOADFMTOFF 14 /**< payload format offset */ #define DRPM_TAG_INTCOPIES 15 /**< copies from internal data (number of external copies to do before internal copy & length of internal copy) */ #define DRPM_TAG_EXTCOPIES 16 /**< copies from external data (offset adjustment of external copy & length of external copy) */ #define DRPM_TAG_EXTDATALEN 17 /**< length of external data */ #define DRPM_TAG_INTDATALEN 18 /**< length of internal data */ /** @} */ /** * @name Compression Levels * @{ */ #define DRPM_COMP_LEVEL_DEFAULT 0 /**< default compression level for given compression type */ /** @} */ /** * @name Check Modes * @{ */ #define DRPM_CHECK_NONE 0 /**< no file checking */ #define DRPM_CHECK_FULL 1 /**< full (i.e.\ slow) on-disk checking */ #define DRPM_CHECK_FILESIZES 2 /**< only checking if filesizes have changed */ /** @} */ /** * @brief DeltaRPM package info * @ingroup drpmRead */ typedef struct drpm drpm; /** * @brief Options for drpm_make() * @ingroup drpmMakeOptions */ typedef struct drpm_make_options drpm_make_options; /** * @ingroup drpmApply * @brief Applies a DeltaRPM to an old RPM or on-disk data to re-create a new RPM. * @param [in] oldrpm Name of old RPM file (if @c NULL, filesystem data is used). * @param [in] deltarpm Name of DeltaRPM file. * @param [in] newrpm Name of new RPM file to be (re-)created. * @return Error code. */ DRPM_VISIBLE int drpm_apply(const char *oldrpm, const char *deltarpm, const char *newrpm); /** * @ingroup drpmCheck * @brief Checks if the reconstruction is possible based on DeltaRPM file. * @param [in] deltarpm Name of DeltaRPM file. * @param [in] checkmode Full check or filesize changes only. * @return Error code. * @see DRPM_CHECK_FULL, DRPM_CHECK_FILESIZES */ DRPM_VISIBLE int drpm_check(const char *deltarpm, int checkmode); /** * @ingroup drpmCheck * @brief Checks if the reconstruction is possible based on sequence ID. * @param [in] oldrpm Name of old RPM file (if @c NULL, filesystem data is used). * @param [in] sequence Sequence ID of the DeltaRPM. * @param [in] checkmode Full check or filesize changes only. * @return Error code. * @see DRPM_CHECK_FULL, DRPM_CHECK_FILESIZES */ DRPM_VISIBLE int drpm_check_sequence(const char *oldrpm, const char *sequence, int checkmode); /** * @ingroup drpmMake * @brief Creates a DeltaRPM from two RPMs. * The DeltaRPM can later be used to recreate the new RPM from either * filesystem data or the old RPM. * * Does the same thing as the * [makedeltarpm(8)](http://linux.die.net/man/8/makedeltarpm) * command-line utility. * * Examples of function calls (without error handling): * @code * // makedeltarpm foo.rpm goo.rpm fg.drpm * drpm_make("foo.rpm", "goo.rpm", "fg.drpm", NULL); * @endcode * @code * // makedeltarpm -r -z xz.6 -s seqfile.txt foo.rpm goo.rpm fg.drpm * * drpm_make_options *opts; * * drpm_make_options_init(&opts); * drpm_make_options_set_type(opts, DRPM_TYPE_RPMONLY); * drpm_make_options_set_seqfile(opts, "seqfile.txt"); * drpm_make_options_set_delta_comp(opts, DRPM_COMP_XZ, 6); * * drpm_make("foo.rpm", "goo.rpm", "fg.drpm", &opts); * * drpm_make_options_destroy(&opts); * @endcode * @code * // makedeltarpm -V 2 -z gzip,off -p foo-print.rpml foo-patch.rpml foo.rpm goo.rpm fg.drpm * * drpm_make_options *opts; * * drpm_make_options_init(&opts); * drpm_make_options_set_version(opts, 2); * drpm_make_options_set_delta_comp(opts, DRPM_COMP_GZIP, DRPM_COMP_LEVEL_DEFAULT); * drpm_make_options_forbid_addblk(opts); * drpm_make_options_add_patches(opts, "foo-print.rpml", "foo-patch.rpml"); * * drpm_make("foo.rpm", "goo.rpm", "fg.drpm", &opts); * * drpm_make_options_destroy(&opts); * @endcode * @code * // makedeltarpm -z uncompressed,bzip2.9 foo.rpm goo.rpm fg.drpm * * drpm_make_options *opts; * * drpm_make_options_init(&opts); * drpm_make_options_set_delta_comp(opts, DRPM_COMP_NONE, 0); * drpm_make_options_set_addblk_comp(opts, DRPM_COMP_BZIP2, 9); * * drpm_make("foo.rpm", "goo.rpm", "fg.drpm", &opts); * * drpm_make_options_destroy(&opts); * @endcode * @code * // makedeltarpm -u foo.rpm foo.drpm * drpm_make("foo.rpm", NULL, "foo.drpm", NULL); * @endcode * @param [in] oldrpm Name of old RPM file. * @param [in] newrpm Name of new RPM file. * @param [in] deltarpm Name of DeltaRPM file to be created. * @param [in] opts Options (if @c NULL, defaults used). * @return Error code. * @note If either @p old_rpm or @p new_rpm is @c NULL, an "identity" * deltarpm is created (may be useful to just replace the signature * of an RPM or to reconstruct an RPM from the filesystem). * @warning If not @c NULL, @p opts should have been initialized with * drpm_make_options_init(), otherwise behaviour is undefined. */ DRPM_VISIBLE int drpm_make(const char *oldrpm, const char *newrpm, const char *deltarpm, const drpm_make_options *opts); /** * @addtogroup drpmMakeOptions * @{ */ /** * @brief Initializes ::drpm_make_options with default options. * Passing @p *opts to drpm_make() immediately after would have the same * effect as passing @c NULL instead. * @param [out] opts Address of options structure pointer. * @return Error code. * @see drpm_make() */ DRPM_VISIBLE int drpm_make_options_init(drpm_make_options **opts); /** * @brief Frees ::drpm_make_options. * @param [out] opts Address of options structure pointer. * @return Error code. * @see drpm_make() */ DRPM_VISIBLE int drpm_make_options_destroy(drpm_make_options **opts); /** * @brief Resets options to default values. * Passing @p opts to drpm_make() immediately after would have the same * effect as passing @c NULL instead. * @param [out] opts Structure specifying options for drpm_make(). * @return Error code. * @see drpm_make() */ DRPM_VISIBLE int drpm_make_options_defaults(drpm_make_options *opts); /** * @brief Copies ::drpm_make_options. * Copies data from @p src to @p dst. * @param [out] dst Destination options. * @param [in] src Source options. * @return Error code. * @warning @p dst should have also been initialized with * drpm_make_options_init() previously, otherwise behaviour is undefined. * @see drpm_make() */ DRPM_VISIBLE int drpm_make_options_copy(drpm_make_options *dst, const drpm_make_options *src); /** * @brief Sets DeltaRPM type. * There are two types of DeltaRPMs: standard and "rpm-only". * The latter was introduced in version 3. * It does not work with filesystem data but is smaller and faster to * combine. * @param [out] opts Structure specifying options for drpm_make(). * @param [in] type Type of deltarpm. * @return Error code. * @see drpm_make() * @see DRPM_TYPE_STANDARD, DRPM_TYPE_RPMONLY */ DRPM_VISIBLE int drpm_make_options_set_type(drpm_make_options *opts, unsigned short type); /** * @brief Sets DeltaRPM version. * The default DeltaRPM format is V3, but an older version may also be * specified. * @param [out] opts Structure specifying options for drpm_make(). * @param [in] version Version (1-3). * @return Error code. * @see drpm_make() */ DRPM_VISIBLE int drpm_make_options_set_version(drpm_make_options *opts, unsigned short version); /** * @brief Sets DeltaRPM compression type and level. * By default, the compression method is the same as used in the new RPM. * @param [out] opts Structure specifying options for drpm_make(). * @param [in] comp Compression type. * @param [in] level Compression level (1-9 or default). * @return Error code. * @see drpm_make() * @see DRPM_COMP_NONE, DRPM_COMP_GZIP, DRPM_COMP_BZIP2, * DRPM_COMP_LZMA, DRPM_COMP_XZ * @see DRPM_COMP_LEVEL_DEFAULT */ DRPM_VISIBLE int drpm_make_options_set_delta_comp(drpm_make_options *opts, unsigned short comp, unsigned short level); /** * @brief DeltaRPM compression method is the same as used in the new RPM. * May be used to reset DeltaRPM compression option after previously * calling drpm_make_options_delta_comp(). * @param [out] opts Structure specifying options for drpm_make(). * @return Error code. * @see drpm_make() */ DRPM_VISIBLE int drpm_make_options_get_delta_comp_from_rpm(drpm_make_options *opts); /** * @brief Forbids add block creation. * An "add block" is a highly compressible block used to store * bytewise subtractions of segments where less than half the bytes * have changed. * It is used in re-creating the new RPM with drpm_apply(), unless this * functions is called to tell drpm_make() not to create an add block. * @param [out] opts Structure specifying options for drpm_make(). * @return Error code. * @see drpm_make() */ DRPM_VISIBLE int drpm_make_options_forbid_addblk(drpm_make_options *opts); /** * @brief Sets add block compression type and level. * The default add block compression type is bzip2, which gives the best * results. * @param [out] opts Structure specifying options for drpm_make(). * @param [in] comp Compression type. * @param [in] level Compression level (1-9 or default). * @return Error code. * @see drpm_make() * @see DRPM_COMP_NONE, DRPM_COMP_GZIP, DRPM_COMP_BZIP2, * DRPM_COMP_LZMA, DRPM_COMP_XZ * @see DRPM_COMP_LEVEL_DEFAULT */ DRPM_VISIBLE int drpm_make_options_set_addblk_comp(drpm_make_options *opts, unsigned short comp, unsigned short level); /** * @brief Specifies file to which to write DeltaRPM sequence ID. * If a valid file name is given, drpm_make() will write out * the sequence ID to the file @p seqfile. * @param [out] opts Structure specifying options for drpm_make(). * @param [in] seqfile Name of file to which to write out sequence. * @return Error code. * @note If @p seqfile is @c NULL, sequence ID shall not be written. * @see drpm_make() */ DRPM_VISIBLE int drpm_make_options_set_seqfile(drpm_make_options *opts, const char *seqfile); /** * @brief Requests incorporation of RPM patch files for the old RPM. * This option enables the usage of patch RPMs, telling drpm_make() to * exclude all files that were not included in the patch RPM but are not * bytewise identical to the ones in the old RPM. * @param [out] opts Structure specifying options for drpm_make(). * @param [in] oldrpmprint The rpm-print of the old RPM. * @param [in] oldpatchrpm The created patch RPM. * @return Error code. * @see drpm_make() */ DRPM_VISIBLE int drpm_make_options_add_patches(drpm_make_options *opts, const char *oldrpmprint, const char *oldpatchrpm); /** * @brief Limits memory usage. * As drpm_make() normally needs about three to four times the size of * the rpm's uncompressed payload, this option may be used to enable * a sliding block algorithm that needs @p mbytes megabytes of memory. * This trades memory usage with the size of the created DeltaRPM. * @param [out] opts Structure specifying options for drpm_make(). * @param [in] mbytes Permitted memory usage in megabytes. * @return Error code. * @see drpm_make() */ //int drpm_make_options_set_memlimit(drpm_make_options *opts, unsigned mbytes); /** @} */ /** * @addtogroup drpmRead * @{ */ /** * @brief Reads information from a DeltaRPM. * Reads information from DeltaRPM package @p filename into @p *delta. * Example of usage: * @code * drpm *delta = NULL; * * int error = drpm_read(&delta, "foo.drpm"); * * if (error != DRPM_ERR_OK) { * fprintf(stderr, "drpm error: %s\n", drpm_strerror(error)); * return; * } * @endcode * @param [out] delta DeltaRPM to be filled with info. * @param [in] filename Name of DeltaRPM file whose data is to be read. * @return Error code. * @note Memory allocated by calling drpm_read() should later be freed * by calling drpm_destroy(). */ DRPM_VISIBLE int drpm_read(drpm **delta, const char *filename); /** * @brief Fetches information representable as an unsigned integer. * Fetches information identified by @p tag from @p delta and copies it * to address pointed to by @p target. * * Example of usage: * @code * unsigned type; * * int error = drpm_get_uint(delta, DRPM_TAG_TYPE, &type); * * if (error != DRPM_ERR_OK) { * fprintf(stderr, "drpm error: %s\n", drpm_strerror(error)); * return; * } * * printf("This is a %s deltarpm\n", (type == DRPM_TYPE_STANDARD) ? "standard" : "rpm-only"); * @endcode * @param [in] delta DeltaRPM containing required info. * @param [in] tag Identifies which info is required. * @param [out] target Tagged info will be copied here. * @return error number * @warning @p delta should have been previously initialized with * drpm_read(), otherwise behaviour is undefined. * @see DRPM_TAG_VERSION * @see DRPM_TAG_TYPE * @see DRPM_TAG_COMP * @see DRPM_TAG_TGTCOMP */ DRPM_VISIBLE int drpm_get_uint(drpm *delta, int tag, unsigned *target); /** * @brief Fetches information representable as an unsigned long integer. * Fetches information identified by @p tag from @p delta and copies it * to address pointed to by @p target. * * Example of usage: * @code * unsigned long tgt_size; * * int error = drpm_get_ulong(delta, DRPM_TAG_TGTSIZE, &tgt_size); * * if (error != DRPM_ERR_OK) { * fprintf(stderr, "drpm error: %s\n", drpm_strerror(error)); * return; * } * * printf("Size of new RPM: %lu\n", tgt_size); * @endcode * @param [in] delta Deltarpm containing required info. * @param [in] tag Identifies which info is required. * @param [out] target Tagged info will be copied here. * @return Error code. * @warning @p delta should have been previously initialized with * drpm_read(), otherwise behaviour is undefined. * @see DRPM_TAG_TGTSIZE * @see DRPM_TAG_TGTHEADERLEN * @see DRPM_TAG_PAYLOADFMTOFF */ DRPM_VISIBLE int drpm_get_ulong(drpm *delta, int tag, unsigned long *target); /** * @brief Fetches information representable as an unsigned long long integer. * Fetches information identified by @p tag from @p delta and copies it * to address pointed to by @p target. * * Example of usage: * @code * unsigned long long int_data_len; * * int error = drpm_get_ullong(delta, DRPM_TAG_INTDATALEN, &int_data_len); * * if (error != DRPM_ERR_OK) { * fprintf(stderr, "drpm error: %s\n", drpm_strerror(error)); * return; * } * * printf("Length of internal data: %llu\n", int_data_len); * @endcode * @param [in] delta Deltarpm containing required info. * @param [in] tag Identifies which info is required. * @param [out] target Tagged info will be copied here. * @return Error code. * @warning @p delta should have been previously initialized with * drpm_read(), otherwise behaviour is undefined. * @see DRPM_TAG_EXTDATALEN * @see DRPM_TAG_INTDATALEN */ DRPM_VISIBLE int drpm_get_ullong(drpm *delta, int tag, unsigned long long *target); /** * @brief Fetches information representable as a string. * Fetches string-type information identified by @p tag from @p delta, * copies it to space previously allocated by the function itself and * saves the address to @p *target. * * Example of usage: * @code * char *tgt_nevr; * * int error = drpm_get_string(delta, DRPM_TAG_TGTNEVR, &tgt_nevr); * * if (error != DRPM_ERR_OK) { * fprintf(stderr, "drpm error: %s\n", drpm_strerror(error)); * return; * } * * printf("Target NEVR: %s\n", tgt_nevr); * * free(tgt_nevr); * @endcode * @param [in] delta Deltarpm containing required info. * @param [in] tag Identifies which info is required. * @param [out] target Tagged info will be copied here. * @return Error code. * @note @p *target should be freed manually by the user when no longer needed. * @warning @p delta should have been previously initialized with * drpm_read(), otherwise behaviour is undefined. * @see DRPM_TAG_FILENAME * @see DRPM_TAG_SEQUENCE * @see DRPM_TAG_SRCNEVR * @see DRPM_TAG_TGTNEVR * @see DRPM_TAG_TGTMD5 * @see DRPM_TAG_TGTCOMPPARAM * @see DRPM_TAG_TGTLEAD */ DRPM_VISIBLE int drpm_get_string(drpm *delta, int tag, char **target); /** * @brief Fetches information representable as an array of unsigned long integers. * Fetches information identified by @p tag from @p delta, * copies it to space previously allocated by the function itself, * saves the address to @p *target and stores size in @p *size. * * Example of usage: * @code * unsigned long *ext_copies; * unsigned long ext_copies_size; * * int error = drpm_get_ulong_array(delta, DRPM_TAG_EXTCOPIES, &ext_copies, &ext_copies_size); * * if (error != DRPM_ERR_OK) { * fprintf(stderr, "drpm error: %s\n", drpm_strerror(error)); * return; * } * * for (unsigned long i = 1; i < ext_copies_size; i += 2) * printf("External copy: offset adjustment = %lu, length = %lu\n", ext_copies[i-1], ext_copies[i]); * * free(ext_copies); * @endcode * @param [in] delta Deltarpm containing required info. * @param [in] tag Identifies which info is required. * @param [out] target Tagged info will be copied here. * @param [out] size Size of array will be copied here. * @return Error code. * @note @p *target should be freed manually by the user when no longer needed. * @warning @p delta should have been previously initialized with * drpm_read(), otherwise behaviour is undefined. * @see DRPM_TAG_ADJELEMS * @see DRPM_TAG_INTCOPIES * @see DRPM_TAG_EXTCOPIES */ DRPM_VISIBLE int drpm_get_ulong_array(drpm *delta, int tag, unsigned long **target, unsigned long *size); /** * @brief Frees memory allocated by drpm_read(). * Frees memory pointed to by @p *delta and sets @p *delta to @c NULL. * * Example of usage: * @code * int error = drpm_destroy(&delta); * * if (error != DRPM_ERR_OK) { * fprintf(stderr, "drpm error: %s\n", drpm_strerror(error)); * return; * } * @endcode * @param [out] delta Deltarpm that is to be freed. * @return Error code. * @warning @p delta should have been previously initialized with * drpm_read(), otherwise behaviour is undefined. */ DRPM_VISIBLE int drpm_destroy(drpm **delta); /** @} */ /** * @brief Returns description of error code as a string. * Works very similarly to * [strerror(3)](http://linux.die.net/man/3/strerror). * @param [in] error error code * @return error description */ DRPM_VISIBLE const char *drpm_strerror(int error); #endif drpm-0.5.1/src/drpm_apply.c000066400000000000000000000441741421165313000155670ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2004,2005 Michael Schroeder (mls@suse.de) Copyright (C) 2016 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include #include #include #include #include #include #include #include #define BUFFER_SIZE 4096 struct checksum { unsigned short digest_algo; union { MD5_CTX md5; SHA256_CTX sha256; } ctx; }; static int check_filesize(const char *, unsigned short, const unsigned char *, size_t); static int check_full(const char *, unsigned short, const unsigned char *, size_t); static int check_prelink(const char *, unsigned short, const unsigned char *, size_t); static size_t checksum_digest_len(struct checksum); static int checksum_final(struct checksum *, unsigned char *); static int checksum_init(struct checksum *, unsigned short); static int checksum_update(struct checksum *, const void *, size_t); static uint16_t elf16(const unsigned char *, bool); static uint32_t elf32(const unsigned char *, bool); static uint64_t elf64(const unsigned char *, bool, bool); /* Expands the compressed sequence of the file order. * May perform checks on the individual files. * May create an index of CPIO entry lengths and offsets into * <*seqfiles_ret> and <*seqfile_len_ret>. */ int expand_sequence(struct cpio_file **seqfiles_ret, size_t *seqfiles_len_ret, const unsigned char *sequence, uint32_t sequence_len, const struct file_info *files, size_t file_count, unsigned short digest_algo, int check_mode) { int error = DRPM_ERR_OK; const bool want_seq = (seqfiles_ret != NULL && seqfiles_len_ret != NULL); struct cpio_file *seqfiles = NULL; size_t *positions; size_t positions_len = 0; MD5_CTX seq_md5; unsigned char seq_md5_digest[MD5_DIGEST_LENGTH]; unsigned char digest[MAX(MD5_DIGEST_LENGTH, SHA256_DIGEST_LENGTH)]; bool even = true; bool jump = false; bool toggle = true; unsigned shift = 0; size_t number = 0; size_t num = 0; size_t num_buf = 0; size_t pos = 0; uint32_t filesize; uint16_t rdev; char *filename; size_t header_len; size_t off = 0; int (*check)(const char *, unsigned short, const unsigned char *, size_t); if (sequence == NULL || sequence_len < MD5_DIGEST_LENGTH) return DRPM_ERR_PROG; switch (check_mode) { case DRPM_CHECK_NONE: check = NULL; break; case DRPM_CHECK_FULL: check = check_full; break; case DRPM_CHECK_FILESIZES: check = check_filesize; break; default: return DRPM_ERR_PROG; } if ((positions = malloc(file_count * sizeof(size_t))) == NULL) return DRPM_ERR_MEMORY; /* decompressing the file order */ for (uint32_t i = MD5_DIGEST_LENGTH; i < sequence_len; ) { if (even) { num_buf = sequence[i] >> 4; } else { num_buf = sequence[i] & 0x0F; i++; } even = !even; if ((num_buf & (1 << 3)) != 0) { num_buf ^= 1 << 3; if (shift) num_buf <<= shift; num |= num_buf; shift += 3; continue; } if (shift) num_buf <<= shift; number = num | num_buf; num = 0; shift = 0; if (jump) { pos = number; toggle = true; jump = false; continue; } if (number == 0) { jump = true; continue; } if (!toggle) { pos += number; toggle = true; continue; } while (number-- > 0) { if (positions_len > file_count || pos > file_count) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } positions[positions_len++] = pos++; } toggle = false; } if (shift) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } if (want_seq && (seqfiles = malloc((positions_len + 1) * sizeof(struct cpio_file))) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } if (MD5_Init(&seq_md5) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } /* constructing an MD5 to match against the DeltaRPM sequence * checking that files have not changed, * and constructing an index of CPIO entries. */ for (size_t i, pos = 0; pos < positions_len; pos++) { i = positions[pos]; if (S_ISREG(files[i].mode)) filesize = files[i].size; else if (S_ISLNK(files[i].mode)) filesize = strlen(files[i].linkto); else filesize = 0; if (S_ISBLK(files[i].mode) || S_ISCHR(files[i].mode)) rdev = files[i].rdev; else rdev = 0; filename = files[i].name; if (filename[0] == '/') filename++; if (MD5_Update(&seq_md5, filename, strlen(filename) + 1) != 1 || md5_update_be32(&seq_md5, files[i].mode) != 1 || md5_update_be32(&seq_md5, filesize) != 1 || md5_update_be32(&seq_md5, rdev) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } if (S_ISLNK(files[i].mode)) { if (MD5_Update(&seq_md5, files[i].linkto, strlen(files[i].linkto) + 1) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } } else if (S_ISREG(files[i].mode) && filesize > 0) { switch (digest_algo) { case DIGESTALGO_MD5: if (!parse_md5(digest, files[i].md5)) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } if (MD5_Update(&seq_md5, digest, MD5_DIGEST_LENGTH) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } break; case DIGESTALGO_SHA256: if (!parse_sha256(digest, files[i].md5)) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } if (MD5_Update(&seq_md5, digest, SHA256_DIGEST_LENGTH) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } break; } if (check != NULL && (error = check(files[i].name, digest_algo, digest, filesize)) != DRPM_ERR_OK) goto cleanup; } if (want_seq) { seqfiles[pos].index = i; header_len = CPIO_HEADER_SIZE + strlen(filename) + 3; // "./" prefix seqfiles[pos].header_len = header_len + CPIO_PADDING(header_len); seqfiles[pos].content_len = filesize + CPIO_PADDING(filesize); seqfiles[pos].offset = off; off += seqfiles[pos].header_len + seqfiles[pos].content_len; } } if (MD5_Final(seq_md5_digest, &seq_md5) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } if (memcmp(sequence, seq_md5_digest, MD5_DIGEST_LENGTH) != 0) { error = DRPM_ERR_MISMATCH; goto cleanup_fail; } if (want_seq) { seqfiles[positions_len].index = -1; header_len = CPIO_HEADER_SIZE + strlen(CPIO_TRAILER) + 1; seqfiles[positions_len].header_len = header_len + CPIO_PADDING(header_len); seqfiles[positions_len].content_len = 0; seqfiles[positions_len].offset = off; *seqfiles_ret = seqfiles; *seqfiles_len_ret = positions_len + 1; } goto cleanup; cleanup_fail: free(seqfiles); cleanup: free(positions); return error; } /******************************* check ********************************/ int check_filesize(const char *filename, unsigned short digest_algo, const unsigned char *digest, size_t filesize) { int error; int filedesc; unsigned char buf[128]; ssize_t read_len; struct stat stats; bool prelink; if (stat(filename, &stats) != 0) return DRPM_ERR_NOINSTALL; if (stats.st_size == (off_t)filesize) return DRPM_ERR_OK; if (stats.st_size > (off_t)filesize) { if ((filedesc = open(filename, O_RDONLY)) < 0) return DRPM_ERR_IO; if ((read_len = read(filedesc, buf, 128)) > 0) { if ((error = is_prelinked(&prelink, filedesc, buf, read_len)) != DRPM_ERR_OK) return error; if (prelink) { close(filedesc); return check_prelink(filename, digest_algo, digest, filesize); } } if (read_len < 0) { close(filedesc); return DRPM_ERR_IO; } close(filedesc); } return DRPM_ERR_MISMATCH; } int check_full(const char *filename, unsigned short digest_algo, const unsigned char *digest, size_t filesize) { int error = DRPM_ERR_OK; int filedesc; unsigned char buf[BUFFER_SIZE]; struct checksum chsm; unsigned char chsm_digest[MAX(MD5_DIGEST_LENGTH, SHA256_DIGEST_LENGTH)]; ssize_t read_len; struct stat stats; bool prelink; if ((filedesc = open(filename, O_RDONLY)) < 0) return DRPM_ERR_IO; if (fstat(filedesc, &stats) != 0) { error = DRPM_ERR_NOINSTALL; goto cleanup; } if ((error = checksum_init(&chsm, digest_algo)) != DRPM_ERR_OK) goto cleanup; if (stats.st_size > (off_t)filesize) { if ((read_len = read(filedesc, buf, BUFFER_SIZE)) > 0) { if ((error = is_prelinked(&prelink, filedesc, buf, read_len)) != DRPM_ERR_OK) return error; if (prelink) { close(filedesc); return check_prelink(filename, digest_algo, digest, filesize); } if (read_len > (ssize_t)filesize) read_len = filesize; if ((error = checksum_update(&chsm, buf, read_len)) != DRPM_ERR_OK) goto cleanup; filesize -= read_len; } } while (filesize > 0 && (read_len = read(filedesc, buf, BUFFER_SIZE)) > 0) { if ((size_t)read_len > filesize) read_len = filesize; if ((error = checksum_update(&chsm, buf, read_len)) != DRPM_ERR_OK) goto cleanup; filesize -= read_len; } if ((error = checksum_final(&chsm, chsm_digest)) != DRPM_ERR_OK) goto cleanup; if (memcmp(chsm_digest, digest, checksum_digest_len(chsm)) != 0) { error = DRPM_ERR_MISMATCH; goto cleanup; } cleanup: close(filedesc); return error; } int check_prelink(const char *filename, unsigned short digest_algo, const unsigned char *digest, size_t filesize) { int error = DRPM_ERR_OK; int filedesc; unsigned char buf[BUFFER_SIZE]; struct checksum chsm; unsigned char chsm_digest[MAX(MD5_DIGEST_LENGTH, SHA256_DIGEST_LENGTH)]; ssize_t read_len; if ((error = prelink_open(filename, &filedesc)) != DRPM_ERR_OK) return error; if ((error = checksum_init(&chsm, digest_algo)) != DRPM_ERR_OK) goto cleanup; while (filesize > 0 && (read_len = read(filedesc, buf, BUFFER_SIZE)) > 0) { if ((size_t)read_len > filesize) read_len = filesize; if ((error = checksum_update(&chsm, buf, read_len)) != DRPM_ERR_OK) goto cleanup; filesize -= read_len; } if (read_len < 0) { error = DRPM_ERR_IO; goto cleanup; } if ((error = checksum_final(&chsm, chsm_digest)) != DRPM_ERR_OK) goto cleanup; if (memcmp(chsm_digest, digest, checksum_digest_len(chsm)) != 0) { error = DRPM_ERR_MISMATCH; goto cleanup; } cleanup: close(filedesc); return error; } /***************************** MD5/SHA256 *****************************/ int checksum_init(struct checksum *chsm, unsigned short digest_algo) { if (chsm == NULL) return DRPM_ERR_PROG; switch (digest_algo) { case DIGESTALGO_MD5: if (MD5_Init(&chsm->ctx.md5) != 1) return DRPM_ERR_OTHER; break; case DIGESTALGO_SHA256: if (SHA256_Init(&chsm->ctx.sha256) != 1) return DRPM_ERR_OTHER; break; default: return DRPM_ERR_PROG; } chsm->digest_algo = digest_algo; return DRPM_ERR_OK; } int checksum_update(struct checksum *chsm, const void *buf, size_t len) { if (chsm == NULL || buf == NULL) return DRPM_ERR_PROG; switch (chsm->digest_algo) { case DIGESTALGO_MD5: return MD5_Update(&chsm->ctx.md5, buf, len) != 1 ? DRPM_ERR_OTHER : DRPM_ERR_OK; case DIGESTALGO_SHA256: return SHA256_Update(&chsm->ctx.sha256, buf, len) != 1 ? DRPM_ERR_OTHER : DRPM_ERR_OK; default: return DRPM_ERR_PROG; } } int checksum_final(struct checksum *chsm, unsigned char *digest) { if (chsm == NULL || digest == NULL) return DRPM_ERR_PROG; switch (chsm->digest_algo) { case DIGESTALGO_MD5: return MD5_Final(digest, &chsm->ctx.md5) != 1 ? DRPM_ERR_OTHER : DRPM_ERR_OK; case DIGESTALGO_SHA256: return SHA256_Final(digest, &chsm->ctx.sha256) != 1 ? DRPM_ERR_OTHER : DRPM_ERR_OK; default: return DRPM_ERR_PROG; } } size_t checksum_digest_len(struct checksum chsm) { return chsm.digest_algo == DIGESTALGO_MD5 ? MD5_DIGEST_LENGTH : SHA256_DIGEST_LENGTH; } /****************************** prelink *******************************/ uint16_t elf16(const unsigned char *buf, bool little_endian) { if (little_endian) return buf[0] | buf[1] << 8; return parse_be16(buf); } uint32_t elf32(const unsigned char *buf, bool little_endian) { if (little_endian) return buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; return parse_be32(buf); } uint64_t elf64(const unsigned char *buf, bool little_endian, bool is64) { if (is64) { buf += little_endian ? 4 : 0; if (buf[0] > 0 || buf[1] > 0 || buf[2] > 0 || buf[3] > 0) return UINT64_MAX; buf += little_endian ? -4 : 4; } if (little_endian) return buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; return parse_be32(buf); } int is_prelinked(bool *is_prelinked_ret, int fd, const unsigned char *buf, ssize_t read_len) { int error = DRPM_ERR_OK; bool is_prelinked = true; size_t len = read_len; bool le; bool is64; off_t soff; int snum; int ssiz; int i; int stridx; unsigned char *sects = NULL; unsigned char *strsect = NULL; unsigned slen; unsigned o; if (is_prelinked_ret == NULL || buf == NULL) return DRPM_ERR_PROG; if (read_len < 0) return DRPM_ERR_IO; if (len < 0x34 || buf[0] != 0x7F || buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F') { is_prelinked = false; goto cleanup; } is64 = (buf[4] == 2); le = (buf[5] != 2); if (is64 && len < 0x40) { is_prelinked = false; goto cleanup; } soff = elf64(is64 ? buf + 40 : buf + 32, le, is64); if (soff == (off_t)~0) { is_prelinked = false; goto cleanup; } ssiz = elf16(buf + (is64 ? 0x40 - 6 : 0x34 - 6), le); if (ssiz < (is64 ? 64 : 40) || ssiz >= 32768) { is_prelinked = false; goto cleanup; } snum = elf16(buf + (is64 ? 0x40 - 4 : 0x34 - 4), le); stridx = elf16(buf + (is64 ? 0x40 - 2 : 0x34 - 2), le); if (stridx >= snum) { is_prelinked = false; goto cleanup; } if ((sects = malloc(snum * ssiz)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if (pread(fd, sects, snum * ssiz, soff) != snum * ssiz) { is_prelinked = false; goto cleanup; } strsect = sects + stridx * ssiz; if (elf32(strsect + 4, le) != 3) { is_prelinked = false; goto cleanup; } soff = elf64(is64 ? strsect + 24 : strsect + 16, le, is64); slen = elf64(is64 ? strsect + 32 : strsect + 20, le, is64); if (soff == (off_t)~0 || slen == (unsigned)~0 || (int)slen < 0) { is_prelinked = false; goto cleanup; } if ((strsect = malloc(slen)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if (pread(fd, strsect, slen, soff) != (ssize_t)slen) { is_prelinked = false; goto cleanup; } for (i = 0; i < snum; i++) { o = elf32(sects + i * ssiz, le); if (o > slen) continue; if (o + 18 <= slen && memcmp(strsect + o, ".gnu.prelink_undo", 18) == 0) break; } is_prelinked = (i != snum); cleanup: free(strsect); free(sects); *is_prelinked_ret = is_prelinked; return error; } int prelink_open(const char *filename, int *filedesc) { pid_t pid; int fd; int status; struct stat stats; char template[] = "/tmp/drpm.XXXXXX"; if (filename == NULL || filedesc == NULL) return DRPM_ERR_PROG; if (stat("/usr/sbin/prelink", &stats) != 0) return DRPM_ERR_OTHER; if ((fd = mkstemp(template)) < 0) return DRPM_ERR_IO; close(fd); pid = fork(); if (pid == (pid_t)-1) { return DRPM_ERR_OTHER; } if (pid == 0) { execl("/usr/sbin/prelink", "prelink", "-o", template, "-u", filename, NULL); _exit(1); } while (waitpid(pid, &status, 0) == (pid_t)-1); if ((fd = open(template, O_RDONLY)) < 0) return DRPM_ERR_IO; unlink(template); *filedesc = fd; return DRPM_ERR_OK; } drpm-0.5.1/src/drpm_block.c000066400000000000000000001125121421165313000155240ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2004,2005 Michael Schroeder (mls@suse.de) Copyright (C) 2016 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include #include #include #include #include #include #include #include #define MAX_OPEN_FILES 50 #define MAX_CORE_BLOCKS 5000 #define BLOCK_SIZE (1 << 13) #define BLK_FREE 0 #define BLK_CORE 1 #define BLK_CORE_NOPAGE 2 #define BLK_PAGE 3 #define BLOCKS(size) (1 + ((size) - 1) / BLOCK_SIZE) /* a list of open files */ struct open_file { struct open_file *prev; struct open_file *next; int filedesc; const char *name; off_t offset; }; /* a block */ struct block { struct block *next; int type; unsigned id; /* core blocks store the buffer directly, while page blocks * only store an offset within a temporary file from which to read * the data */ union { off_t offset; unsigned char *buffer; } data; }; struct blocks { struct block *free_core_blocks; struct block *core_blocks; size_t core_blocks_count; struct block *page_blocks; size_t page_blocks_count; int page_filedesc; struct block **blocks_table; size_t *blocks_max; unsigned char *cpio_buffer; const char *linkto; ssize_t cpio_files_index; const struct cpio_file *cpio_files; size_t cpio_files_len; const struct file_info *files; bool from_rpm; union { struct { struct open_file *files_head; struct open_file *files_tail; unsigned short file_count; struct open_file **open_files; } from_filesytem; struct { struct rpm *old_rpm; unsigned rpm_id; uint64_t left; unsigned char *old_header; size_t old_header_size; size_t old_header_offset; } from_rpm; } rpm_files; struct block *last_block; int (*fill_block)(struct blocks *, struct block *, size_t, size_t); }; static int fillblock_filesystem(struct blocks *, struct block *, size_t, size_t); static int fillblock_prelink(struct blocks *, struct block *, size_t, size_t, const struct cpio_file *); static int fillblock_rpm_rpmonly(struct blocks *, struct block *, size_t, size_t); static int fillblock_rpm_standard(struct blocks *, struct block *, size_t, size_t); static struct block *get_free_core_block(struct blocks *); static int get_block(struct blocks *, struct block **, size_t, size_t); static int new_core_block(struct blocks *, struct block **); static int push_block(struct blocks *, const struct block *, size_t); static int read_page_block(struct blocks *, struct block *, const struct block *); static int write_page_block(struct blocks *, const struct block *, size_t); /* returns size of block */ size_t block_size() { return BLOCK_SIZE; } /* determines block ID from offset */ size_t block_id(uint64_t offset) { return offset / BLOCK_SIZE; } /* creates blocks for reading external data */ int blocks_create(struct blocks **blks_ret, uint64_t ext_data_len, const struct file_info *files, const struct cpio_file *cpio_files, size_t cpio_files_len, const uint32_t *ext_copies, size_t ext_copies_count, struct rpm *old_rpm, bool rpm_only) { int error = DRPM_ERR_OK; const size_t block_count = BLOCKS(ext_data_len); uint64_t off = 0; size_t max_cpio_header_len; uint32_t old_header_size; struct blocks blks = { .page_filedesc = -1, .cpio_files_index = -1, .cpio_files = cpio_files, .cpio_files_len = cpio_files_len, .files = files, .from_rpm = (old_rpm != NULL) }; if (blks_ret == NULL) return DRPM_ERR_PROG; if (block_count >= UINT32_MAX) return DRPM_ERR_OVERFLOW; if (blks.from_rpm) { blks.rpm_files.from_rpm.old_rpm = old_rpm; blks.rpm_files.from_rpm.rpm_id = 0; if (rpm_only) { blks.rpm_files.from_rpm.left = ext_data_len; if ((error = rpm_fetch_header(old_rpm, &blks.rpm_files.from_rpm.old_header, &old_header_size)) != DRPM_ERR_OK) goto cleanup; blks.rpm_files.from_rpm.old_header_size = old_header_size; blks.rpm_files.from_rpm.old_header_offset = 0; blks.fill_block = fillblock_rpm_rpmonly; } else { blks.rpm_files.from_rpm.left = 0; blks.rpm_files.from_rpm.old_header = NULL; blks.rpm_files.from_rpm.old_header_size = 0; blks.rpm_files.from_rpm.old_header_offset = 0; blks.fill_block = fillblock_rpm_standard; } } else { if ((blks.rpm_files.from_filesytem.open_files = calloc(cpio_files_len, sizeof(struct open_file *))) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } blks.rpm_files.from_filesytem.files_head = NULL; blks.rpm_files.from_filesytem.files_tail = NULL; blks.rpm_files.from_filesytem.file_count = 0; blks.fill_block = fillblock_filesystem; } if ((*blks_ret = malloc(sizeof(struct blocks))) == NULL || (blks.blocks_table = calloc(block_count, sizeof(struct block *))) == NULL || (blks.blocks_max = calloc(block_count, sizeof(size_t))) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } for (size_t blk_i, blk_l, i = 0; i < ext_copies_count; i++) { off += (int32_t)ext_copies[2 * i]; blk_i = off / BLOCK_SIZE; off += ext_copies[2 * i + 1]; blk_l = BLOCKS(off); for ( ; blk_i < blk_l; blk_i++) blks.blocks_max[blk_i] = i; } max_cpio_header_len = CPIO_HEADER_SIZE + strlen(CPIO_TRAILER) + 1; max_cpio_header_len += CPIO_PADDING(max_cpio_header_len); for (size_t i = 0; i < cpio_files_len; i++) if (cpio_files[i].header_len > max_cpio_header_len) max_cpio_header_len = cpio_files[i].header_len; if ((blks.cpio_buffer = malloc(max_cpio_header_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } **blks_ret = blks; return DRPM_ERR_OK; cleanup: free(*blks_ret); free(blks.blocks_table); free(blks.blocks_max); free(blks.cpio_buffer); return error; } /* frees block data */ int blocks_destroy(struct blocks **blks_ref) { struct blocks *blks; struct block *blk_lists[3]; if (blks_ref == NULL || *blks_ref == NULL) return DRPM_ERR_PROG; blks = *blks_ref; if (blks->from_rpm) { free(blks->rpm_files.from_rpm.old_header); } else { for (struct open_file *tmp, *file = blks->rpm_files.from_filesytem.files_head; file != NULL; ) { close(file->filedesc); tmp = file; file = file->next; free(tmp); } free(blks->rpm_files.from_filesytem.open_files); } blk_lists[0] = blks->core_blocks; blk_lists[1] = blks->free_core_blocks; blk_lists[2] = blks->page_blocks; for (unsigned short i = 0; i < 3; i++) { for (struct block *blk = blk_lists[i], *tmp; blk != NULL; ) { if (blk->type != BLK_PAGE) free(blk->data.buffer); tmp = blk; blk = blk->next; free(tmp); } } if (!(blks->page_filedesc < 0)) close(blks->page_filedesc); free(blks->blocks_table); free(blks->blocks_max); free(blks->cpio_buffer); free(*blks_ref); *blks_ref = NULL; return DRPM_ERR_OK; } /* fetches external data */ int blocks_next(struct blocks *blks, unsigned char buffer[BLOCK_SIZE], size_t *buffer_len, uint64_t offset, size_t copy_len, size_t copy_cnt, size_t id) { int error; size_t blk_off; if (blks == NULL || buffer == NULL || buffer_len == NULL) return DRPM_ERR_PROG; if (blks->last_block == NULL || id != blks->last_block->id) { blks->last_block = blks->blocks_table[id]; if ((blks->last_block == NULL || blks->last_block->type == BLK_PAGE) && (error = get_block(blks, &blks->last_block, id, copy_cnt)) != DRPM_ERR_OK) return error; } blk_off = offset % BLOCK_SIZE; *buffer_len = (blk_off + copy_len > BLOCK_SIZE) ? BLOCK_SIZE - blk_off : copy_len; memcpy(buffer, blks->last_block->data.buffer + blk_off, *buffer_len); return DRPM_ERR_OK; } /* gets new block and fills it */ int get_block(struct blocks *blks, struct block **blk_ret, size_t id, size_t copy_cnt) { static size_t cleanup_count = 0; int error; struct block *blk; struct block *page_blk; if (blks == NULL || blk_ret == NULL) return DRPM_ERR_PROG; *blk_ret = NULL; blk = blks->blocks_table[id]; if (blk != NULL && (blk->type == BLK_CORE || blk->type == BLK_CORE_NOPAGE)) { *blk_ret = blk; return DRPM_ERR_OK; } if ((blk = get_free_core_block(blks)) == NULL) { if (blks->core_blocks_count < MAX_CORE_BLOCKS && (++cleanup_count % 8) != 0) { if ((error = new_core_block(blks, &blk)) != DRPM_ERR_OK) return error; } else { for (struct block **blk_ptr = &blks->core_blocks; (blk = *blk_ptr) != NULL; blk_ptr = &blk->next) { if (blks->blocks_max[blk->id] < copy_cnt || (blk->id < id && blks->blocks_max[blk->id] == copy_cnt)) { *blk_ptr = blk->next; blks->blocks_table[blk->id] = NULL; blk->type = BLK_FREE; blk->next = blks->free_core_blocks; blks->free_core_blocks = blk; } else { blk_ptr = &blk->next; } } if ((blk = get_free_core_block(blks)) == NULL) { if (blks->core_blocks_count < MAX_CORE_BLOCKS) { if ((error = new_core_block(blks, &blk)) != DRPM_ERR_OK) return error; } else { for (struct block **blk_ptr = &blks->core_blocks; (blk = *blk_ptr) != NULL; blk_ptr = &blk->next) { if (blk->next == NULL) { *blk_ptr = NULL; break; } } blk->next = blks->core_blocks; blks->core_blocks = blk; if (blk->type == BLK_CORE) { if ((error = write_page_block(blks, blk, copy_cnt)) != DRPM_ERR_OK) return error; } else { blks->blocks_table[blk->id] = NULL; } blk->type = BLK_FREE; } } } } page_blk = blks->blocks_table[id]; *blk_ret = blks->blocks_table[id] = blk; if (page_blk != NULL && page_blk->type == BLK_PAGE) return read_page_block(blks, blk, page_blk); /* filling block */ if ((error = blks->fill_block(blks, blk, id, copy_cnt)) != DRPM_ERR_OK) return error; return DRPM_ERR_OK; } /* allocates a new block */ int new_core_block(struct blocks *blks, struct block **new_ret) { struct block *new; if (blks == NULL || new_ret == NULL) return DRPM_ERR_PROG; if ((new = malloc(sizeof(struct block))) == NULL || (new->data.buffer = malloc(BLOCK_SIZE)) == NULL) { free(new); return DRPM_ERR_MEMORY; } new->type = BLK_FREE; new->next = blks->core_blocks; blks->core_blocks = new; blks->core_blocks_count++; *new_ret = new; return DRPM_ERR_OK; } /* pops a free core block and pushes it to core blocks for reuse */ struct block *get_free_core_block(struct blocks *blks) { struct block *blk; if (blks->free_core_blocks == NULL) return NULL; blk = blks->free_core_blocks; blks->free_core_blocks = blks->free_core_blocks->next; blk->next = blks->core_blocks; blks->core_blocks = blk; return blk; } /* inserts a block in table */ int push_block(struct blocks *blks, const struct block *blk, size_t copy_cnt) { int error; struct block *new; if (blks == NULL || blk == NULL) return DRPM_ERR_PROG; if ((new = get_free_core_block(blks)) == NULL) { if (blks->core_blocks_count < MAX_CORE_BLOCKS) { if ((error = new_core_block(blks, &new)) != DRPM_ERR_OK) return error; } else if (blk->type == BLK_CORE) { return write_page_block(blks, blk, copy_cnt); } else { blks->blocks_table[blk->id] = NULL; return DRPM_ERR_OK; } } new->id = blk->id; new->type = blk->type; memcpy(new->data.buffer, blk->data.buffer, BLOCK_SIZE); blks->blocks_table[new->id] = new; return DRPM_ERR_OK; } /* insert a page block in table and writes its data to temporary file */ int write_page_block(struct blocks *blks, const struct block *blk, size_t copy_cnt) { struct block *new; char template[] = "/tmp/drpmpageXXXXXX"; if (blks == NULL || blk == NULL || blk->type == BLK_PAGE) return DRPM_ERR_PROG; for (new = blks->page_blocks; new != NULL; new = new->next) { if (new->id == blk->id) { blks->blocks_table[new->id] = new; return DRPM_ERR_OK; } } for (new = blks->page_blocks; new != NULL; new = new->next) if (blks->blocks_max[new->id] < copy_cnt) break; if (new == NULL) { if ((new = malloc(sizeof(struct block))) == NULL) return DRPM_ERR_MEMORY; new->type = BLK_PAGE; new->data.offset = blks->page_blocks_count; new->next = blks->page_blocks; blks->page_blocks = new; blks->page_blocks_count++; if (blks->page_filedesc < 0) { if ((blks->page_filedesc = mkstemp(template)) < 0) { free(new); return DRPM_ERR_IO; } unlink(template); } } new->id = blk->id; if (pwrite(blks->page_filedesc, blk->data.buffer, BLOCK_SIZE, new->data.offset * BLOCK_SIZE) != BLOCK_SIZE) { free(new); return DRPM_ERR_IO; } blks->blocks_table[new->id] = new; return DRPM_ERR_OK; } /* reads page block data from temporary file into destination block */ int read_page_block(struct blocks *blks, struct block *dst, const struct block *src) { if (blks == NULL || dst == NULL || src == NULL || blks->page_filedesc < 0 || dst->type == BLK_PAGE || src->type != BLK_PAGE) return DRPM_ERR_PROG; if (pread(blks->page_filedesc, dst->data.buffer, BLOCK_SIZE, src->data.offset * BLOCK_SIZE) != BLOCK_SIZE) return DRPM_ERR_IO; dst->id = src->id; dst->type = BLK_CORE; blks->blocks_table[dst->id] = dst; return DRPM_ERR_OK; } /* fills CPIO header and linkto buffers based on file info at */ void fill_cpio_header(struct blocks *blks, ssize_t index) { struct cpio_header header = {0}; struct file_info file; char *name; if (index < 0) { header.nlink = 1; header.namesize = strlen(CPIO_TRAILER) + 1; cpio_header_write(&header, (char *)blks->cpio_buffer); strcpy((char *)blks->cpio_buffer + CPIO_HEADER_SIZE, CPIO_TRAILER); memcpy(blks->cpio_buffer + CPIO_HEADER_SIZE + header.namesize, "\0\0\0", CPIO_PADDING(CPIO_HEADER_SIZE + header.namesize)); return; } file = blks->files[index]; name = file.name; if (name[0] == '/') name++; if (S_ISREG(file.mode)) { header.filesize = file.size; } else if (S_ISLNK(file.mode)) { header.filesize = strlen(file.linkto); blks->linkto = file.linkto; } if (S_ISBLK(file.mode) || S_ISCHR(file.mode)) { header.rdevmajor = major(file.rdev); header.rdevminor = minor(file.rdev); } header.nlink = 1; header.mode = file.mode; header.namesize = strlen(name) + 3; // "./" prefix cpio_header_write(&header, (char *)blks->cpio_buffer); strcpy((char *)blks->cpio_buffer + CPIO_HEADER_SIZE, "./"); strcpy((char *)blks->cpio_buffer + CPIO_HEADER_SIZE + 2, name); memcpy(blks->cpio_buffer + CPIO_HEADER_SIZE + header.namesize, "\0\0\0", CPIO_PADDING(CPIO_HEADER_SIZE + header.namesize)); } /* opens new file and appends it to list, sets indicator */ int open_new_file(struct blocks *blks, bool *prelinked, size_t index) { int error; struct open_file *new = NULL; struct stat stats; int filedesc; struct file_info file; unsigned char plnk_buf[128]; struct open_file *files_head; struct open_file *files_tail; if (blks == NULL || prelinked == NULL) return DRPM_ERR_PROG; file = blks->files[blks->cpio_files[index].index]; files_head = blks->rpm_files.from_filesytem.files_head; files_tail = blks->rpm_files.from_filesytem.files_tail; *prelinked = false; if ((filedesc = open(file.name, O_RDONLY)) < 0) return DRPM_ERR_IO; if (fstat(filedesc, &stats) == 0 && stats.st_size != file.size) { if ((error = is_prelinked(prelinked, filedesc, plnk_buf, pread(filedesc, plnk_buf, 128, SEEK_SET))) != DRPM_ERR_OK) { close(filedesc); return error; } if (*prelinked) { close(filedesc); return DRPM_ERR_OK; } } if (blks->rpm_files.from_filesytem.file_count < MAX_OPEN_FILES) { if ((new = malloc(sizeof(struct open_file))) == NULL) { close(filedesc); return DRPM_ERR_MEMORY; } blks->rpm_files.from_filesytem.file_count++; } else { new = files_head; files_head = files_head->next; if (files_head == NULL) files_tail = NULL; else files_head->prev = NULL; close(new->filedesc); } new->filedesc = filedesc; new->name = file.name; new->offset = 0; new->prev = NULL; new->next = NULL; if (files_head == NULL) { files_head = files_tail = new; } else { files_tail->next = new; new->prev = files_tail; files_tail = new; } blks->rpm_files.from_filesytem.files_head = files_head; blks->rpm_files.from_filesytem.files_tail = files_tail; blks->rpm_files.from_filesytem.open_files[index] = new; return DRPM_ERR_OK; } /* gets open file and moves it to end of list */ struct open_file *get_open_file(struct blocks *blks, size_t index) { struct open_file *file = blks->rpm_files.from_filesytem.open_files[index]; struct open_file *files_head = blks->rpm_files.from_filesytem.files_head; struct open_file *files_tail = blks->rpm_files.from_filesytem.files_tail; if (file == NULL) return NULL; if (file->next == NULL) return file; file->next->prev = file->prev; if (file->prev == NULL) files_head = file->next; else file->prev->next = file->next; file->next = NULL; file->prev = files_tail; files_tail->next = file; files_tail = file; blks->rpm_files.from_filesytem.files_head = files_head; blks->rpm_files.from_filesytem.files_tail = files_tail; return file; } /***************************** fill block *****************************/ /* Fills a block from old RPM in the case of a standard delta. * Uses stored file order to quickly locate files. * CPIO entries are altered in the same way as when creating the delta. */ int fillblock_rpm_standard(struct blocks *blks, struct block *blk, size_t id, size_t copy_cnt) { int error = DRPM_ERR_OK; const struct cpio_file *cpio; size_t len = BLOCK_SIZE; size_t off; size_t read_len; unsigned char *buf_ptr; ssize_t i; struct cpio_header cpio_hdr; char cpio_buf[CPIO_HEADER_SIZE] = {0}; size_t c_namesize; size_t c_filesize; char *name; char *name_buffer = NULL; char *name_buffer_tmp; size_t name_buffer_len = 0; struct rpm *old_rpm; uint64_t left; size_t rpm_id; if (blks == NULL || blk == NULL) return DRPM_ERR_PROG; old_rpm = blks->rpm_files.from_rpm.old_rpm; left = blks->rpm_files.from_rpm.left; rpm_id = blks->rpm_files.from_rpm.rpm_id; buf_ptr = blk->data.buffer; while (true) { if (left > 0) { cpio = blks->cpio_files + blks->cpio_files_index; if (left > cpio->content_len) { off = cpio->header_len + cpio->content_len - left; read_len = left - cpio->content_len; if (read_len > len) read_len = len; memcpy(buf_ptr, blks->cpio_buffer + off, read_len); buf_ptr += read_len; left -= read_len; len -= read_len; } if (len > 0 && left > 0) { read_len = MIN(len, left); if (S_ISLNK(blks->files[cpio->index].mode)) { strncpy((char *)buf_ptr, blks->linkto, read_len); blks->linkto += MIN(strlen(blks->linkto), read_len); } else if ((error = rpm_archive_read_chunk(old_rpm, buf_ptr, read_len)) != DRPM_ERR_OK) { break; } buf_ptr += read_len; left -= read_len; len -= read_len; } } if (len > 0 && blks->cpio_files_index >= 0 && blks->cpio_files[blks->cpio_files_index].index < 0) { memset(buf_ptr, 0, len); len = 0; } if (len == 0) { blk->type = BLK_CORE; blk->id = rpm_id++; if (blk->id == id) { error = DRPM_ERR_OK; break; } if (blk->id > id) { error = DRPM_ERR_PROG; break; } if (blks->blocks_max[blk->id] > copy_cnt && (error = push_block(blks, blk, copy_cnt)) != DRPM_ERR_OK) break; len = BLOCK_SIZE; buf_ptr = blk->data.buffer; continue; } blks->cpio_files_index++; cpio = blks->cpio_files + blks->cpio_files_index; i = cpio->index; if (i < 0) { fill_cpio_header(blks, i); left = cpio->header_len + cpio->content_len; continue; } while (true) { if ((error = rpm_archive_read_chunk(old_rpm, cpio_buf, CPIO_HEADER_SIZE)) != DRPM_ERR_OK || (error = cpio_header_read(&cpio_hdr, cpio_buf)) != DRPM_ERR_OK) break; c_namesize = cpio_hdr.namesize; c_filesize = cpio_hdr.filesize; c_filesize += CPIO_PADDING(c_filesize); if (c_namesize > name_buffer_len) { if ((name_buffer_tmp = realloc(name_buffer, c_namesize)) == NULL) { error = DRPM_ERR_MEMORY; break; } name_buffer = name_buffer_tmp; name_buffer_len = c_namesize; } if ((error = rpm_archive_read_chunk(old_rpm, name_buffer, c_namesize)) != DRPM_ERR_OK) break; name = name_buffer; name[c_namesize - 1] = '\0'; if (strcmp(name, CPIO_TRAILER) == 0) { error = DRPM_ERR_FORMAT; break; } if (strncmp(name, "./", 2) == 0) name += 2; if ((error = rpm_archive_read_chunk(old_rpm, NULL, CPIO_PADDING(CPIO_HEADER_SIZE + c_namesize))) != DRPM_ERR_OK) break; if (strcmp(name, blks->files[i].name + ((blks->files[i].name[0] == '/') ? 1 : 0)) == 0) { error = DRPM_ERR_OK; break; } if ((error = rpm_archive_read_chunk(old_rpm, NULL, c_filesize)) != DRPM_ERR_OK) break; } if (error != DRPM_ERR_OK) break; fill_cpio_header(blks, cpio->index); if (!S_ISREG(blks->files[cpio->index].mode)) { if ((error = rpm_archive_read_chunk(old_rpm, NULL, c_filesize)) != DRPM_ERR_OK) break; } else if (c_filesize != cpio->content_len) { error = DRPM_ERR_MISMATCH; break; } left = cpio->header_len + cpio->content_len; } blks->rpm_files.from_rpm.left = left; blks->rpm_files.from_rpm.rpm_id = rpm_id; free(name_buffer); return error; } /* Fills a block from old RPM in the case of an rpm-only delta. * CPIO data is not altered, but old header is prepended. */ int fillblock_rpm_rpmonly(struct blocks *blks, struct block *blk, size_t id, size_t copy_cnt) { int error; size_t read_len; size_t header_read_len; unsigned char *header; size_t header_size; size_t header_offset; uint64_t left; struct rpm *old_rpm; size_t rpm_id; if (blks == NULL || blk == NULL) return DRPM_ERR_PROG; header = blks->rpm_files.from_rpm.old_header; header_size = blks->rpm_files.from_rpm.old_header_size; header_offset = blks->rpm_files.from_rpm.old_header_offset; left = blks->rpm_files.from_rpm.left; old_rpm = blks->rpm_files.from_rpm.old_rpm; rpm_id = blks->rpm_files.from_rpm.rpm_id; while (true) { read_len = MIN(left, BLOCK_SIZE); if (header_offset < header_size) { if (header_offset + read_len > header_size) { header_read_len = header_size - header_offset; if ((error = rpm_archive_read_chunk(old_rpm, blk->data.buffer + header_read_len, read_len - header_read_len)) != DRPM_ERR_OK) break; } else { header_read_len = read_len; } memcpy(blk->data.buffer, header + header_offset, header_read_len); header_offset += header_read_len; } else { if ((error = rpm_archive_read_chunk(old_rpm, blk->data.buffer, read_len)) != DRPM_ERR_OK) break; } left -= read_len; if (read_len < BLOCK_SIZE) memset(blk->data.buffer + read_len, 0, BLOCK_SIZE - read_len); blk->type = BLK_CORE; blk->id = rpm_id++; if (blk->id == id) { error = DRPM_ERR_OK; break; } if (blk->id > id) { error = DRPM_ERR_PROG; break; } if (blks->blocks_max[blk->id] > copy_cnt && (error = push_block(blks, blk, copy_cnt)) != DRPM_ERR_OK) break; } blks->rpm_files.from_rpm.old_header_offset = header_offset; blks->rpm_files.from_rpm.left = left; blks->rpm_files.from_rpm.rpm_id = rpm_id; return error; } /* Fills block from filesystem data (only works for standard deltas). * CPIO entries are created from installed files to match the pattern used * in altering the old RPM's archive when creating the delta. */ int fillblock_filesystem(struct blocks *blks, struct block *blk, size_t id, size_t copy_cnt) { int error = DRPM_ERR_OK; const struct cpio_file *cpio; unsigned char *buf_ptr; size_t len; uint64_t off; size_t i; size_t file_off; size_t read_len; struct open_file *file; bool prelinked; if (blks == NULL || blk == NULL) return DRPM_ERR_PROG; buf_ptr = blk->data.buffer; len = BLOCK_SIZE; off = id * BLOCK_SIZE; i = blks->cpio_files_index >= 0 ? blks->cpio_files_index : 0; for (cpio = blks->cpio_files + i; i > 0 && cpio->offset > off; i--, cpio--); for ( ; i < blks->cpio_files_len; i++, cpio++) if (cpio->offset <= off && cpio->offset + cpio->header_len + cpio->content_len > off) break; if (i == blks->cpio_files_len) return DRPM_ERR_PROG; if ((ssize_t)i != blks->cpio_files_index) { fill_cpio_header(blks, cpio->index); blks->cpio_files_index = i; } while (len > 0) { if (off < cpio->offset + cpio->header_len) { file_off = off - cpio->offset; read_len = MIN(len, cpio->header_len - file_off); memcpy(buf_ptr, blks->cpio_buffer + file_off, read_len); buf_ptr += read_len; off += read_len; len -= read_len; continue; } if (cpio->index < 0) { memset(buf_ptr, 0, len); len = 0; error = DRPM_ERR_OK; break; } if (off < cpio->offset + cpio->header_len + cpio->content_len) { file_off = off - (cpio->offset + cpio->header_len); if (S_ISLNK(blks->files[cpio->index].mode)) { read_len = MIN(len, cpio->content_len - file_off); if (file_off > strlen(blks->linkto)) memset(buf_ptr, 0, read_len); else strncpy((char *)buf_ptr, blks->linkto + file_off, read_len); } else if (file_off < blks->files[cpio->index].size) { read_len = MIN(len, blks->files[cpio->index].size - file_off); file = get_open_file(blks, cpio->index); if (file == NULL) { if ((error = open_new_file(blks, &prelinked, cpio->index)) != DRPM_ERR_OK) break; if (prelinked) { blks->cpio_files_index = -1; return fillblock_prelink(blks, blk, id, copy_cnt, cpio); } file = get_open_file(blks, cpio->index); } if (file->offset != (off_t)file_off && lseek(file->filedesc, file_off, SEEK_SET) != (off_t)file_off) { error = DRPM_ERR_IO; break; } if (read(file->filedesc, buf_ptr, read_len) != (ssize_t)read_len) { error = DRPM_ERR_FORMAT; break; } file->offset = file_off + read_len; } else { read_len = MIN(len, cpio->content_len - file_off); memset(buf_ptr, 0, read_len); } buf_ptr += read_len; off += read_len; len -= read_len; continue; } blks->cpio_files_index++; cpio++; fill_cpio_header(blks, cpio->index); } blk->id = id; blk->type = BLK_CORE_NOPAGE; return error; } /* if an installed file is modified by prelink, this will use the original */ int fillblock_prelink(struct blocks *blks, struct block *blk, size_t id, size_t copy_cnt, const struct cpio_file *cpio) { int error = DRPM_ERR_OK; struct stat stats; uint64_t off = id * BLOCK_SIZE; int filedesc = -1; bool prelinked; unsigned char plnk_buf[128]; size_t read_len; size_t id_orig = id; size_t len; unsigned char *buf_ptr; size_t file_off; const char *linkto; unsigned char blk_buf[BLOCK_SIZE]; if (blks == NULL || blk == NULL || cpio == NULL) return DRPM_ERR_PROG; while (true) { while (cpio->offset > off) cpio--; if (cpio->index < 0 || cpio->content_len == 0 || cpio->offset + cpio->header_len >= off || S_ISLNK(blks->files[cpio->index].mode)) break; if ((filedesc = open(blks->files[cpio->index].name, O_RDONLY)) < 0) { error = DRPM_ERR_IO; goto cleanup; } if (fstat(filedesc, &stats) != 0 || stats.st_size == blks->files[cpio->index].size) break; if ((error = is_prelinked(&prelinked, filedesc, plnk_buf, pread(filedesc, plnk_buf, 128, 0))) != DRPM_ERR_OK) goto cleanup; if (!prelinked) break; close(filedesc); filedesc = -1; do { id--; off = id * BLOCK_SIZE; } while (cpio->offset + cpio->header_len < off); } if (!(filedesc < 0)) { file_off = off - (cpio->offset + cpio->header_len); if (file_off > 0 && file_off < blks->files[cpio->index].size && lseek(filedesc, file_off, SEEK_SET) == (off_t)-1) { error = DRPM_ERR_IO; goto cleanup; } } while (true) { len = BLOCK_SIZE; buf_ptr = blk->data.buffer; while (len > 0) { while (off >= cpio->offset + cpio->header_len + cpio->content_len) { if (!(filedesc < 0)) { close(filedesc); filedesc = -1; } cpio++; } if (off < cpio->offset + cpio->header_len) { file_off = off - cpio->offset; read_len = MIN(len, cpio->header_len - file_off); fill_cpio_header(blks, cpio->index); memcpy(buf_ptr, blks->cpio_buffer + file_off, read_len); buf_ptr += read_len; off += read_len; len -= read_len; } if (len == 0) break; if (cpio->index < 0) { memset(buf_ptr, 0, len); buf_ptr += len; off += len; len = 0; } else if (S_ISLNK(blks->files[cpio->index].mode)) { file_off = off - (cpio->offset + cpio->header_len); linkto = blks->files[cpio->index].linkto; read_len = MIN(len, strlen(linkto) < file_off ? 0 : strlen(linkto)); if (read_len) { memcpy(buf_ptr, linkto + file_off, read_len); buf_ptr += read_len; off += read_len; len -= read_len; file_off += read_len; } if (cpio->content_len > file_off) { read_len = MIN(len, cpio->content_len - file_off); if (read_len > 0) { memset(buf_ptr, 0, read_len); buf_ptr += read_len; off += read_len; len -= read_len; } } } else if (cpio->content_len > 0) { file_off = off - (cpio->offset + cpio->header_len); if (file_off < blks->files[cpio->index].size) { read_len = MIN(len, blks->files[cpio->index].size - file_off); if (filedesc < 0) { prelinked = false; if ((filedesc = open(blks->files[cpio->index].name, O_RDONLY)) < 0) { error = DRPM_ERR_IO; goto cleanup; } else if (fstat(filedesc, &stats) == 0 && stats.st_size != blks->files[cpio->index].size) { if ((error = is_prelinked(&prelinked, filedesc, plnk_buf, pread(filedesc, plnk_buf, 128, 0))) != DRPM_ERR_OK) goto cleanup; if (prelinked) { close(filedesc); if ((error = prelink_open(blks->files[cpio->index].name, &filedesc)) != DRPM_ERR_OK) goto cleanup; } } } if (read(filedesc, buf_ptr, read_len) != (ssize_t)read_len) { error = DRPM_ERR_IO; goto cleanup; } buf_ptr += read_len; off += read_len; len -= read_len; file_off += read_len; } if (file_off >= blks->files[cpio->index].size) { if (!(filedesc < 0)) { close(filedesc); filedesc = -1; } read_len = MIN(len, cpio->content_len - file_off); if (read_len > 0) memset(buf_ptr, 0, read_len); buf_ptr += read_len; off += read_len; len -= read_len; } } } blk->type = BLK_CORE; blk->id = id; if (id == id_orig) { memcpy(blk_buf, blk->data.buffer, BLOCK_SIZE); } else if (blks->blocks_max[id] > copy_cnt || (blks->blocks_max[id] == copy_cnt && id > id_orig)) { if ((error = push_block(blks, blk, copy_cnt)) != DRPM_ERR_OK) goto cleanup; } if (filedesc < 0 || !prelinked) break; id++; off = id * BLOCK_SIZE; } if (id < id_orig) { error = DRPM_ERR_PROG; goto cleanup; } memcpy(blk->data.buffer, blk_buf, BLOCK_SIZE); blk->type = BLK_CORE; blk->id = id_orig; cleanup: if (!(filedesc < 0)) close(filedesc); return error; } drpm-0.5.1/src/drpm_compstrm.c000066400000000000000000000515741421165313000163100ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2015 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_LZLIB_DEVEL #include #endif #ifdef WITH_ZSTD #include #endif struct compstrm { unsigned char *data; size_t data_len; size_t data_pos; int filedesc; union { z_stream gzip; bz_stream bzip2; lzma_stream lzma; #ifdef HAVE_LZLIB_DEVEL struct LZ_Encoder *lzip; #endif #ifdef WITH_ZSTD ZSTD_CCtx *zstd_context; #endif } stream; int (*write_chunk)(struct compstrm *, size_t, const void *); int (*finish)(struct compstrm *); bool finished; }; static int finish_bzip2(struct compstrm *); static int finish_gzip(struct compstrm *); static int finish_lzma(struct compstrm *); static int init_bzip2(struct compstrm *, int); static int init_gzip(struct compstrm *, int); static int init_lzma(struct compstrm *, int); static int init_xz(struct compstrm *, int); static int writechunk(struct compstrm *, size_t, const void *); static int writechunk_bzip2(struct compstrm *, size_t, const void *); static int writechunk_gzip(struct compstrm *, size_t, const void *); static int writechunk_lzma(struct compstrm *, size_t, const void *); #ifdef HAVE_LZLIB_DEVEL static int finish_lzip(struct compstrm *); static int init_lzip(struct compstrm *, int); static int writechunk_lzip(struct compstrm *, size_t, const void *); static int lzip_error(struct compstrm *strm) { switch (LZ_compress_errno(strm->stream.lzip)) { case LZ_ok: return DRPM_ERR_OK; case LZ_bad_argument: case LZ_sequence_error: return DRPM_ERR_PROG; case LZ_mem_error: return DRPM_ERR_MEMORY; default: return DRPM_ERR_OTHER; } } #endif #ifdef WITH_ZSTD static int finish_zstd(struct compstrm *); static int init_zstd(struct compstrm *, int); static int writechunk_zstd(struct compstrm *, size_t, const void *); #endif /* Functions for finishing compression for individual methods. */ int finish_bzip2(struct compstrm *strm) { int error = DRPM_ERR_OK; int ret; unsigned char *data_tmp; char out_buffer[CHUNK_SIZE]; size_t out_len; strm->stream.bzip2.next_in = NULL; strm->stream.bzip2.avail_in = 0; do { strm->stream.bzip2.next_out = out_buffer; strm->stream.bzip2.avail_out = CHUNK_SIZE; ret = BZ2_bzCompress(&strm->stream.bzip2, BZ_FINISH); out_len = CHUNK_SIZE - strm->stream.bzip2.avail_out; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (ret != BZ_STREAM_END); cleanup: BZ2_bzCompressEnd(&strm->stream.bzip2); return error; } int finish_gzip(struct compstrm *strm) { int error = DRPM_ERR_OK; unsigned char *data_tmp; unsigned char out_buffer[CHUNK_SIZE]; size_t out_len; strm->stream.gzip.next_in = Z_NULL; strm->stream.gzip.avail_in = 0; do { strm->stream.gzip.next_out = out_buffer; strm->stream.gzip.avail_out = CHUNK_SIZE; deflate(&strm->stream.gzip, Z_FINISH); out_len = CHUNK_SIZE - strm->stream.gzip.avail_out; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (strm->stream.gzip.avail_out == 0); cleanup: deflateEnd(&strm->stream.gzip); return error; } int finish_lzma(struct compstrm *strm) { int error = DRPM_ERR_OK; int ret; unsigned char *data_tmp; unsigned char out_buffer[CHUNK_SIZE]; size_t out_len; strm->stream.lzma.next_in = NULL; strm->stream.lzma.avail_in = 0; do { strm->stream.lzma.next_out = out_buffer; strm->stream.lzma.avail_out = CHUNK_SIZE; switch ((ret = lzma_code(&strm->stream.lzma, LZMA_FINISH))) { case LZMA_OK: case LZMA_STREAM_END: break; case LZMA_MEM_ERROR: error = DRPM_ERR_MEMORY; goto cleanup; default: error = DRPM_ERR_FORMAT; goto cleanup; } out_len = CHUNK_SIZE - strm->stream.lzma.avail_out; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (ret != LZMA_STREAM_END); cleanup: lzma_end(&strm->stream.lzma); return error; } #ifdef HAVE_LZLIB_DEVEL int finish_lzip(struct compstrm *strm) { int error = DRPM_ERR_OK; int rd; unsigned char *data_tmp; unsigned char out_buffer[CHUNK_SIZE]; size_t out_len; LZ_compress_finish(strm->stream.lzip); do { if ((rd = LZ_compress_read(strm->stream.lzip, out_buffer, CHUNK_SIZE)) < 0) { error = lzip_error(strm); if (error == DRPM_ERR_OK) error = DRPM_ERR_OTHER; goto cleanup; } out_len = rd; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (!LZ_compress_finished(strm->stream.lzip)); cleanup: LZ_compress_close(strm->stream.lzip); return error; } #endif #ifdef WITH_ZSTD int finish_zstd(struct compstrm *strm) { size_t const buffOutSize = ZSTD_CStreamOutSize(); void* const buffOut = malloc(buffOutSize); if (buffOut == NULL) return DRPM_ERR_MEMORY; unsigned char *data_tmp; size_t remaining; // No more new input just finish flushing compression data ZSTD_inBuffer input = { NULL, 0, 0 }; do{ ZSTD_outBuffer output = { buffOut, buffOutSize, 0 }; remaining = ZSTD_compressStream2(strm->stream.zstd_context, &output , &input, ZSTD_e_end); if (ZSTD_isError(remaining)) return DRPM_ERR_OTHER; if (output.pos == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + output.pos)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, buffOut, output.pos); strm->data_len += output.pos; } while(remaining != 0); free(buffOut); ZSTD_freeCCtx(strm->stream.zstd_context); return DRPM_ERR_OK; } #endif /* Function for initializing compression for individual methods. */ int init_bzip2(struct compstrm *strm, int level) { strm->write_chunk = writechunk_bzip2; strm->finish = finish_bzip2; strm->stream.bzip2.bzalloc = NULL; strm->stream.bzip2.bzfree = NULL; strm->stream.bzip2.opaque = NULL; strm->stream.bzip2.next_in = NULL; strm->stream.bzip2.avail_in = 0; if (level == DRPM_COMP_LEVEL_DEFAULT) level = 9; switch (BZ2_bzCompressInit(&strm->stream.bzip2, level, 0, 0)) { case BZ_CONFIG_ERROR: BZ2_bzCompressEnd(&strm->stream.bzip2); return DRPM_ERR_CONFIG; case BZ_MEM_ERROR: BZ2_bzCompressEnd(&strm->stream.bzip2); return DRPM_ERR_MEMORY; } return DRPM_ERR_OK; } int init_gzip(struct compstrm *strm, int level) { strm->write_chunk = writechunk_gzip; strm->finish = finish_gzip; strm->stream.gzip.zalloc = Z_NULL; strm->stream.gzip.zfree = Z_NULL; strm->stream.gzip.opaque = Z_NULL; if (level == DRPM_COMP_LEVEL_DEFAULT) level = Z_BEST_COMPRESSION; switch (deflateInit2(&strm->stream.gzip, level, Z_DEFLATED, 16 + MAX_WBITS, 8, Z_DEFAULT_STRATEGY)) { case Z_VERSION_ERROR: deflateEnd(&strm->stream.gzip); return DRPM_ERR_CONFIG; case Z_MEM_ERROR: deflateEnd(&strm->stream.gzip); return DRPM_ERR_MEMORY; } return DRPM_ERR_OK; } int init_lzma(struct compstrm *strm, int level) { lzma_stream stream = LZMA_STREAM_INIT; lzma_options_lzma options; strm->write_chunk = writechunk_lzma; strm->finish = finish_lzma; strm->stream.lzma = stream; if (level == DRPM_COMP_LEVEL_DEFAULT) level = 2; lzma_lzma_preset(&options, level); switch (lzma_alone_encoder(&strm->stream.lzma, &options)) { case LZMA_OK: break; case LZMA_MEM_ERROR: return DRPM_ERR_MEMORY; default: return DRPM_ERR_FORMAT; } return DRPM_ERR_OK; } int init_xz(struct compstrm *strm, int level) { lzma_stream stream = LZMA_STREAM_INIT; strm->write_chunk = writechunk_lzma; strm->finish = finish_lzma; strm->stream.lzma = stream; memset(&strm->stream.lzma, 0, sizeof(lzma_stream)); if (level == DRPM_COMP_LEVEL_DEFAULT) level = 3; switch (lzma_easy_encoder(&strm->stream.lzma, level, LZMA_CHECK_SHA256)) { case LZMA_OK: break; case LZMA_MEM_ERROR: return DRPM_ERR_MEMORY; default: return DRPM_ERR_FORMAT; } return DRPM_ERR_OK; } #ifdef HAVE_LZLIB_DEVEL int init_lzip(struct compstrm *strm, int level) { int error; strm->write_chunk = writechunk_lzip; strm->finish = finish_lzip; if ((strm->stream.lzip = LZ_compress_open(65535, 16, SIZE_MAX)) == NULL) return DRPM_ERR_MEMORY; if ((error = lzip_error(strm)) != DRPM_ERR_OK) LZ_compress_close(strm->stream.lzip); return error; } #endif #ifdef WITH_ZSTD int init_zstd(struct compstrm *strm, int level) { if ((strm->stream.zstd_context = ZSTD_createCCtx()) == NULL) return DRPM_ERR_MEMORY; if (level == DRPM_COMP_LEVEL_DEFAULT) level = 19; if (ZSTD_isError(ZSTD_CCtx_setParameter(strm->stream.zstd_context, ZSTD_c_compressionLevel, level))) return DRPM_ERR_OTHER; strm->write_chunk = writechunk_zstd; strm->finish = finish_zstd; return DRPM_ERR_OK; } #endif /* Frees memory allocated by compression stream. */ int compstrm_destroy(struct compstrm **strm) { if (strm == NULL || *strm == NULL) return DRPM_ERR_PROG; free((*strm)->data); free(*strm); *strm = NULL; return DRPM_ERR_OK; } /* Initializes compression stream. * The compression method will be and the compression level will be . * If is valid, compressed data will be written to the file. */ int compstrm_init(struct compstrm **strm, int filedesc, unsigned short comp, int level) { int error; if (strm == NULL || (level != DRPM_COMP_LEVEL_DEFAULT && (level < 1 || level > 99))) return DRPM_ERR_PROG; if ((*strm = malloc(sizeof(struct compstrm))) == NULL) return DRPM_ERR_MEMORY; (*strm)->data = NULL; (*strm)->data_len = 0; (*strm)->data_pos = 0; (*strm)->filedesc = filedesc; (*strm)->finished = false; switch (comp) { case DRPM_COMP_NONE: (*strm)->write_chunk = writechunk; (*strm)->finish = NULL; break; case DRPM_COMP_GZIP: if ((error = init_gzip(*strm, level)) != DRPM_ERR_OK) goto cleanup_fail; break; case DRPM_COMP_BZIP2: if ((error = init_bzip2(*strm, level)) != DRPM_ERR_OK) goto cleanup_fail; break; case DRPM_COMP_LZMA: if ((error = init_lzma(*strm, level)) != DRPM_ERR_OK) goto cleanup_fail; break; case DRPM_COMP_XZ: if ((error = init_xz(*strm, level)) != DRPM_ERR_OK) goto cleanup_fail; break; #ifdef HAVE_LZLIB_DEVEL case DRPM_COMP_LZIP: if ((error = init_lzip(*strm, level)) != DRPM_ERR_OK) goto cleanup_fail; break; #endif #ifdef WITH_ZSTD case DRPM_COMP_ZSTD: if ((error = init_zstd(*strm, level)) != DRPM_ERR_OK) goto cleanup_fail; break; #endif default: return DRPM_ERR_PROG; } return DRPM_ERR_OK; cleanup_fail: free(*strm); *strm = NULL; return error; } /* Finishes up compression. * If neither nor are NULL, stores all data * compressed by this stream in <*data> (and its size in <*data_len>). */ int compstrm_finish(struct compstrm *strm, unsigned char **data, size_t *data_len) { int error; size_t comp_write_len; const bool copy_data = (data != NULL && data_len != NULL); if (strm == NULL || strm->finished) return DRPM_ERR_PROG; if (copy_data) { *data = NULL; *data_len = 0; } if (strm->finish != NULL) { if ((error = strm->finish(strm)) != DRPM_ERR_OK) return error; comp_write_len = strm->data_len - strm->data_pos; if (strm->filedesc >= 0 && comp_write_len > 0 && write(strm->filedesc, strm->data + strm->data_pos, comp_write_len) != (ssize_t)comp_write_len) return DRPM_ERR_IO; } strm->finished = true; if (copy_data && strm->data_len > 0) { if ((*data = malloc(strm->data_len)) == NULL) return DRPM_ERR_MEMORY; memcpy(*data, strm->data, strm->data_len); *data_len = strm->data_len; } return DRPM_ERR_OK; } int compstrm_write_be32(struct compstrm *strm, uint32_t number) { unsigned char bytes[4]; if (strm == NULL || strm->finished) return DRPM_ERR_PROG; create_be32(number, bytes); return compstrm_write(strm, 4, bytes); } int compstrm_write_be64(struct compstrm *strm, uint64_t number) { unsigned char bytes[8]; if (strm == NULL || strm->finished) return DRPM_ERR_PROG; create_be64(number, bytes); return compstrm_write(strm, 8, bytes); } /* Compresses bytes pointed to by . */ int compstrm_write(struct compstrm *strm, size_t write_len, const void *buffer) { int error; size_t comp_write_len; if (strm == NULL || strm->finished) return DRPM_ERR_PROG; if (write_len == 0) return DRPM_ERR_OK; if (buffer == NULL) return DRPM_ERR_PROG; if ((error = strm->write_chunk(strm, write_len, buffer)) != DRPM_ERR_OK) return error; comp_write_len = strm->data_len - strm->data_pos; if (strm->filedesc >= 0 && comp_write_len > 0) { if (write(strm->filedesc, strm->data + strm->data_pos, comp_write_len) != (ssize_t)comp_write_len) return DRPM_ERR_IO; } strm->data_pos = strm->data_len; return DRPM_ERR_OK; } /* Functions for compressing input data using individual methods. */ // no compression int writechunk(struct compstrm *strm, size_t in_len, const void *in_buffer) { unsigned char *data_tmp; if ((data_tmp = realloc(strm->data, strm->data_len + in_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, in_buffer, in_len); strm->data_len += in_len; return DRPM_ERR_OK; } int writechunk_bzip2(struct compstrm *strm, size_t in_len, const void *in_buffer) { unsigned char *data_tmp; char out_buffer[CHUNK_SIZE]; size_t out_len; strm->stream.bzip2.next_in = (char *)in_buffer; strm->stream.bzip2.avail_in = in_len; do { strm->stream.bzip2.next_out = out_buffer; strm->stream.bzip2.avail_out = CHUNK_SIZE; BZ2_bzCompress(&strm->stream.bzip2, BZ_RUN); out_len = CHUNK_SIZE - strm->stream.bzip2.avail_out; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (strm->stream.bzip2.avail_out == 0); return DRPM_ERR_OK; } int writechunk_gzip(struct compstrm *strm, size_t in_len, const void *in_buffer) { unsigned char *data_tmp; unsigned char out_buffer[CHUNK_SIZE]; size_t out_len; strm->stream.gzip.next_in = (unsigned char *)in_buffer; strm->stream.gzip.avail_in = in_len; do { strm->stream.gzip.next_out = out_buffer; strm->stream.gzip.avail_out = CHUNK_SIZE; deflate(&strm->stream.gzip, Z_NO_FLUSH); out_len = CHUNK_SIZE - strm->stream.gzip.avail_out; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (strm->stream.gzip.avail_out == 0); return DRPM_ERR_OK; } int writechunk_lzma(struct compstrm *strm, size_t in_len, const void *in_buffer) { unsigned char *data_tmp; unsigned char out_buffer[CHUNK_SIZE]; size_t out_len; strm->stream.lzma.next_in = (unsigned char *)in_buffer; strm->stream.lzma.avail_in = in_len; do { strm->stream.lzma.next_out = out_buffer; strm->stream.lzma.avail_out = CHUNK_SIZE; switch (lzma_code(&strm->stream.lzma, LZMA_RUN)) { case LZMA_OK: break; case LZMA_MEM_ERROR: return DRPM_ERR_MEMORY; default: return DRPM_ERR_FORMAT; } out_len = CHUNK_SIZE - strm->stream.lzma.avail_out; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (strm->stream.lzma.avail_out == 0); return DRPM_ERR_OK; } #ifdef HAVE_LZLIB_DEVEL int writechunk_lzip(struct compstrm *strm, size_t in_len, const void *in_buffer) { int error; unsigned char *data_tmp; unsigned char out_buffer[CHUNK_SIZE]; size_t out_len; size_t written = 0; int wr; int rd; while (written < in_len) { if (LZ_compress_write_size(strm->stream.lzip) > 0) { if ((wr = LZ_compress_write(strm->stream.lzip, (unsigned char *)in_buffer + written, in_len - written)) < 0) { error = lzip_error(strm); return error == DRPM_ERR_OK ? DRPM_ERR_OTHER : error; } written += wr; } if ((rd = LZ_compress_read(strm->stream.lzip, out_buffer, CHUNK_SIZE)) < 0) { error = lzip_error(strm); return error == DRPM_ERR_OK ? DRPM_ERR_OTHER : error; } out_len = rd; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; }; return DRPM_ERR_OK; } #endif #ifdef WITH_ZSTD int writechunk_zstd(struct compstrm *strm, size_t in_len, const void *in_buffer) { size_t const buffOutSize = ZSTD_CStreamOutSize(); void* const buffOut = malloc(buffOutSize); if (buffOut == NULL) return DRPM_ERR_MEMORY; unsigned char *data_tmp; ZSTD_inBuffer input = { in_buffer, in_len, 0 }; do{ ZSTD_outBuffer output = { buffOut, buffOutSize, 0 }; size_t const remaining = ZSTD_compressStream2(strm->stream.zstd_context, &output , &input, ZSTD_e_continue); if (ZSTD_isError(remaining)) return DRPM_ERR_OTHER; if (output.pos == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + output.pos)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, buffOut, output.pos); strm->data_len += output.pos; } while(input.pos != input.size); free(buffOut); return DRPM_ERR_OK; } #endif drpm-0.5.1/src/drpm_decompstrm.c000066400000000000000000000524541421165313000166170ustar00rootroot00000000000000/* Authors: Pavel Tobias Matej Chalk Copyright (C) 2014 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_LZLIB_DEVEL #include #endif #ifdef WITH_ZSTD #include #endif #include /* magic bytes for determining compression type */ #define MAGIC_BZIP2(x) (((x) >> 40) == 0x425A68) #define MAGIC_GZIP(x) (((x) >> 48) == 0x1F8B) #define MAGIC_LZMA(x) (((x) >> 40) == 0x5D0000) #define MAGIC_XZ(x) (((x) >> 16) == 0xFD377A585A00) #define MAGIC_LZIP(x) (((x) >> 32) == 0x4C5A4950) #define MAGIC_ZSTD(x) (((x) >> 32) == 0x28B52FFD) struct decompstrm { unsigned char *data; size_t data_len; size_t data_pos; int filedesc; union { z_stream gzip; bz_stream bzip2; lzma_stream lzma; #ifdef HAVE_LZLIB_DEVEL struct LZ_Decoder *lzip; #endif #ifdef WITH_ZSTD ZSTD_DCtx *zstd_context; #endif } stream; bool lzip_eof; int (*read_chunk)(struct decompstrm *); void (*finish)(struct decompstrm *); size_t comp_size; MD5_CTX *md5; const unsigned char *buffer; size_t buffer_len; }; static void finish_bzip2(struct decompstrm *); static void finish_gzip(struct decompstrm *); static void finish_lzma(struct decompstrm *); static int init_bzip2(struct decompstrm *); static int init_gzip(struct decompstrm *); static int init_lzma(struct decompstrm *); static int readchunk(struct decompstrm *); static int readchunk_bzip2(struct decompstrm *); static int readchunk_gzip(struct decompstrm *); static int readchunk_lzma(struct decompstrm *); #ifdef HAVE_LZLIB_DEVEL static void finish_lzip(struct decompstrm *); static int init_lzip(struct decompstrm *); static int readchunk_lzip(struct decompstrm *); static int lzip_error(struct decompstrm *strm) { switch (LZ_decompress_errno(strm->stream.lzip)) { case LZ_ok: return DRPM_ERR_OK; case LZ_bad_argument: case LZ_sequence_error: return DRPM_ERR_PROG; case LZ_mem_error: return DRPM_ERR_MEMORY; default: return DRPM_ERR_OTHER; } } #endif #ifdef WITH_ZSTD static void finish_zstd(struct decompstrm *); static int init_zstd(struct decompstrm *); static int readchunk_zstd(struct decompstrm *); #endif /* Functions for finishing decompression for individual methods. */ void finish_bzip2(struct decompstrm *strm) { BZ2_bzDecompressEnd(&strm->stream.bzip2); } void finish_gzip(struct decompstrm *strm) { inflateEnd(&strm->stream.gzip); } void finish_lzma(struct decompstrm *strm) { lzma_end(&strm->stream.lzma); } #ifdef HAVE_LZLIB_DEVEL void finish_lzip(struct decompstrm *strm) { LZ_decompress_close(strm->stream.lzip); } #endif #ifdef WITH_ZSTD void finish_zstd(struct decompstrm *strm) { ZSTD_freeDCtx(strm->stream.zstd_context); } #endif /* Functions for initializing decompression for individual methods. */ int init_bzip2(struct decompstrm *strm) { strm->read_chunk = readchunk_bzip2; strm->finish = finish_bzip2; strm->stream.bzip2.bzalloc = NULL; strm->stream.bzip2.bzfree = NULL; strm->stream.bzip2.opaque = NULL; strm->stream.bzip2.next_in = NULL; strm->stream.bzip2.avail_in = 0; switch (BZ2_bzDecompressInit(&strm->stream.bzip2, 0, 1)) { case BZ_CONFIG_ERROR: BZ2_bzDecompressEnd(&strm->stream.bzip2); return DRPM_ERR_CONFIG; case BZ_MEM_ERROR: BZ2_bzDecompressEnd(&strm->stream.bzip2); return DRPM_ERR_MEMORY; } return DRPM_ERR_OK; } int init_gzip(struct decompstrm *strm) { strm->read_chunk = readchunk_gzip; strm->finish = finish_gzip; strm->stream.gzip.zalloc = Z_NULL; strm->stream.gzip.zfree = Z_NULL; strm->stream.gzip.opaque = Z_NULL; strm->stream.gzip.next_in = Z_NULL; strm->stream.gzip.avail_in = 0; switch (inflateInit2(&strm->stream.gzip, 16 + MAX_WBITS)) { case Z_VERSION_ERROR: inflateEnd(&strm->stream.gzip); return DRPM_ERR_CONFIG; case Z_MEM_ERROR: inflateEnd(&strm->stream.gzip); return DRPM_ERR_MEMORY; } return DRPM_ERR_OK; } int init_lzma(struct decompstrm *strm) { lzma_stream stream = LZMA_STREAM_INIT; strm->read_chunk = readchunk_lzma; strm->finish = finish_lzma; strm->stream.lzma = stream; switch (lzma_auto_decoder(&strm->stream.lzma, UINT64_MAX, 0)) { case LZMA_OK: break; case LZMA_MEM_ERROR: return DRPM_ERR_MEMORY; default: return DRPM_ERR_FORMAT; } return DRPM_ERR_OK; } #ifdef HAVE_LZLIB_DEVEL int init_lzip(struct decompstrm *strm) { int error; strm->read_chunk = readchunk_lzip; strm->finish = finish_lzip; strm->lzip_eof = false; if ((strm->stream.lzip = LZ_decompress_open()) == NULL) return DRPM_ERR_MEMORY; if ((error = lzip_error(strm)) != DRPM_ERR_OK) LZ_decompress_close(strm->stream.lzip); return error; } #endif #ifdef WITH_ZSTD int init_zstd(struct decompstrm *strm) { if ((strm->stream.zstd_context = ZSTD_createDCtx()) == NULL) return DRPM_ERR_MEMORY; strm->read_chunk = readchunk_zstd; strm->finish = finish_zstd; return DRPM_ERR_OK; } #endif /* Frees memory allocated by decompression stream. */ int decompstrm_destroy(struct decompstrm **strm) { if (strm == NULL || *strm == NULL) return DRPM_ERR_PROG; if ((*strm)->finish != NULL) (*strm)->finish(*strm); free((*strm)->data); free(*strm); *strm = NULL; return DRPM_ERR_OK; } /* Initializes decompression stream. * The detected compression method will be stored in <*comp> (if not NULL). * If is not NULL, input data will be used to update the MD5 context. * If is valid, compressed data will be read from the file. * Otherwise, input data is read from of size . */ int decompstrm_init(struct decompstrm **strm, int filedesc, unsigned short *comp, MD5_CTX *md5, const unsigned char *buffer, size_t buffer_len) { uint64_t magic; int error = DRPM_ERR_OK; if (strm == NULL || (filedesc < 0 && (buffer == NULL || buffer_len < 8))) return DRPM_ERR_PROG; if (filedesc < 0) { magic = parse_be64(buffer); } else { if ((error = read_be64(filedesc, &magic)) != DRPM_ERR_OK) return error; if (lseek(filedesc, -8, SEEK_CUR) == -1) return DRPM_ERR_IO; } if ((*strm = malloc(sizeof(struct decompstrm))) == NULL) return DRPM_ERR_MEMORY; (*strm)->data = NULL; (*strm)->data_len = 0; (*strm)->data_pos = 0; (*strm)->filedesc = filedesc; (*strm)->comp_size = 0; (*strm)->md5 = md5; (*strm)->buffer = buffer; (*strm)->buffer_len = buffer_len; if (MAGIC_GZIP(magic)) { if (comp != NULL) *comp = DRPM_COMP_GZIP; if ((error = init_gzip(*strm)) != DRPM_ERR_OK) goto cleanup_fail; } else if (MAGIC_BZIP2(magic)) { if (comp != NULL) *comp = DRPM_COMP_BZIP2; if ((error = init_bzip2(*strm)) != DRPM_ERR_OK) goto cleanup_fail; } else if (MAGIC_XZ(magic)) { if (comp != NULL) *comp = DRPM_COMP_XZ; if ((error = init_lzma(*strm)) != DRPM_ERR_OK) goto cleanup_fail; } else if (MAGIC_LZMA(magic)) { if (comp != NULL) *comp = DRPM_COMP_LZMA; if ((error = init_lzma(*strm)) != DRPM_ERR_OK) goto cleanup_fail; #ifdef HAVE_LZLIB_DEVEL } else if (MAGIC_LZIP(magic)) { if (comp != NULL) *comp = DRPM_COMP_LZIP; if ((error = init_lzip(*strm)) != DRPM_ERR_OK) goto cleanup_fail; #endif #ifdef WITH_ZSTD } else if (MAGIC_ZSTD(magic)) { if (comp != NULL) *comp = DRPM_COMP_ZSTD; if ((error = init_zstd(*strm)) != DRPM_ERR_OK) goto cleanup_fail; #endif } else { if (comp != NULL) *comp = DRPM_COMP_NONE; (*strm)->read_chunk = readchunk; (*strm)->finish = NULL; } return DRPM_ERR_OK; cleanup_fail: free(*strm); *strm = NULL; return error; } /* Fetches size of *compressed* data. */ int decompstrm_get_comp_size(struct decompstrm *strm, size_t *size) { if (strm == NULL || size == NULL) return DRPM_ERR_PROG; *size = strm->comp_size; return DRPM_ERR_OK; } int decompstrm_read_be32(struct decompstrm *strm, uint32_t *buffer_ret) { int error; unsigned char bytes[4]; if (strm == NULL) return DRPM_ERR_PROG; if ((error = decompstrm_read(strm, 4, bytes)) != DRPM_ERR_OK) return error; *buffer_ret = parse_be32(bytes); return DRPM_ERR_OK; } int decompstrm_read_be64(struct decompstrm *strm, uint64_t *buffer_ret) { int error; unsigned char bytes[8]; if (strm == NULL) return DRPM_ERR_PROG; if ((error = decompstrm_read(strm, 8, bytes)) != DRPM_ERR_OK) return error; *buffer_ret = parse_be64(bytes); return DRPM_ERR_OK; } /* Decompresses enough data to store bytes at . */ int decompstrm_read(struct decompstrm *strm, size_t read_len, void *buffer_ret) { int error; if (strm == NULL) return DRPM_ERR_PROG; if (UNSIGNED_SUM_OVERFLOWS(strm->data_len, read_len)) return DRPM_ERR_OVERFLOW; while (strm->data_pos + read_len > strm->data_len) if ((error = strm->read_chunk(strm)) != DRPM_ERR_OK) return error; if (buffer_ret != NULL) memcpy(buffer_ret, strm->data + strm->data_pos, read_len); strm->data_pos += read_len; return DRPM_ERR_OK; } /* Decompresses the entire file and stores the result <*buffer_ret> * (and the size <*len_ret>). */ int decompstrm_read_until_eof(struct decompstrm *strm, size_t *len_ret, unsigned char **buffer_ret) { int error; bool eof = false; size_t data_len_prev; if (strm == NULL || (buffer_ret != NULL && len_ret == NULL)) return DRPM_ERR_PROG; while (!eof) { data_len_prev = strm->data_len; switch ((error = strm->read_chunk(strm))) { case DRPM_ERR_OK: break; case DRPM_ERR_FORMAT: // nothing more to read eof = true; break; default: return error; } if (data_len_prev > strm->data_len) return DRPM_ERR_OVERFLOW; } if (len_ret != NULL) { *len_ret = strm->data_len - strm->data_pos; if (buffer_ret != NULL) { if ((*buffer_ret = malloc(*len_ret)) == NULL) return DRPM_ERR_MEMORY; memcpy(*buffer_ret, strm->data + strm->data_pos, *len_ret); strm->data_pos = strm->data_len; } } return DRPM_ERR_OK; } /* Functions for decompressing chunks of data. */ // no compression int readchunk(struct decompstrm *strm) { ssize_t in_len; unsigned char *data_tmp; unsigned char buffer[CHUNK_SIZE]; if (strm->filedesc < 0) { in_len = MIN(CHUNK_SIZE, strm->buffer_len); memcpy(buffer, strm->buffer, in_len); strm->buffer += in_len; strm->buffer_len -= in_len; } else { if ((in_len = read(strm->filedesc, buffer, CHUNK_SIZE)) < 0) return DRPM_ERR_IO; } if (in_len == 0) return DRPM_ERR_FORMAT; if ((data_tmp = realloc(strm->data, strm->data_len + in_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, buffer, in_len); strm->data_len += in_len; strm->comp_size = strm->data_len; if (strm->md5 != NULL && MD5_Update(strm->md5, buffer, in_len) != 1) return DRPM_ERR_OTHER; return DRPM_ERR_OK; } int readchunk_bzip2(struct decompstrm *strm) { ssize_t in_len; unsigned char *data_tmp; char in_buffer[CHUNK_SIZE]; char out_buffer[CHUNK_SIZE]; size_t out_len; if (strm->filedesc < 0) { in_len = MIN(CHUNK_SIZE, strm->buffer_len); memcpy(in_buffer, strm->buffer, in_len); strm->buffer += in_len; strm->buffer_len -= in_len; } else { if ((in_len = read(strm->filedesc, in_buffer, CHUNK_SIZE)) < 0) return DRPM_ERR_IO; } if (in_len == 0) return DRPM_ERR_FORMAT; strm->stream.bzip2.next_in = in_buffer; strm->stream.bzip2.avail_in = in_len; do { strm->stream.bzip2.next_out = out_buffer; strm->stream.bzip2.avail_out = CHUNK_SIZE; switch (BZ2_bzDecompress(&strm->stream.bzip2)) { case BZ_DATA_ERROR: case BZ_DATA_ERROR_MAGIC: return DRPM_ERR_FORMAT; case BZ_MEM_ERROR: return DRPM_ERR_MEMORY; } out_len = CHUNK_SIZE - strm->stream.bzip2.avail_out; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (!strm->stream.bzip2.avail_out); strm->comp_size += in_len; if (strm->md5 != NULL && MD5_Update(strm->md5, in_buffer, in_len) != 1) return DRPM_ERR_OTHER; return DRPM_ERR_OK; } int readchunk_gzip(struct decompstrm *strm) { ssize_t in_len; unsigned char *data_tmp; unsigned char in_buffer[CHUNK_SIZE]; unsigned char out_buffer[CHUNK_SIZE]; size_t out_len; if (strm->filedesc < 0) { in_len = MIN(CHUNK_SIZE, strm->buffer_len); memcpy(in_buffer, strm->buffer, in_len); strm->buffer += in_len; strm->buffer_len -= in_len; } else { if ((in_len = read(strm->filedesc, in_buffer, CHUNK_SIZE)) < 0) return DRPM_ERR_IO; } if (in_len == 0) return DRPM_ERR_FORMAT; strm->stream.gzip.next_in = in_buffer; strm->stream.gzip.avail_in = in_len; do { strm->stream.gzip.next_out = out_buffer; strm->stream.gzip.avail_out = CHUNK_SIZE; switch (inflate(&strm->stream.gzip, Z_SYNC_FLUSH)) { case Z_DATA_ERROR: case Z_NEED_DICT: case Z_STREAM_ERROR: return DRPM_ERR_FORMAT; case Z_MEM_ERROR: return DRPM_ERR_MEMORY; } out_len = CHUNK_SIZE - strm->stream.gzip.avail_out; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (!strm->stream.gzip.avail_out); strm->comp_size += in_len; if (strm->md5 != NULL && MD5_Update(strm->md5, in_buffer, in_len) != 1) return DRPM_ERR_OTHER; return DRPM_ERR_OK; } int readchunk_lzma(struct decompstrm *strm) { ssize_t in_len; unsigned char *data_tmp; unsigned char in_buffer[CHUNK_SIZE]; unsigned char out_buffer[CHUNK_SIZE]; size_t out_len; if (strm->filedesc < 0) { in_len = MIN(CHUNK_SIZE, strm->buffer_len); memcpy(in_buffer, strm->buffer, in_len); strm->buffer += in_len; strm->buffer_len -= in_len; } else { if ((in_len = read(strm->filedesc, in_buffer, CHUNK_SIZE)) < 0) return DRPM_ERR_IO; } if (in_len == 0) return DRPM_ERR_FORMAT; strm->stream.lzma.next_in = in_buffer; strm->stream.lzma.avail_in = in_len; do { strm->stream.lzma.next_out = out_buffer; strm->stream.lzma.avail_out = CHUNK_SIZE; switch (lzma_code(&strm->stream.lzma, LZMA_RUN)) { case LZMA_OK: case LZMA_STREAM_END: break; case LZMA_FORMAT_ERROR: case LZMA_OPTIONS_ERROR: case LZMA_DATA_ERROR: case LZMA_BUF_ERROR: return DRPM_ERR_FORMAT; case LZMA_MEM_ERROR: return DRPM_ERR_MEMORY; default: return DRPM_ERR_OTHER; } out_len = CHUNK_SIZE - strm->stream.lzma.avail_out; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (!strm->stream.lzma.avail_out); strm->comp_size += in_len; if (strm->md5 != NULL && MD5_Update(strm->md5, in_buffer, in_len) != 1) return DRPM_ERR_OTHER; return DRPM_ERR_OK; } #ifdef HAVE_LZLIB_DEVEL int readchunk_lzip(struct decompstrm *strm) { int error; unsigned char *data_tmp; ssize_t in_len; unsigned char in_buffer[CHUNK_SIZE]; unsigned char out_buffer[CHUNK_SIZE]; size_t out_len; ssize_t written = 0; int wr; int rd; if (strm->lzip_eof) return DRPM_ERR_FORMAT; if (strm->filedesc < 0) { in_len = MIN(CHUNK_SIZE, strm->buffer_len); memcpy(in_buffer, strm->buffer, in_len); strm->buffer += in_len; strm->buffer_len -= in_len; } else { if ((in_len = read(strm->filedesc, in_buffer, CHUNK_SIZE)) < 0) return DRPM_ERR_IO; } if (in_len == 0) { strm->lzip_eof = true; LZ_decompress_finish(strm->stream.lzip); do { if ((rd = LZ_decompress_read(strm->stream.lzip, out_buffer, CHUNK_SIZE)) < 0) { error = lzip_error(strm); return error == DRPM_ERR_OK ? DRPM_ERR_OTHER : error; } out_len = rd; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; } while (!LZ_decompress_finished(strm->stream.lzip)); } else { while (written < in_len) { if (LZ_decompress_write_size(strm->stream.lzip) > 0) { if ((wr = LZ_decompress_write(strm->stream.lzip, in_buffer + written, in_len - written)) < 0) { error = lzip_error(strm); return error == DRPM_ERR_OK ? DRPM_ERR_OTHER : error; } written += wr; } if ((rd = LZ_decompress_read(strm->stream.lzip, out_buffer, CHUNK_SIZE)) < 0) { error = lzip_error(strm); return error == DRPM_ERR_OK ? DRPM_ERR_OTHER : error; } out_len = rd; if (out_len == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + out_len)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, out_buffer, out_len); strm->data_len += out_len; }; } strm->comp_size += in_len; if (strm->md5 != NULL && MD5_Update(strm->md5, in_buffer, in_len) != 1) return DRPM_ERR_OTHER; return DRPM_ERR_OK; } #endif #ifdef WITH_ZSTD int readchunk_zstd(struct decompstrm *strm) { ssize_t in_len; unsigned char *data_tmp; unsigned char in_buffer[CHUNK_SIZE]; if (strm->filedesc < 0) { in_len = MIN(CHUNK_SIZE, strm->buffer_len); memcpy(in_buffer, strm->buffer, in_len); strm->buffer += in_len; strm->buffer_len -= in_len; } else { if ((in_len = read(strm->filedesc, in_buffer, CHUNK_SIZE)) < 0) return DRPM_ERR_IO; } if (in_len == 0) return DRPM_ERR_FORMAT; size_t const buffOutSize = ZSTD_DStreamOutSize(); void* const buffOut = malloc(buffOutSize); if (buffOut == NULL) return DRPM_ERR_MEMORY; ZSTD_inBuffer input = { in_buffer, in_len, 0 }; while (input.pos < input.size) { ZSTD_outBuffer output = { buffOut, buffOutSize, 0 }; size_t const ret = ZSTD_decompressStream(strm->stream.zstd_context, &output , &input); if (ZSTD_isError(ret)) return DRPM_ERR_OTHER; if (output.pos == 0) continue; if ((data_tmp = realloc(strm->data, strm->data_len + output.pos)) == NULL) return DRPM_ERR_MEMORY; strm->data = data_tmp; memcpy(strm->data + strm->data_len, buffOut, output.pos); strm->data_len += output.pos; } strm->comp_size += in_len; if (strm->md5 != NULL && MD5_Update(strm->md5, in_buffer, in_len) != 1) return DRPM_ERR_OTHER; free(buffOut); return DRPM_ERR_OK; } #endif drpm-0.5.1/src/drpm_deltarpm.c000066400000000000000000000073141421165313000162450ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2004,2005 Michael Schroeder (mls@suse.de) Copyright (C) 2015 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #define DELTARPM_COMP_UN 0 #define DELTARPM_COMP_GZ 1 #define DELTARPM_COMP_BZ_20 2 #define DELTARPM_COMP_GZ_RSYNC 3 #define DELTARPM_COMP_BZ_17 4 #define DELTARPM_COMP_LZMA 5 #define DELTARPM_COMP_XZ 6 #define DELTARPM_COMP_ZSTD 7 #define DELTARPM_COMP_BZ DELTARPM_COMP_BZ_20 #define DELTARPM_MKCOMP(comp, level) ((comp) | ((level) << 8)) #define DELTARPM_COMPALGO(comp) ((comp) & 255) #define DELTARPM_COMPLEVEL(comp) (((comp) >> 8) & 255) /* Converts *from* deltarpm's on-disk encoding. */ bool deltarpm_decode_comp(uint32_t deltarpm_comp, unsigned short *comp, unsigned short *level) { switch (DELTARPM_COMPALGO(deltarpm_comp)) { case DELTARPM_COMP_UN: *comp = DRPM_COMP_NONE; break; case DELTARPM_COMP_GZ: case DELTARPM_COMP_GZ_RSYNC: *comp = DRPM_COMP_GZIP; break; case DELTARPM_COMP_BZ_20: case DELTARPM_COMP_BZ_17: *comp = DRPM_COMP_BZIP2; break; case DELTARPM_COMP_LZMA: *comp = DRPM_COMP_LZMA; break; case DELTARPM_COMP_XZ: *comp = DRPM_COMP_XZ; break; #ifdef WITH_ZSTD case DELTARPM_COMP_ZSTD: *comp = DRPM_COMP_ZSTD; break; #endif default: return false; } if (level != NULL) *level = DELTARPM_COMPLEVEL(deltarpm_comp); return true; } /* Converts *to* deltarpm's on-disk encoding. */ bool deltarpm_encode_comp(uint32_t *deltarpm_comp, unsigned short comp, unsigned short level) { switch (comp) { case DRPM_COMP_NONE: *deltarpm_comp = DELTARPM_MKCOMP(DELTARPM_COMP_UN, level); break; case DRPM_COMP_GZIP: *deltarpm_comp = DELTARPM_MKCOMP(DELTARPM_COMP_GZ, level); break; case DRPM_COMP_BZIP2: *deltarpm_comp = DELTARPM_MKCOMP(DELTARPM_COMP_BZ, level); break; case DRPM_COMP_LZMA: *deltarpm_comp = DELTARPM_MKCOMP(DELTARPM_COMP_LZMA, level); break; case DRPM_COMP_XZ: *deltarpm_comp = DELTARPM_MKCOMP(DELTARPM_COMP_XZ, level); break; #ifdef WITH_ZSTD case DRPM_COMP_ZSTD: *deltarpm_comp = DELTARPM_MKCOMP(DELTARPM_COMP_ZSTD, level); break; #endif default: return false; } return true; } void free_deltarpm(struct deltarpm *delta) { struct deltarpm delta_init = {0}; switch (delta->type) { case DRPM_TYPE_STANDARD: rpm_destroy(&delta->head.tgt_rpm); break; case DRPM_TYPE_RPMONLY: free(delta->head.tgt_nevr); break; } free(delta->src_nevr); free(delta->sequence); free(delta->tgt_comp_param); free(delta->offadj_elems); free(delta->tgt_leadsig); free(delta->int_copies); free(delta->ext_copies); free(delta->add_data); if (delta->int_data_as_ptrs) free(delta->int_data.ptrs); else free(delta->int_data.bytes); *delta = delta_init; } drpm-0.5.1/src/drpm_diff.c000066400000000000000000000352171421165313000153500ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2015 Red Hat This program is licensed under the BSD license, read LICENSE.BSD for further information. */ /* Copyright 2004,2005 Michael Schroeder rewritten from bsdiff.c, http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/bsdiff added library interface and hash method, enhanced suffix method. */ /*- * Copyright 2003-2005 Colin Percival * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted providing that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "drpm.h" #include "drpm_private.h" #include #include #define BUFFER_SIZE 4096 struct diff_copy { size_t old_off; size_t old_len; size_t new_off; size_t new_len; }; static int create_diff_copies(const struct diff_copy *, size_t, uint32_t **, uint32_t *, uint32_t **, uint32_t *); static int create_int_data_array(const struct diff_copy *, const unsigned char *, const uint32_t *, uint32_t, const unsigned char ***, uint64_t *); /* Compares and byte sequences (of lengths * and , respectively). * If neither nor are NULL, * creates an add block and stores it in <*add_block_ret> * (and its length in <*add_block_len_ret>). The addblock compression * is determined by and . * Internal data will be created as chunks in an array and stored in * <*int_data_array_ret> (length in <*int_data_array_len_ret>). * External copies will be stored in <*ext_copies_ret> and the number * of external copies shall be in <*ext_copies_count_ret>. * Internal copies will be stored in <*int_copies_ret> and the number * of internal copies shall be in <*int_copies_count_ret>. */ int make_diff(const unsigned char *old, size_t old_len, const unsigned char *new, size_t new_len, const unsigned char ***int_data_array_ret, uint64_t *int_data_len_ret, uint32_t **ext_copies_ret, uint32_t *ext_copies_count_ret, uint32_t **int_copies_ret, uint32_t *int_copies_count_ret, unsigned char **add_block_ret, uint32_t *add_block_len_ret, unsigned short add_block_comp, int add_block_comp_level) { int error; const bool addblk = (add_block_ret != NULL && add_block_len_ret != NULL); size_t add_block_len; struct compstrm *stream; struct diff_copy *diff_copies = NULL; size_t diff_copies_len = 0; //struct sfxsrt *suffix; struct hash *hashtab; size_t old_pos = 0; size_t new_pos = 0; size_t old_pos_prev = 0; size_t new_pos_prev = 0; size_t len = 0; size_t len_forward; size_t len_back; size_t len_overlap; size_t len_split; size_t max_len; size_t i; size_t count; size_t best_count; size_t count_back; size_t count_forward; unsigned char buffer[BUFFER_SIZE]; size_t write_len; if (old == NULL || new == NULL || int_data_array_ret == NULL || int_data_len_ret == NULL || ext_copies_ret == NULL || ext_copies_count_ret == NULL || int_copies_ret == NULL || int_copies_count_ret == NULL) return DRPM_ERR_PROG; if (addblk) *add_block_ret = NULL; //if ((error = sfxsrt_create(&suffix, old, old_len)) != DRPM_ERR_OK) if ((error = hash_create(&hashtab, old, old_len)) != DRPM_ERR_OK) goto cleanup_fail; if (addblk && (error = compstrm_init(&stream, -1, add_block_comp, add_block_comp_level)) != DRPM_ERR_OK) goto cleanup_fail; while (new_pos_prev < new_len) { /* find new match */ //new_pos = sfxsrt_search(suffix, old, old_len, new, new_len, new_pos = hash_search(hashtab, old, old_len, new, new_len, addblk ? old_pos_prev - new_pos_prev : old_len, new_pos + len, &old_pos, &len); /* extend last match forwards */ max_len = MIN(old_len - old_pos_prev, new_pos - new_pos_prev); if (addblk) { len_forward = best_count = count = 0; for (i = 0; i < max_len; ) { if (old[old_pos_prev + i] == new[new_pos_prev + i]) { count++; i++; if (2 * count >= best_count + i) { best_count = 2 * count - i; len_forward = i; } } else { i++; } } } else { // no add block => no mismatches for (i = 0; i < max_len; i++) if (old[old_pos_prev + i] != new[new_pos_prev + i]) break; len_forward = i; } /* extend new match backwards */ if (addblk && new_pos < new_len) { len_back = best_count = count = 0; max_len = MIN(old_pos, new_pos - new_pos_prev); for (i = 1; i <= max_len; i++) { if (old[old_pos - i] == new[new_pos - i]) { count++; if (2 * count >= best_count + i) { best_count = 2 * count - i; len_back = i; } } } } else { // no add block => no mismatches len_back = 0; } /* if extensions overlap, find a good place to split */ if (new_pos_prev + len_forward > new_pos - len_back) { len_split = best_count = count_back = count_forward = 0; len_overlap = (new_pos_prev + len_forward) - (new_pos - len_back); for (i = 0; i < len_overlap; i++) { if (old[old_pos_prev + len_forward - len_overlap + i] == new[new_pos_prev + len_forward - len_overlap + i]) count_forward++; if (old[old_pos - len_back + i] == new[new_pos - len_back + i]) count_back++; if (count_forward > count_back && count_forward - count_back > best_count) { best_count = count_forward - count_back; len_split = i + 1; } } len_forward -= len_overlap - len_split; len_back -= len_split; } /* old_off, old_len |---------> old: B6 00 00 57 5E 71 0C 04 02 17 B5 BE 15 43 A7 B0 00 00 >>>>>>>>>>> <<<<< ============== 00 00 0C F3 -> add block >>>>>>>>>>> <<<<< ============== new: 13 10 24 00 00 63 51 25 C1 B5 1D 15 43 A7 B0 00 4F 92 |---> new_off, new_len >>> - forwards extension of previous match <<< - backwards extension of new match === - new match */ if (!resize32((void **)&diff_copies, diff_copies_len, sizeof(struct diff_copy))) { error = DRPM_ERR_MEMORY; goto cleanup; } diff_copies[diff_copies_len].new_off = new_pos_prev + len_forward; diff_copies[diff_copies_len].new_len = (new_pos - len_back) - (new_pos_prev + len_forward); diff_copies[diff_copies_len].old_off = old_pos_prev; diff_copies[diff_copies_len].old_len = len_forward; diff_copies_len++; if (addblk) { while (len_forward > 0) { write_len = MIN(len_forward, BUFFER_SIZE); for (i = 0; i < write_len; i++) buffer[i] = new[new_pos_prev + i] - old[old_pos_prev + i]; if ((error = compstrm_write(stream, write_len, buffer)) != DRPM_ERR_OK) goto cleanup_fail; old_pos_prev += write_len; new_pos_prev += write_len; len_forward -= write_len; } } old_pos_prev = old_pos - len_back; new_pos_prev = new_pos - len_back; } /* use diff_copies to create outputs */ if ((error = create_diff_copies(diff_copies, diff_copies_len, ext_copies_ret, ext_copies_count_ret, int_copies_ret, int_copies_count_ret)) != DRPM_ERR_OK || (error = create_int_data_array(diff_copies, new, *int_copies_ret, *int_copies_count_ret, int_data_array_ret, int_data_len_ret)) != DRPM_ERR_OK || (addblk && (error = compstrm_finish(stream, add_block_ret, &add_block_len)) != DRPM_ERR_OK)) goto cleanup_fail; if (addblk) *add_block_len_ret = add_block_len; goto cleanup; cleanup_fail: if (addblk) free(*add_block_ret); cleanup: free(diff_copies); //sfxsrt_free(&suffix); hash_free(&hashtab); if (addblk) { if (error == DRPM_ERR_OK) error = compstrm_destroy(&stream); else compstrm_destroy(&stream); } return error; } /* Creates internal and external copies from diff data. */ int create_diff_copies(const struct diff_copy *diff_copies, size_t diff_copies_len, uint32_t **ext_copies_ret, uint32_t *ext_copies_count_ret, uint32_t **int_copies_ret, uint32_t *int_copies_count_ret) { int error; uint32_t *int_copies = NULL; uint32_t int_copies_count = 0; uint32_t *ext_copies = NULL; uint32_t ext_copies_count = 0; size_t new_len; size_t old_len; size_t old_off; uint32_t last_ext_copies_count = 0; size_t offset = 0; for (size_t i = 0; i < diff_copies_len; i++) { new_len = diff_copies[i].new_len; old_len = diff_copies[i].old_len; old_off = diff_copies[i].old_off; if (old_len) { while (true) { if (!resize16((void **)&ext_copies, ext_copies_count * 2, 4)) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } if (old_off > offset && old_off - offset >= (uint32_t)INT32_MIN) { ext_copies[ext_copies_count * 2] = INT32_MAX; ext_copies[ext_copies_count * 2 + 1] = 0; ext_copies_count++; offset += INT32_MAX; continue; } else if (old_off < offset && offset - old_off >= (uint32_t)INT32_MIN) { ext_copies[ext_copies_count * 2] = TWOS_COMPLEMENT(INT32_MAX); ext_copies[ext_copies_count * 2 + 1] = 0; ext_copies_count++; offset -= INT32_MAX; continue; } ext_copies[ext_copies_count * 2] = (int32_t)(old_off - offset); if (old_len >= (uint32_t)INT32_MIN) { ext_copies[ext_copies_count++ * 2 + 1] = INT32_MAX; old_len -= INT32_MAX; old_off = offset += INT32_MAX; continue; } ext_copies[ext_copies_count++ * 2 + 1] = old_len; offset = old_off + old_len; break; } } if (new_len) { while (true) { if (!resize16((void **)&int_copies, int_copies_count * 2, 4)) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } int_copies[int_copies_count * 2] = ext_copies_count - last_ext_copies_count; last_ext_copies_count = ext_copies_count; if (new_len >= (uint32_t)INT32_MIN) { int_copies[int_copies_count++ * 2 + 1] = INT32_MAX; new_len -= INT32_MAX; continue; } int_copies[int_copies_count++ * 2 + 1] = new_len; break; } } } if (ext_copies_count - last_ext_copies_count > 0) { if (!resize16((void **)&int_copies, int_copies_count * 2, 4)) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } int_copies[int_copies_count * 2] = ext_copies_count - last_ext_copies_count; int_copies[int_copies_count * 2 + 1] = 0; int_copies_count++; } *ext_copies_ret = ext_copies; *ext_copies_count_ret = ext_copies_count; *int_copies_ret = int_copies; *int_copies_count_ret = int_copies_count; return DRPM_ERR_OK; cleanup_fail: free(int_copies); free(ext_copies); return error; } /* Constructs internal data from diff data and internal copies. */ int create_int_data_array(const struct diff_copy *diff_copies, //size_t diff_copies_len, const unsigned char *new, const uint32_t *int_copies, uint32_t int_copies_count, const unsigned char ***int_data_array_ret, uint64_t *int_data_len_ret) { const unsigned char **int_data_array; uint64_t int_data_len = 0; size_t todo; size_t offset = 0; size_t left = 0; if ((int_data_array = malloc(int_copies_count * sizeof(unsigned char *))) == NULL) return DRPM_ERR_MEMORY; for (size_t i = 0, j = 0; i < int_copies_count; i++) { todo = int_copies[i * 2 + 1]; if (todo > 0) { while (left == 0) { left = diff_copies[j].new_len; offset = diff_copies[j].new_off; j++; } } int_data_array[i] = new + offset; offset += todo; left -= todo; int_data_len += todo; } *int_data_array_ret = int_data_array; *int_data_len_ret = int_data_len; return DRPM_ERR_OK; } drpm-0.5.1/src/drpm_make.c000066400000000000000000001031031421165313000153430ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2004,2005 Michael Schroeder (mls@suse.de) Copyright (C) 2015 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include #include #include #include #include #define __USE_XOPEN 1 #include #include #include #include #include #define BUFFER_SIZE 4096 #define IN_MULTILIB_DIR(path) (strstr((path), "lib/") != NULL ||\ strstr((path), "lib32/") != NULL ||\ strstr((path), "lib64/") != NULL) #define CPIO_ALLOC_SIZE 65536 #define MAGIC_RPML 0x52504D4C #ifndef RPMFILE_UNPATCHED #define RPMFILE_UNPATCHED (1 << 10) #endif /* sequence compressing RPM file order */ #define SEQ_INIT {.data = NULL, .index = 0, .alloc_len = 0,\ .last_seq_start = 0, .last_seq = -1} #define SEQ_ALLOC_SIZE 32 #define SEQ_BYTE_LEN(index) (((index) + 1) / 2) struct files_seq { unsigned char *data; size_t index; size_t alloc_len; size_t last_seq_start; ssize_t last_seq; }; /* RPM patches */ struct patch_file { char *name; uint16_t mode; uint32_t flags; unsigned char md5[MD5_DIGEST_LENGTH]; }; struct patch_info { char *nevr; struct patch_file *files; size_t file_count; }; struct rpm_patches { struct patch_info rpmprint; struct patch_info patchrpm; }; static int cpio_extend(unsigned char **, size_t *, const void *, size_t); static bool is_unpatched(const struct rpm_patches *, const char *, const char *); static int rpml_get_uint16(int, uint16_t *); static int rpml_get_uint32(int, uint32_t *); static int rpml_get_string(int, char **); static int rpml_get_filename(int, char **, uint32_t *); static int rpml_skip(int, off_t); static int seq_add(struct files_seq *, unsigned); static bool seq_append(struct files_seq *, unsigned); static int seq_final(struct files_seq *, unsigned char **, size_t *); bool seq_append(struct files_seq *seq, unsigned val) { size_t len = 1; unsigned tmp = val; while (tmp >= (1<<3)) { tmp >>= 3; len++; } if (SEQ_BYTE_LEN(seq->index + len) > seq->alloc_len) { if ((seq->data = realloc(seq->data, seq->alloc_len + SEQ_ALLOC_SIZE)) == NULL) return false; seq->alloc_len += SEQ_ALLOC_SIZE; } do { if (seq->index % 2 == 0) seq->data[seq->index / 2] = ((val & 7) | ((len > 1) ? (1<<3) : 0)) << 4; else seq->data[seq->index / 2] |= (val & 7) | ((len > 1) ? (1<<3) : 0); } while (seq->index++, val >>= 3, --len > 0); return true; } /* Encodes next file index. */ int seq_add(struct files_seq *seq, unsigned index) { unsigned val; if ((ssize_t)index == seq->last_seq + 1) { seq->last_seq++; return DRPM_ERR_OK; } val = seq->last_seq - seq->last_seq_start + 1; if (val > 0 && !seq_append(seq, val)) return DRPM_ERR_MEMORY; if (seq->last_seq >= 0 && (ssize_t)index > seq->last_seq + 1) { if (!seq_append(seq, index - (seq->last_seq + 1))) return DRPM_ERR_MEMORY; } else { if (!seq_append(seq, 0) || !seq_append(seq, index)) return DRPM_ERR_MEMORY; } seq->last_seq_start = index; seq->last_seq = index; return DRPM_ERR_OK; } /* Finalizes sequence and writes it to buffer. */ int seq_final(struct files_seq *seq, unsigned char **buffer, size_t *size) { unsigned val = seq->last_seq - seq->last_seq_start + 1; if (val > 0 && !seq_append(seq, val)) return DRPM_ERR_MEMORY; *buffer = seq->data; *size = SEQ_BYTE_LEN(seq->index); return DRPM_ERR_OK; } /* Extends old CPIO buffer. */ int cpio_extend(unsigned char **cpio, size_t *cpio_len, const void *seq, size_t len) { size_t old_cpio_len = *cpio_len; size_t new_cpio_len = old_cpio_len + len; size_t old_padding = PADDING(old_cpio_len, CPIO_ALLOC_SIZE); size_t new_padding = PADDING(new_cpio_len, CPIO_ALLOC_SIZE); unsigned char *cpio_tmp; if (UNSIGNED_SUM_OVERFLOWS(old_cpio_len, len) || UNSIGNED_SUM_OVERFLOWS(new_cpio_len, new_padding)) return DRPM_ERR_OVERFLOW; if (len > old_padding) { if ((cpio_tmp = realloc(*cpio, new_cpio_len + new_padding)) == NULL) return DRPM_ERR_MEMORY; *cpio = cpio_tmp; } memcpy(*cpio + old_cpio_len, seq, len); *cpio_len = new_cpio_len; return DRPM_ERR_OK; } /* Reads CPIO header entry. */ int cpio_header_read(struct cpio_header *cpio_hdr, const char buffer[CPIO_HEADER_SIZE + 1]) { ssize_t ino_ret; ssize_t mode_ret; ssize_t uid_ret; ssize_t gid_ret; ssize_t nlink_ret; ssize_t mtime_ret; ssize_t filesize_ret; ssize_t devmajor_ret; ssize_t devminor_ret; ssize_t rdevmajor_ret; ssize_t rdevminor_ret; ssize_t namesize_ret; if (strncmp(buffer, CPIO_MAGIC, 6) != 0 || (ino_ret = parse_hexnum((buffer += 6), 8)) < 0 || (mode_ret = parse_hexnum((buffer += 8), 8)) < 0 || (uid_ret = parse_hexnum((buffer += 8), 8)) < 0 || (gid_ret = parse_hexnum((buffer += 8), 8)) < 0 || (nlink_ret = parse_hexnum((buffer += 8), 8)) < 0 || (mtime_ret = parse_hexnum((buffer += 8), 8)) < 0 || (filesize_ret = parse_hexnum((buffer += 8), 8)) < 0 || (devmajor_ret = parse_hexnum((buffer += 8), 8)) < 0 || (devminor_ret = parse_hexnum((buffer += 8), 8)) < 0 || (rdevmajor_ret = parse_hexnum((buffer += 8), 8)) < 0 || (rdevminor_ret = parse_hexnum((buffer += 8), 8)) < 0 || (namesize_ret = parse_hexnum((buffer += 8), 8)) < 0) return DRPM_ERR_FORMAT; cpio_hdr->ino = ino_ret; cpio_hdr->mode = mode_ret; cpio_hdr->uid = uid_ret; cpio_hdr->gid = gid_ret; cpio_hdr->nlink = nlink_ret; cpio_hdr->mtime = mtime_ret; cpio_hdr->filesize = filesize_ret; cpio_hdr->devmajor = devmajor_ret; cpio_hdr->devminor = devminor_ret; cpio_hdr->rdevmajor = rdevmajor_ret; cpio_hdr->rdevminor = rdevminor_ret; cpio_hdr->namesize = namesize_ret; return DRPM_ERR_OK; } /* Writes CPIO header entry. */ void cpio_header_write(const struct cpio_header *cpio_hdr, char buffer[CPIO_HEADER_SIZE + 1]) { sprintf(buffer, CPIO_MAGIC "%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", cpio_hdr->ino, cpio_hdr->mode, cpio_hdr->uid, cpio_hdr->gid, cpio_hdr->nlink, cpio_hdr->mtime, cpio_hdr->filesize, cpio_hdr->devmajor, cpio_hdr->devminor, cpio_hdr->rdevmajor, cpio_hdr->rdevminor, cpio_hdr->namesize, 0); } /* For standard DeltaRPMs, the old RPM's CPIO archive is parsed based * on file metadata found in the RPM header. An altered CPIO archive * is created: e.g. some files may be skipped, a symlink's file content * is replaced with the name of its target, etc. * This is also where the deltarpm sequence is created, consisting of * the MD5 sum for the files followed by the encoded order of the files * in the RPM header. * Additionally (for V3 DeltaRPMs), an array of offset adjustment * elements is created, which stores offset differences between * entries in the original and altered CPIO archives. */ int parse_cpio_from_rpm_filedata(struct rpm *rpm_file, unsigned char **cpio_ret, size_t *cpio_len_ret, unsigned char **sequence_ret, uint32_t *sequence_len_ret, uint32_t **offadjs_ret, uint32_t *offadjn_ret, const struct rpm_patches *patches) { int error = DRPM_ERR_OK; struct file_info *files = NULL; size_t file_count; bool file_colors; struct file_info file = {0}; unsigned files_index; unsigned short digest_algo; unsigned char digest[MAX(MD5_DIGEST_LENGTH, SHA256_DIGEST_LENGTH)] = {0}; unsigned char *cpio = NULL; size_t cpio_len = 0; size_t cpio_pos = 0; struct cpio_header cpio_hdr; const struct cpio_header cpio_hdr_init = {0}; char cpio_buffer[CPIO_HEADER_SIZE + 1]; bool offadj; uint32_t *offadjs = NULL; uint32_t offadjn = 0; size_t cpio_len_prev = 0; uint64_t offset; size_t c_filesize; size_t c_namesize; char *name; size_t name_len; char *name_buffer = NULL; char *name_buffer_tmp; size_t name_buffer_len = 0; unsigned char *sequence = NULL; uint32_t sequence_len; MD5_CTX seq_md5; unsigned char seq_md5_digest[MD5_DIGEST_LENGTH]; struct files_seq seq = SEQ_INIT; unsigned char *seq_files = NULL; size_t seq_files_len; unsigned short padding_bytes; size_t data_len; size_t read_len; char buffer[BUFFER_SIZE]; bool skip; if (rpm_file == NULL || cpio_ret == NULL || cpio_len_ret == NULL || sequence_ret == NULL || sequence_len_ret == NULL) return DRPM_ERR_PROG; *cpio_ret = NULL; *cpio_len_ret = 0; *sequence_ret = NULL; *sequence_len_ret = 0; if ((offadj = (offadjs_ret != NULL && offadjn_ret != NULL))) { *offadjs_ret = NULL; *offadjn_ret = 0; } if (MD5_Init(&seq_md5) != 1) return DRPM_ERR_OTHER; if ((error = rpm_get_file_info(rpm_file, &files, &file_count, &file_colors)) != DRPM_ERR_OK) return error; if ((error = rpm_get_digest_algo(rpm_file, &digest_algo)) != DRPM_ERR_OK) goto cleanup_fail; rpm_archive_rewind(rpm_file); while (true) { /* reading CPIO header and pathname */ if ((error = rpm_archive_read_chunk(rpm_file, cpio_buffer, CPIO_HEADER_SIZE)) != DRPM_ERR_OK) goto cleanup_fail; if ((error = cpio_header_read(&cpio_hdr, cpio_buffer)) != DRPM_ERR_OK) goto cleanup_fail; c_filesize = cpio_hdr.filesize; c_namesize = cpio_hdr.namesize; if (c_namesize > name_buffer_len) { if ((name_buffer_tmp = realloc(name_buffer, c_namesize)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } name_buffer = name_buffer_tmp; name_buffer_len = c_namesize; } if ((error = rpm_archive_read_chunk(rpm_file, name_buffer, c_namesize)) != DRPM_ERR_OK) goto cleanup_fail; name = name_buffer; name[c_namesize - 1] = '\0'; /* end of archive? */ if (strcmp(name, CPIO_TRAILER) == 0) break; if (strncmp(name, "./", 2) == 0) name += 2; name_len = strlen(name) + 1; padding_bytes = CPIO_PADDING(CPIO_HEADER_SIZE + c_namesize); if ((error = rpm_archive_read_chunk(rpm_file, NULL, padding_bytes)) != DRPM_ERR_OK) goto cleanup_fail; const size_t cpio_hdrname_len = CPIO_HEADER_SIZE + c_namesize + padding_bytes; size_t cpio_pos_before_hdrname = cpio_pos; cpio_pos += cpio_hdrname_len; /* looking up file in RPM header, skipping if not found * or if it's a regular file and one of the following occur: * - file size mismatch between CPIO and RPM headers * - bad file flags * - bad verify flags * - colored file in non-multilib dir */ for (files_index = 0; files_index < file_count; files_index++) { if (strcmp(name, files[files_index].name + ((files[files_index].name[0] == '/') ? 1 : 0)) == 0) break; } if (!(skip = (files_index == file_count))) { file = files[files_index]; cpio_hdr = cpio_hdr_init; if (patches != NULL && S_ISREG(file.mode) && is_unpatched(patches, name, file.md5)) { skip = true; } else if (S_ISREG(file.mode)) { skip = (c_filesize != file.size) || ((file.flags & (RPMFILE_CONFIG | RPMFILE_MISSINGOK | RPMFILE_GHOST)) != 0) || ((file.verify & RPMVERIFY_MD5) == 0 || (file.verify & RPMVERIFY_FILESIZE) == 0) || (file_colors && (file.color & (RPMFC_ELF32 | RPMFC_ELF64)) != 0 && !IN_MULTILIB_DIR(name)); cpio_hdr.filesize = file.size; } else if (S_ISLNK(file.mode)) { cpio_hdr.filesize = strlen(file.linkto); } else if (S_ISBLK(file.mode) || S_ISCHR(file.mode)) { cpio_hdr.rdevmajor = major(file.rdev); cpio_hdr.rdevminor = minor(file.rdev); } } if (!skip) { cpio_hdr.mode = file.mode; cpio_hdr.namesize = name_len + 2; // including "./" prefix cpio_hdr.nlink = 1; /* offset adjustment */ if (cpio_len != cpio_pos_before_hdrname) { if (offadj) { while (true) { if (!resize32((void **)&offadjs, offadjn * 2, 4)) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } if ((uint32_t)(cpio_len - cpio_len_prev) >= (uint32_t)INT32_MIN) { offadjs[offadjn * 2] = INT32_MAX; offadjs[offadjn * 2 + 1] = 0; offadjn++; cpio_len_prev += INT32_MAX; continue; } offadjs[offadjn * 2] = cpio_len - cpio_len_prev; cpio_len_prev = cpio_len; if (cpio_pos_before_hdrname < cpio_len) { offset = cpio_len - cpio_pos_before_hdrname; if (offset >= (uint32_t)INT32_MIN) { offadjs[offadjn++ * 2 + 1] = TWOS_COMPLEMENT((uint32_t)INT32_MAX); cpio_pos_before_hdrname += INT32_MAX; continue; } offadjs[offadjn++ * 2 + 1] = TWOS_COMPLEMENT(offset); } else { offset = cpio_pos_before_hdrname - cpio_len; if (offset >= (uint32_t)INT32_MIN) { offadjs[offadjn++ * 2 + 1] = INT32_MAX; cpio_pos_before_hdrname -= INT32_MAX; continue; } offadjs[offadjn++ * 2 + 1] = offset; } break; } } cpio_pos = cpio_len + cpio_hdrname_len; } /* adding new entry to cpio, updating MD5 */ cpio_header_write(&cpio_hdr, cpio_buffer); if ((error = cpio_extend(&cpio, &cpio_len, cpio_buffer, CPIO_HEADER_SIZE)) != DRPM_ERR_OK || (error = cpio_extend(&cpio, &cpio_len, "./", 2)) != DRPM_ERR_OK || (error = cpio_extend(&cpio, &cpio_len, name, name_len)) != DRPM_ERR_OK || (error = cpio_extend(&cpio, &cpio_len, "\0\0\0", CPIO_PADDING(CPIO_HEADER_SIZE + cpio_hdr.namesize))) != DRPM_ERR_OK) goto cleanup_fail; if (MD5_Update(&seq_md5, name, name_len) != 1 || md5_update_be32(&seq_md5, cpio_hdr.mode) != 1 || md5_update_be32(&seq_md5, cpio_hdr.filesize) != 1 || md5_update_be32(&seq_md5, makedev(cpio_hdr.rdevmajor, cpio_hdr.rdevminor)) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } if (S_ISLNK(file.mode)) { if ((error = cpio_extend(&cpio, &cpio_len, file.linkto, cpio_hdr.filesize)) != DRPM_ERR_OK || (error = cpio_extend(&cpio, &cpio_len, "\0\0\0", CPIO_PADDING(cpio_hdr.filesize))) != DRPM_ERR_OK) goto cleanup_fail; if (MD5_Update(&seq_md5, file.linkto, cpio_hdr.filesize + 1) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } } else if (S_ISREG(file.mode) && cpio_hdr.filesize) { switch (digest_algo) { case DIGESTALGO_MD5: if (!parse_md5(digest, file.md5)) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } if (MD5_Update(&seq_md5, digest, MD5_DIGEST_LENGTH) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } break; case DIGESTALGO_SHA256: if (!parse_sha256(digest, file.md5)) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } if (MD5_Update(&seq_md5, digest, SHA256_DIGEST_LENGTH) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } break; } } /* adding file index to sequence */ if ((error = seq_add(&seq, files_index)) != DRPM_ERR_OK) goto cleanup_fail; } /* reading file data and copying to cpio */ data_len = c_filesize; while (data_len > 0) { read_len = MIN(data_len, BUFFER_SIZE); if ((error = rpm_archive_read_chunk(rpm_file, buffer, read_len)) != DRPM_ERR_OK) goto cleanup_fail; cpio_pos += read_len; /* Do not add file data of skipped files or symlinks to cpio */ if (!S_ISLNK(file.mode) && !skip && (error = cpio_extend(&cpio, &cpio_len, buffer, read_len)) != DRPM_ERR_OK) goto cleanup_fail; data_len -= read_len; } if ((padding_bytes = CPIO_PADDING(c_filesize)) > 0) { if ((error = rpm_archive_read_chunk(rpm_file, NULL, padding_bytes)) != DRPM_ERR_OK) goto cleanup_fail; cpio_pos += padding_bytes; if (!S_ISLNK(file.mode) && !skip && (error = cpio_extend(&cpio, &cpio_len, "\0\0\0", padding_bytes)) != DRPM_ERR_OK) goto cleanup_fail; } } /* writing CPIO trailer */ cpio_hdr = cpio_hdr_init; cpio_hdr.nlink = 1; cpio_hdr.namesize = strlen(CPIO_TRAILER) + 1; cpio_header_write(&cpio_hdr, cpio_buffer); if ((error = cpio_extend(&cpio, &cpio_len, cpio_buffer, CPIO_HEADER_SIZE)) != DRPM_ERR_OK || (error = cpio_extend(&cpio, &cpio_len, CPIO_TRAILER, cpio_hdr.namesize)) != DRPM_ERR_OK || (error = cpio_extend(&cpio, &cpio_len, "\0\0\0", CPIO_PADDING(CPIO_HEADER_SIZE + cpio_hdr.namesize))) != DRPM_ERR_OK) goto cleanup_fail; /* completing sequence */ if ((error = seq_final(&seq, &seq_files, &seq_files_len)) != DRPM_ERR_OK) goto cleanup_fail; if (MD5_Final(seq_md5_digest, &seq_md5) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } sequence_len = MD5_DIGEST_LENGTH + seq_files_len; if ((sequence = malloc(sequence_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } memcpy(sequence, seq_md5_digest, MD5_DIGEST_LENGTH); memcpy(sequence + MD5_DIGEST_LENGTH, seq_files, seq_files_len); *cpio_ret = cpio; *cpio_len_ret = cpio_len; *sequence_ret = sequence; *sequence_len_ret = sequence_len; if (offadj) { *offadjs_ret = offadjs; *offadjn_ret = offadjn; } goto cleanup; cleanup_fail: free(cpio); free(sequence); if (offadj) free(offadjs); cleanup: for (size_t i = 0; i < file_count; i++) { free(files[i].name); free(files[i].md5); free(files[i].linkto); } free(files); free(name_buffer); free(seq_files); return error; } /* RPM patches */ int rpml_get_uint16(int filedesc, uint16_t *ret) { unsigned char buf[2]; if (read(filedesc, buf, 2) != 2) return DRPM_ERR_FORMAT; if (ret != NULL) *ret = parse_be16(buf); return DRPM_ERR_OK; } int rpml_get_uint32(int filedesc, uint32_t *ret) { unsigned char buf[4]; if (read(filedesc, buf, 4) != 4) return DRPM_ERR_FORMAT; if (ret != NULL) *ret = parse_be32(buf); return DRPM_ERR_OK; } int rpml_get_string(int filedesc, char **ret) { uint8_t len; if (read(filedesc, &len, 1) != 1) return DRPM_ERR_FORMAT; if (ret != NULL) { if ((*ret = malloc(len + 1)) == NULL) return DRPM_ERR_MEMORY; if (read(filedesc, *ret, len) != len) { free(*ret); return DRPM_ERR_FORMAT; } (*ret)[len] = '\0'; } return DRPM_ERR_OK; } int rpml_get_filename(int filedesc, char **filename_ret, uint32_t *filename_len_ret) { int error; uint8_t off; uint16_t len; uint8_t buf[2]; char *filename; uint32_t filename_len; uint32_t new_filename_len; if (filename_ret == NULL || filename_len_ret == NULL) return DRPM_ERR_PROG; filename = *filename_ret; filename_len = *filename_len_ret; if (read(filedesc, buf, 2) != 2) return DRPM_ERR_FORMAT; off = buf[0]; if (buf[1] == 0xFF) { if ((error = rpml_get_uint16(filedesc, &len)) != DRPM_ERR_OK) return error; } else { len = buf[1]; } new_filename_len = off + len + 1; if (new_filename_len > filename_len) { if ((filename = realloc(filename, new_filename_len)) == NULL) return DRPM_ERR_MEMORY; filename_len = new_filename_len; } if (read(filedesc, filename + off, len) != len) return DRPM_ERR_FORMAT; filename[off + len] = '\0'; *filename_ret = filename; *filename_len_ret = filename_len; return DRPM_ERR_OK; } int rpml_skip(int filedesc, off_t len) { return lseek(filedesc, len, SEEK_CUR) != (off_t)-1 ? DRPM_ERR_OK : DRPM_ERR_IO; } int read_rpmlist(int filedesc, struct patch_info *patch, bool skip_magic) { int error = DRPM_ERR_OK; char *filename = NULL; uint32_t filename_len; const char *fname; uint32_t magic; char *name = NULL; char *evr = NULL; uint16_t patches_count; uint32_t files_count; uint8_t num; uint8_t num2; unsigned char buf[4]; uint8_t read_bytes; if (!skip_magic) { if ((error = rpml_get_uint32(filedesc, &magic)) != DRPM_ERR_OK) return error; if (magic != MAGIC_RPML) return DRPM_ERR_FORMAT; } if ((error = rpml_get_string(filedesc, &name)) != DRPM_ERR_OK || (error = rpml_get_string(filedesc, &evr)) != DRPM_ERR_OK) goto cleanup; if ((patch->nevr = malloc(strlen(name) + strlen(evr) + 2)) == NULL) { error = DRPM_ERR_FORMAT; goto cleanup; } sprintf(patch->nevr, "%s-%s", name, evr); if ((error = rpml_get_string(filedesc, NULL)) != DRPM_ERR_OK || // build host (error = rpml_get_uint32(filedesc, NULL)) != DRPM_ERR_OK || // build time (error = rpml_get_uint16(filedesc, &patches_count)) != DRPM_ERR_OK) goto cleanup; if (patches_count > 0) { for (uint16_t i = 0; i < patches_count; i++) if ((error = rpml_get_string(filedesc, NULL)) != DRPM_ERR_OK) goto cleanup; if ((error = rpml_get_uint32(filedesc, &files_count)) != DRPM_ERR_OK) goto cleanup; for (uint32_t i = 0; i < files_count; i++) { if (!resize16((void **)&patch->files, patch->file_count, sizeof(struct patch_file))) { error = DRPM_ERR_MEMORY; goto cleanup; } if ((error = rpml_get_filename(filedesc, &filename, &filename_len))) goto cleanup; if ((patch->files[patch->file_count].name = malloc(strlen(filename) + 1)) == NULL) { error = DRPM_ERR_MEMORY; } strcpy(patch->files[patch->file_count].name, filename); patch->files[patch->file_count].mode = S_IFREG; patch->files[patch->file_count].flags = RPMFILE_UNPATCHED; memset(patch->files[patch->file_count].md5, 0, MD5_DIGEST_LENGTH); patch->file_count++; } } while (true) { if ((error = rpml_get_filename(filedesc, &filename, &filename_len)) != DRPM_ERR_OK) goto cleanup; if (strlen(filename) == 0) break; if (!resize16((void **)&patch->files, patch->file_count, sizeof(struct patch_file))) { error = DRPM_ERR_MEMORY; goto cleanup; } fname = (strncmp(filename, "./", 2) == 0) ? filename + 2 : filename; if ((patch->files[patch->file_count].name = malloc(strlen(fname) + 1)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } patch->files[patch->file_count].flags = RPMFILE_NONE; memset(patch->files[patch->file_count].md5, 0, MD5_DIGEST_LENGTH); if ((error = rpml_get_uint16(filedesc, &patch->files[patch->file_count].mode)) != DRPM_ERR_OK) goto cleanup; if (patch->files[patch->file_count].mode != 0) { if (read(filedesc, &num, 1) != 1) { error = DRPM_ERR_FORMAT; goto cleanup; } if (num == 0xFF) { if (read(filedesc, &num2, 1) != 1 || read(filedesc, &num, 1) != 1) { error = DRPM_ERR_FORMAT; goto cleanup; } if (((num2 > 0) && (error = rpml_skip(filedesc, num2 + 1)) != DRPM_ERR_OK) || ((num & 0xFC) && (error = rpml_skip(filedesc, (num >> 2 & 0x3F) + 1)) != DRPM_ERR_OK)) goto cleanup; } else { if (((num & 0xE0) && (error = rpml_skip(filedesc, (num >> 5 & 7) + 1)) != DRPM_ERR_OK) || ((num & 0x1C) && (error = rpml_skip(filedesc, (num >> 2 & 7) + 1)) != DRPM_ERR_OK)) goto cleanup; } if ((S_ISCHR(patch->files[patch->file_count].mode) || S_ISBLK(patch->files[patch->file_count].mode)) && (error = rpml_get_uint32(filedesc, NULL)) != DRPM_ERR_OK) // rdev goto cleanup; if (S_ISREG(patch->files[patch->file_count].mode) || S_ISLNK(patch->files[patch->file_count].mode)) { read_bytes = (num % 4) + 1; memset(buf, 0, 4); if (read(filedesc, buf + (4 - read_bytes), read_bytes) != read_bytes && parse_be32(buf) > 0 && read(filedesc, patch->files[patch->file_count].md5, MD5_DIGEST_LENGTH) != MD5_DIGEST_LENGTH) { error = DRPM_ERR_FORMAT; goto cleanup; } } } patch->file_count++; } cleanup: free(filename); free(name); free(evr); return error; } /* Reads RPM patches. */ int patches_read(const char *oldrpmprint, const char *oldpatchrpm, struct rpm_patches **patches) { int error = DRPM_ERR_OK; int filedesc; struct patch_info *rpmprint; struct patch_info *patchrpm; uint32_t magic; struct rpm *rpmst = NULL; struct file_info *files; size_t file_count; char *fname; if (patches == NULL) return DRPM_ERR_PROG; /* no patching */ if (oldrpmprint == NULL || oldpatchrpm == NULL) { *patches = NULL; return DRPM_ERR_OK; } if ((*patches = malloc(sizeof(struct rpm_patches))) == NULL) return DRPM_ERR_MEMORY; rpmprint = &(*patches)->rpmprint; patchrpm = &(*patches)->patchrpm; if ((filedesc = open(oldpatchrpm, O_RDONLY)) < 0) { error = DRPM_ERR_IO; goto cleanup_fail; } if ((error = read_rpmlist(filedesc, patchrpm, false)) != DRPM_ERR_OK) goto cleanup_fail; close(filedesc); if ((filedesc = open(oldrpmprint, O_RDONLY)) < 0) { error = DRPM_ERR_IO; goto cleanup_fail; } if ((error = read_be32(filedesc, &magic)) != DRPM_ERR_OK) goto cleanup_fail; switch (magic) { case MAGIC_RPM: if ((error = rpm_read(&rpmst, oldrpmprint, RPM_ARCHIVE_DONT_READ, NULL, NULL, NULL)) != DRPM_ERR_OK || (error = rpm_get_nevr(rpmst, &rpmprint->nevr)) != DRPM_ERR_OK || (error = rpm_get_file_info(rpmst, &files, &file_count, NULL)) != DRPM_ERR_OK) goto cleanup_fail; if ((rpmprint->files = malloc(file_count)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } rpmprint->file_count = file_count; for (size_t i = 0; i < file_count; i++) { fname = files[i].name; if (fname[0] == '/') fname++; if ((rpmprint->files[i].name = malloc(strlen(fname) + 1)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } strcpy(rpmprint->files[i].name, fname); rpmprint->files[i].mode = files[i].mode; rpmprint->files[i].flags = files[i].flags; if (!parse_md5(rpmprint->files[i].md5, files[i].md5)) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } } break; case MAGIC_RPML: if ((error = read_rpmlist(filedesc, rpmprint, true)) != DRPM_ERR_OK) goto cleanup_fail; break; default: error = DRPM_ERR_FORMAT; goto cleanup_fail; } goto cleanup; cleanup_fail: patches_destroy(patches); cleanup: close(filedesc); return error; } int patches_destroy(struct rpm_patches **patches) { if (patches == NULL || *patches == NULL) return DRPM_ERR_PROG; for (size_t i = 0; i < (*patches)->rpmprint.file_count; i++) free((*patches)->rpmprint.files[i].name); free((*patches)->rpmprint.files); free((*patches)->rpmprint.nevr); for (size_t i = 0; i < (*patches)->patchrpm.file_count; i++) free((*patches)->patchrpm.files[i].name); free((*patches)->patchrpm.files); free((*patches)->patchrpm.nevr); free(*patches); *patches = NULL; return DRPM_ERR_OK; } /* Checks that patches' NEVRs match the old RPMs. */ int patches_check_nevr(const struct rpm_patches *patches, const char *nevr) { if (patches == NULL) return DRPM_ERR_PROG; return (strcmp(patches->rpmprint.nevr, nevr) == 0 && strcmp(patches->patchrpm.nevr, nevr) == 0) ? DRPM_ERR_OK : DRPM_ERR_ARGS; } /* Checks if the file is unpatched. */ bool is_unpatched(const struct rpm_patches *patches, const char *name, const char rpm_md5[MD5_DIGEST_LENGTH * 2 + 1]) { size_t i; char patch_md5[MD5_DIGEST_LENGTH * 2 + 1]; for (i = 0; i < patches->rpmprint.file_count; i++) if (strcmp(name, patches->rpmprint.files[i].name) == 0) break; if (i == patches->rpmprint.file_count || !(patches->rpmprint.files[i].flags & RPMFILE_UNPATCHED)) return false; for (i = 0; i < patches->patchrpm.file_count; i++) if (strcmp(name, patches->patchrpm.files[i].name) == 0) break; if (i == patches->patchrpm.file_count) // shouldn't happen return true; dump_hex(patch_md5, patches->patchrpm.files[i].md5, MD5_DIGEST_LENGTH); return (strcmp(rpm_md5, patch_md5) != 0); } /* In the case of an rpm-only identity deltarpm, since identity deltarpms * only read one RPM file and rpm-only deltarpms take the RPMs' CPIO * archives "as is" (i.e. they are not altered based on file metadata), * there is nothing to diff. */ int fill_nodiff_deltarpm(struct deltarpm *delta, const char *rpm_filename, bool comp_not_set) { struct rpm *solo_rpm; char *nevr = NULL; int error = DRPM_ERR_OK; if (comp_not_set) { delta->comp = DRPM_COMP_GZIP; delta->comp_level = DRPM_COMP_LEVEL_DEFAULT; } delta->tgt_comp = DRPM_COMP_NONE; if ((delta->sequence = malloc(MD5_DIGEST_LENGTH)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } delta->sequence_len = MD5_DIGEST_LENGTH; if ((error = rpm_read(&solo_rpm, rpm_filename, RPM_ARCHIVE_READ_UNCOMP, NULL, delta->sequence, delta->tgt_md5)) != DRPM_ERR_OK || (error = rpm_fetch_lead_and_signature(solo_rpm, &delta->tgt_leadsig, &delta->tgt_leadsig_len)) != DRPM_ERR_OK || (error = rpm_get_nevr(solo_rpm, &nevr)) != DRPM_ERR_OK) goto cleanup; if ((delta->src_nevr = malloc(strlen(nevr) + 1)) == NULL || (delta->head.tgt_nevr = malloc(strlen(nevr) + 1)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } strcpy(delta->src_nevr, nevr); strcpy(delta->head.tgt_nevr, nevr); delta->tgt_size = rpm_size_full(solo_rpm); delta->tgt_header_len = rpm_size_header(solo_rpm); cleanup: free(nevr); rpm_destroy(&solo_rpm); return error; } drpm-0.5.1/src/drpm_options.c000066400000000000000000000165601421165313000161330ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2016 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include int drpm_make_options_init(struct drpm_make_options **opts) { const struct drpm_make_options init = {0}; if (opts == NULL) return DRPM_ERR_ARGS; if ((*opts = malloc(sizeof(struct drpm_make_options))) == NULL) return DRPM_ERR_MEMORY; **opts = init; drpm_make_options_defaults(*opts); return DRPM_ERR_OK; } int drpm_make_options_destroy(struct drpm_make_options **opts) { if (opts == NULL) return DRPM_ERR_ARGS; free((*opts)->seqfile); free((*opts)->oldrpmprint); free((*opts)->oldpatchrpm); free(*opts); *opts = NULL; return DRPM_ERR_OK; } int drpm_make_options_defaults(struct drpm_make_options *opts) { if (opts == NULL) return DRPM_ERR_ARGS; free(opts->seqfile); free(opts->oldrpmprint); free(opts->oldpatchrpm); opts->rpm_only = false; opts->version = 3; opts->comp_from_rpm = true; opts->comp = USHRT_MAX; opts->comp_level = DRPM_COMP_LEVEL_DEFAULT; opts->addblk = true; opts->addblk_comp = DRPM_COMP_BZIP2; opts->addblk_comp_level = DRPM_COMP_LEVEL_DEFAULT; opts->seqfile = NULL; opts->oldrpmprint = NULL; opts->oldpatchrpm = NULL; opts->mbytes = 0; return DRPM_ERR_OK; } int drpm_make_options_copy(struct drpm_make_options *opts_dst, const struct drpm_make_options *opts_src) { if (opts_dst == NULL || opts_src == NULL) return DRPM_ERR_ARGS; opts_dst->rpm_only = opts_src->rpm_only; opts_dst->version = opts_src->version; opts_dst->comp_from_rpm = opts_src->comp_from_rpm; opts_dst->comp = opts_src->comp; opts_dst->comp_level = opts_src->comp_level; opts_dst->addblk = opts_src->addblk; opts_dst->addblk_comp = opts_src->addblk_comp; opts_dst->addblk_comp_level = opts_src->addblk_comp_level; opts_dst->mbytes = opts_src->mbytes; free(opts_dst->seqfile); free(opts_dst->oldrpmprint); free(opts_dst->oldpatchrpm); opts_dst->seqfile = NULL; opts_dst->oldrpmprint = NULL; opts_dst->oldpatchrpm = NULL; if (opts_src->seqfile != NULL) { if ((opts_dst->seqfile = malloc(strlen(opts_src->seqfile) + 1)) == NULL) return DRPM_ERR_OK; strcpy(opts_dst->seqfile, opts_src->seqfile); } if (opts_src->oldrpmprint != NULL) { if ((opts_dst->oldrpmprint = malloc(strlen(opts_src->oldrpmprint) + 1)) == NULL) return DRPM_ERR_OK; strcpy(opts_dst->oldrpmprint, opts_src->oldrpmprint); } if (opts_src->oldpatchrpm != NULL) { if ((opts_dst->oldpatchrpm = malloc(strlen(opts_src->oldpatchrpm) + 1)) == NULL) return DRPM_ERR_OK; strcpy(opts_dst->oldpatchrpm, opts_src->oldpatchrpm); } return DRPM_ERR_OK; } int drpm_make_options_set_type(struct drpm_make_options *opts, unsigned short type) { if (opts == NULL) return DRPM_ERR_ARGS; switch (type) { case DRPM_TYPE_STANDARD: opts->rpm_only = false; break; case DRPM_TYPE_RPMONLY: opts->rpm_only = true; break; default: return DRPM_ERR_ARGS; } return DRPM_ERR_OK; } int drpm_make_options_set_version(struct drpm_make_options *opts, unsigned short version) { if (opts == NULL || version < 1 || version > 3) return DRPM_ERR_ARGS; opts->version = version; return DRPM_ERR_OK; } int drpm_make_options_set_delta_comp(struct drpm_make_options *opts, unsigned short comp, unsigned short level) { if (opts == NULL || (level != DRPM_COMP_LEVEL_DEFAULT && (level < 1 || level > 9))) return DRPM_ERR_ARGS; switch (comp) { case DRPM_COMP_NONE: case DRPM_COMP_GZIP: case DRPM_COMP_BZIP2: case DRPM_COMP_LZMA: case DRPM_COMP_XZ: case DRPM_COMP_LZIP: #ifdef WITH_ZSTD case DRPM_COMP_ZSTD: #endif opts->comp_from_rpm = false; opts->comp = comp; opts->comp_level = level; break; default: return DRPM_ERR_ARGS; } return DRPM_ERR_OK; } int drpm_make_options_get_delta_comp_from_rpm(struct drpm_make_options *opts) { if (opts == NULL) return DRPM_ERR_ARGS; opts->comp_from_rpm = true; return DRPM_ERR_OK; } int drpm_make_options_forbid_addblk(struct drpm_make_options *opts) { if (opts == NULL) return DRPM_ERR_ARGS; opts->addblk = false; return DRPM_ERR_OK; } int drpm_make_options_set_addblk_comp(struct drpm_make_options *opts, unsigned short comp, unsigned short level) { if (opts == NULL || (level != DRPM_COMP_LEVEL_DEFAULT && (level < 1 || level > 9))) return DRPM_ERR_ARGS; switch (comp) { case DRPM_COMP_NONE: case DRPM_COMP_GZIP: case DRPM_COMP_BZIP2: case DRPM_COMP_LZMA: case DRPM_COMP_XZ: case DRPM_COMP_LZIP: #ifdef WITH_ZSTD case DRPM_COMP_ZSTD: #endif opts->addblk = true; opts->addblk_comp = comp; opts->addblk_comp_level = level; break; default: return DRPM_ERR_ARGS; } return DRPM_ERR_OK; } int drpm_make_options_set_seqfile(struct drpm_make_options *opts, const char *seqfile) { char *tmp; if (opts == NULL) return DRPM_ERR_ARGS; if (seqfile == NULL) { free(opts->seqfile); opts->seqfile = NULL; } else { if (opts->seqfile == NULL || strlen(opts->seqfile) < strlen(seqfile)) { if ((tmp = realloc(opts->seqfile, strlen(seqfile) + 1)) == NULL) return DRPM_ERR_MEMORY; opts->seqfile = tmp; } strcpy(opts->seqfile, seqfile); } return DRPM_ERR_OK; } int drpm_make_options_add_patches(struct drpm_make_options *opts, const char *oldrpmprint, const char *oldpatchrpm) { char *tmp; if (opts == NULL || oldrpmprint == NULL || oldpatchrpm == NULL) return DRPM_ERR_ARGS; if (opts->oldrpmprint == NULL || strlen(opts->oldrpmprint) < strlen(oldrpmprint)) { if ((tmp = realloc(opts->oldrpmprint, strlen(oldrpmprint) + 1)) == NULL) return DRPM_ERR_MEMORY; opts->oldrpmprint = tmp; } if (opts->oldpatchrpm == NULL || strlen(opts->oldpatchrpm) < strlen(oldpatchrpm)) { if ((tmp = realloc(opts->oldpatchrpm, strlen(oldpatchrpm) + 1)) == NULL) return DRPM_ERR_MEMORY; opts->oldpatchrpm = tmp; } strcpy(opts->oldrpmprint, oldrpmprint); strcpy(opts->oldpatchrpm, oldpatchrpm); return DRPM_ERR_OK; } // TODO: not yet used int drpm_make_options_set_memlimit(struct drpm_make_options *opts, unsigned mbytes) { if (opts == NULL) return DRPM_ERR_ARGS; opts->mbytes = mbytes; return DRPM_ERR_OK; } drpm-0.5.1/src/drpm_private.h000066400000000000000000000251671421165313000161220ustar00rootroot00000000000000/* Authors: Pavel Tobias Matej Chalk Copyright (C) 2014 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifndef _DRPM_PRIVATE_H_ #define _DRPM_PRIVATE_H_ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #define CHUNK_SIZE 1024 #define CREAT_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) #define DIGESTALGO_MD5 0 #define DIGESTALGO_SHA256 1 #define RPM_PAYLOAD_FORMAT_DRPM 0 #define RPM_PAYLOAD_FORMAT_CPIO 1 #define RPM_PAYLOAD_FORMAT_XAR 2 #define RPM_ARCHIVE_DONT_READ 0 #define RPM_ARCHIVE_READ_UNCOMP 1 #define RPM_ARCHIVE_READ_DECOMP 2 #define MIN(x,y) (((x) < (y)) ? (x) : (y)) #define MAX(x,y) (((x) > (y)) ? (x) : (y)) #define TWOS_COMPLEMENT(x) (~(x) + 1) #define UNSIGNED_SUM_OVERFLOWS(x,y) ((x) + (y) < (y)) #define PADDING(offset, align) ((((align) - ((offset) % (align))) % (align))) #define MAGIC_RPM 0xEDABEEDB #define RPM_LEADSIG_MIN_LEN 112 /* 96B rpmlead + 16B signature intro */ #define CPIO_MAGIC "070701" #define CPIO_TRAILER "TRAILER!!!" #define CPIO_HEADER_SIZE 110 /* new ASCII format (6B + 8B * 13) */ #define CPIO_PADDING(offset) PADDING((offset), 4) struct drpm { char *filename; uint32_t version; uint32_t type; uint32_t comp; char *sequence; char *src_nevr; char *tgt_nevr; uint32_t tgt_size; char tgt_md5[MD5_DIGEST_LENGTH * 2 + 1]; uint32_t tgt_comp; char *tgt_comp_param; uint32_t tgt_header_len; uint32_t *offadj_elems; char *tgt_leadsig; uint32_t payload_fmt_off; uint32_t *int_copies; uint32_t *ext_copies; uint64_t ext_data_len; uint64_t int_data_len; uint32_t offadj_elems_size; uint32_t int_copies_size; uint32_t ext_copies_size; }; struct drpm_make_options { bool rpm_only; unsigned short version; bool comp_from_rpm; unsigned short comp; unsigned short comp_level; bool addblk; unsigned short addblk_comp; unsigned short addblk_comp_level; char *seqfile; char *oldrpmprint; char *oldpatchrpm; unsigned mbytes; }; struct cpio_file; struct cpio_header; struct deltarpm; struct file_info; //drpm_block.c struct blocks; //drpm_compstrm.c struct compstrm; //drpm_decompstrm.c struct decompstrm; //drpm_make.c struct rpm_patches; //drpm_rpm.c struct rpm; //drpm_search.c struct hash; struct sfxsrt; //drpm_write.c struct compstrm_wrapper; //drpm_apply.c int expand_sequence(struct cpio_file **, size_t *, const unsigned char *, uint32_t, const struct file_info *, size_t, unsigned short, int); int is_prelinked(bool *, int, const unsigned char *, ssize_t); int prelink_open(const char *, int *); //drpm_block.c size_t block_id(uint64_t offset); size_t block_size(); int blocks_create(struct blocks **, uint64_t, const struct file_info *, const struct cpio_file *, size_t, const uint32_t *, size_t, struct rpm *, bool); int blocks_destroy(struct blocks **); int blocks_next(struct blocks *, unsigned char *, size_t *, uint64_t, size_t, size_t, size_t); //drpm_compstrm.c int compstrm_destroy(struct compstrm **); int compstrm_finish(struct compstrm *, unsigned char **, size_t *); int compstrm_init(struct compstrm **, int, unsigned short, int); int compstrm_write(struct compstrm *, size_t, const void *); int compstrm_write_be32(struct compstrm *, uint32_t); int compstrm_write_be64(struct compstrm *, uint64_t); //drpm_decompstrm.c int decompstrm_destroy(struct decompstrm **); int decompstrm_get_comp_size(struct decompstrm *, size_t *); int decompstrm_init(struct decompstrm **, int, unsigned short *, MD5_CTX *, const unsigned char *, size_t); int decompstrm_read(struct decompstrm *, size_t, void *); int decompstrm_read_be32(struct decompstrm *, uint32_t *); int decompstrm_read_be64(struct decompstrm *, uint64_t *); int decompstrm_read_until_eof(struct decompstrm *, size_t *, unsigned char **); //drpm_deltarpm.c bool deltarpm_decode_comp(uint32_t, unsigned short *, unsigned short *); bool deltarpm_encode_comp(uint32_t *, unsigned short, unsigned short); void free_deltarpm(struct deltarpm *); //drpm_diff.c int make_diff(const unsigned char *, size_t, const unsigned char *, size_t, const unsigned char ***, uint64_t *, uint32_t **, uint32_t *, uint32_t **, uint32_t *, unsigned char **, uint32_t *, unsigned short, int); //drpm_make.c int cpio_header_read(struct cpio_header *, const char *); void cpio_header_write(const struct cpio_header *, char *); int fill_nodiff_deltarpm(struct deltarpm *, const char *, bool); int parse_cpio_from_rpm_filedata(struct rpm *, unsigned char **, size_t *, unsigned char **, uint32_t *, uint32_t **, uint32_t *, const struct rpm_patches *); int patches_check_nevr(const struct rpm_patches *, const char *); int patches_destroy(struct rpm_patches **); int patches_read(const char *, const char *, struct rpm_patches **); //drpm_read.c int deltarpm_to_drpm(const struct deltarpm *, struct drpm *); void drpm_free(struct drpm *); int read_be32(int, uint32_t *); int read_be64(int, uint64_t *); int read_deltarpm(struct deltarpm *, const char *); //drpm_rpm.c int rpm_archive_read_chunk(struct rpm *, void *, size_t); int rpm_archive_rewind(struct rpm *); int rpm_destroy(struct rpm **); int rpm_fetch_archive(struct rpm *, unsigned char **, size_t *); int rpm_fetch_header(struct rpm *, unsigned char **, uint32_t *); int rpm_fetch_lead_and_signature(struct rpm *, unsigned char **, uint32_t *); int rpm_find_payload_format_offset(struct rpm *, uint32_t *); int rpm_get_comp(struct rpm *, unsigned short *); int rpm_get_comp_level(struct rpm *, unsigned short *); int rpm_get_digest_algo(struct rpm *, unsigned short *); int rpm_get_file_info(struct rpm *, struct file_info **, size_t *, bool *); int rpm_get_nevr(struct rpm *, char **); int rpm_get_payload_format(struct rpm *, unsigned short *); bool rpm_is_sourcerpm(struct rpm *); int rpm_patch_payload_format(struct rpm *, const char *); int rpm_read(struct rpm **, const char *, int, unsigned short *, unsigned char *, unsigned char *); int rpm_read_header(struct rpm **, const char *, const char *); int rpm_replace_lead_and_signature(struct rpm *, unsigned char *, size_t); int rpm_signature_empty(struct rpm *); int rpm_signature_get_md5(struct rpm *, unsigned char *, bool *); int rpm_signature_reload(struct rpm *); int rpm_signature_set_md5(struct rpm *, unsigned char *); int rpm_signature_set_size(struct rpm *, uint32_t); uint32_t rpm_size_full(struct rpm *); uint32_t rpm_size_header(struct rpm *); int rpm_write(struct rpm *, const char *, bool, unsigned char *, bool); //drpm_search.c int hash_create(struct hash **, const unsigned char *, size_t); void hash_free(struct hash **); size_t hash_search(struct hash *, const unsigned char *, size_t, const unsigned char *, size_t, size_t, size_t, size_t *, size_t *); int sfxsrt_create(struct sfxsrt **, const unsigned char *, size_t); void sfxsrt_free(struct sfxsrt **); size_t sfxsrt_search(struct sfxsrt *, const unsigned char *, size_t, const unsigned char *, size_t, size_t, size_t, size_t *, size_t *); //drpm_utils.c void create_be32(uint32_t, unsigned char *); void create_be64(uint64_t, unsigned char *); void dump_hex(char *, const unsigned char *, size_t); int md5_update_be32(MD5_CTX *, uint32_t); uint16_t parse_be16(const unsigned char *); uint32_t parse_be32(const unsigned char *); uint64_t parse_be64(const unsigned char *); ssize_t parse_hex(unsigned char *, const char *); ssize_t parse_hexnum(const char *, size_t); bool parse_md5(unsigned char *, const char *); bool parse_sha256(unsigned char *, const char *); bool resize16(void **, size_t, size_t); bool resize32(void **, size_t, size_t); //drpm_write.c int compstrm_wrapper_destroy(struct compstrm_wrapper **); int compstrm_wrapper_finish(struct compstrm_wrapper *, unsigned char **, size_t *); int compstrm_wrapper_init(struct compstrm_wrapper **, size_t, int, unsigned short, int); int compstrm_wrapper_write(struct compstrm_wrapper *, const unsigned char *, size_t); int write_be32(int, uint32_t); int write_be64(int, uint64_t); int write_comp(struct compstrm *, size_t *, int, const void *, size_t); int write_deltarpm(struct deltarpm *); int write_seqfile(struct deltarpm *, const char *); struct cpio_file { ssize_t index; size_t header_len; size_t content_len; size_t offset; }; struct cpio_header { uint16_t ino; uint16_t mode; uint16_t uid; uint16_t gid; uint16_t nlink; uint32_t mtime; uint32_t filesize; uint8_t devmajor; uint8_t devminor; uint8_t rdevmajor; uint8_t rdevminor; uint16_t namesize; }; struct deltarpm { const char *filename; unsigned short type; unsigned short comp; unsigned short comp_level; union { struct rpm *tgt_rpm; char *tgt_nevr; } head; unsigned short version; char *src_nevr; uint32_t sequence_len; unsigned char *sequence; unsigned char tgt_md5[MD5_DIGEST_LENGTH]; uint32_t tgt_size; unsigned short tgt_comp; unsigned short tgt_comp_level; uint32_t tgt_comp_param_len; unsigned char *tgt_comp_param; uint32_t tgt_header_len; uint32_t offadj_elems_count; uint32_t *offadj_elems; uint32_t tgt_leadsig_len; unsigned char *tgt_leadsig; uint32_t payload_fmt_off; uint32_t int_copies_count; uint32_t ext_copies_count; uint32_t *int_copies; uint32_t *ext_copies; uint64_t ext_data_len; uint32_t add_data_len; unsigned char *add_data; uint64_t int_data_len; bool int_data_as_ptrs; union { unsigned char *bytes; const unsigned char **ptrs; } int_data; }; struct file_info { char *name; uint32_t flags; char *md5; uint16_t rdev; uint32_t size; uint16_t mode; uint32_t verify; char *linkto; uint32_t color; }; #endif drpm-0.5.1/src/drpm_read.c000066400000000000000000000436721421165313000153570ustar00rootroot00000000000000/* Authors: Pavel Tobias Matej Chalk Copyright (C) 2014 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include #include #include #include #include #define MAGIC_DRPM 0x6472706D #define MAGIC_DLT(x) (((x) >> 8) == 0x444C54) #define MAGIC_DLT3(x) ((x) == 0x444C5433) static int readdelta_rest(int, struct deltarpm *); static int readdelta_rpmonly(int, struct deltarpm *); static int readdelta_standard(int, struct deltarpm *); /* Reads 32-byte integer in network byte order from file. */ int read_be32(int filedesc, uint32_t *buffer_ret) { unsigned char buffer[4]; switch (read(filedesc, buffer, 4)) { case 4: break; case -1: return DRPM_ERR_IO; default: return DRPM_ERR_FORMAT; } *buffer_ret = parse_be32(buffer); return DRPM_ERR_OK; } /* Reads 64-byte integer in network byte order from file. */ int read_be64(int filedesc, uint64_t *buffer_ret) { unsigned char buffer[8]; switch (read(filedesc, buffer, 8)) { case 8: break; case -1: return DRPM_ERR_IO; default: return DRPM_ERR_FORMAT; } *buffer_ret = parse_be64(buffer); return DRPM_ERR_OK; } /* Reads the rest of the DeltaRPM, i.e. the compressed part * that has the same format for standard and rpm-only deltas. */ int readdelta_rest(int filedesc, struct deltarpm *delta) { struct decompstrm *stream; uint32_t version; uint32_t src_nevr_len; uint32_t deltarpm_comp; uint32_t offadj_elems_size; uint32_t int_copies_size; uint32_t ext_copies_size; uint32_t ext_data_32; uint32_t add_data_len; uint32_t int_data_32; uint64_t off; int error = DRPM_ERR_OK; /* initializing decompression and determining compression method */ if ((error = decompstrm_init(&stream, filedesc, &delta->comp, NULL, NULL, 0)) != DRPM_ERR_OK) return error; /* reading delta version (1-3) */ if ((error = decompstrm_read_be32(stream, &version)) != DRPM_ERR_OK) goto cleanup; if (!MAGIC_DLT(version)) { error = DRPM_ERR_FORMAT; goto cleanup; } delta->version = version % 256 - '0'; if (delta->version < 3 && delta->type == DRPM_TYPE_RPMONLY) { // rpm-only deltas only supported since version 3 error = DRPM_ERR_FORMAT; goto cleanup; } /* reading source NEVR */ if ((error = decompstrm_read_be32(stream, &src_nevr_len)) != DRPM_ERR_OK) goto cleanup; if ((delta->src_nevr = malloc(src_nevr_len + 1)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if ((error = decompstrm_read(stream, src_nevr_len, delta->src_nevr)) != DRPM_ERR_OK) goto cleanup; delta->src_nevr[src_nevr_len] = '\0'; /* reading DeltaRPM sequence */ if ((error = decompstrm_read_be32(stream, &delta->sequence_len)) != DRPM_ERR_OK) goto cleanup; // sequence consists of an MD5 checksum and, for standard deltas, // the compressed order in which the files from the RPM header // appear in the CPIO archive if (delta->sequence_len < MD5_DIGEST_LENGTH || (delta->sequence_len != MD5_DIGEST_LENGTH && delta->type == DRPM_TYPE_RPMONLY)) { error = DRPM_ERR_FORMAT; goto cleanup; } if ((delta->sequence = malloc(delta->sequence_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if ((error = decompstrm_read(stream, delta->sequence_len, delta->sequence)) != DRPM_ERR_OK) goto cleanup; /* reading MD5 sum of the target RPM */ if ((error = decompstrm_read(stream, MD5_DIGEST_LENGTH, delta->tgt_md5)) != DRPM_ERR_OK) goto cleanup; if (delta->version >= 2) { /* reading size of the target RPM and the target compression */ if ((error = decompstrm_read_be32(stream, &delta->tgt_size)) != DRPM_ERR_OK || (error = decompstrm_read_be32(stream, &deltarpm_comp)) != DRPM_ERR_OK) goto cleanup; if (!deltarpm_decode_comp(deltarpm_comp, &delta->tgt_comp, &delta->tgt_comp_level)) { error = DRPM_ERR_FORMAT; goto cleanup; } /* reading target compression parameters */ if ((error = decompstrm_read_be32(stream, &delta->tgt_comp_param_len)) != DRPM_ERR_OK) goto cleanup; if (delta->tgt_comp_param_len > 0) { if ((delta->tgt_comp_param = malloc(delta->tgt_comp_param_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if ((error = decompstrm_read(stream, delta->tgt_comp_param_len, delta->tgt_comp_param)) != DRPM_ERR_OK) goto cleanup; } if (delta->version == 3) { /* reading size of target header included in the diff * and the offset adjustment elements for the CPIO archive */ if ((error = decompstrm_read_be32(stream, &delta->tgt_header_len)) != DRPM_ERR_OK || (error = decompstrm_read_be32(stream, &delta->offadj_elems_count)) != DRPM_ERR_OK) goto cleanup; if (delta->offadj_elems_count > 0) { offadj_elems_size = delta->offadj_elems_count * 2; if ((delta->offadj_elems = malloc(offadj_elems_size * 4)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } for (uint32_t i = 0; i < offadj_elems_size; i += 2) if ((error = decompstrm_read_be32(stream, delta->offadj_elems + i)) != DRPM_ERR_OK) goto cleanup; for (uint32_t j = 1; j < offadj_elems_size; j += 2) { if ((error = decompstrm_read_be32(stream, delta->offadj_elems + j)) != DRPM_ERR_OK) goto cleanup; if ((delta->offadj_elems[j] & INT32_MIN) != 0) delta->offadj_elems[j] = TWOS_COMPLEMENT(delta->offadj_elems[j] ^ INT32_MIN); } } } } if (delta->tgt_header_len == 0 && delta->type == DRPM_TYPE_RPMONLY) { // rpm-only deltas include the header in the diff error = DRPM_ERR_FORMAT; goto cleanup; } /* reading target lead and signature */ if ((error = decompstrm_read_be32(stream, &delta->tgt_leadsig_len)) != DRPM_ERR_OK) goto cleanup; if (delta->tgt_leadsig_len < RPM_LEADSIG_MIN_LEN) { error = DRPM_ERR_FORMAT; goto cleanup; } if ((delta->tgt_leadsig = malloc(delta->tgt_leadsig_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if ((error = decompstrm_read(stream, delta->tgt_leadsig_len, delta->tgt_leadsig)) != DRPM_ERR_OK) goto cleanup; /* reading payload format offset and internal and external copies */ if ((error = decompstrm_read_be32(stream, &delta->payload_fmt_off)) != DRPM_ERR_OK || (error = decompstrm_read_be32(stream, &delta->int_copies_count)) != DRPM_ERR_OK || (error = decompstrm_read_be32(stream, &delta->ext_copies_count)) != DRPM_ERR_OK) goto cleanup; int_copies_size = delta->int_copies_count * 2; ext_copies_size = delta->ext_copies_count * 2; if (int_copies_size > 0) { if ((delta->int_copies = malloc(int_copies_size * 4)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } for (uint32_t i = 0; i < int_copies_size; i += 2) if ((error = decompstrm_read_be32(stream, delta->int_copies + i)) != DRPM_ERR_OK) goto cleanup; for (uint32_t j = 1; j < int_copies_size; j += 2) if ((error = decompstrm_read_be32(stream, delta->int_copies + j)) != DRPM_ERR_OK) goto cleanup; } if (ext_copies_size > 0) { if ((delta->ext_copies = malloc(ext_copies_size * 4)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } for (uint32_t i = 0; i < ext_copies_size; i += 2) { if ((error = decompstrm_read_be32(stream, delta->ext_copies + i)) != DRPM_ERR_OK) goto cleanup; if ((delta->ext_copies[i] & INT32_MIN) != 0) delta->ext_copies[i] = TWOS_COMPLEMENT(delta->ext_copies[i] ^ INT32_MIN); } for (uint32_t j = 1; j < ext_copies_size; j += 2) if ((error = decompstrm_read_be32(stream, delta->ext_copies + j)) != DRPM_ERR_OK) goto cleanup; } /* reading length of external data */ if (delta->version == 3) { if ((error = decompstrm_read_be64(stream, &delta->ext_data_len)) != DRPM_ERR_OK) goto cleanup; } else { if ((error = decompstrm_read_be32(stream, &ext_data_32)) != DRPM_ERR_OK) goto cleanup; delta->ext_data_len = ext_data_32; } /* reading add data */ if ((error = decompstrm_read_be32(stream, &add_data_len)) != DRPM_ERR_OK) goto cleanup; if (add_data_len > 0) { if (delta->type == DRPM_TYPE_RPMONLY) { // should be empty for rpm-only deltas, as they include this earlier error = DRPM_ERR_FORMAT; goto cleanup; } if ((delta->add_data = malloc(add_data_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if ((error = decompstrm_read(stream, add_data_len, delta->add_data)) != DRPM_ERR_OK) goto cleanup; delta->add_data_len = add_data_len; } /* reading internal data */ if (delta->version == 3) { if ((error = decompstrm_read_be64(stream, &delta->int_data_len)) != DRPM_ERR_OK) goto cleanup; } else { if ((error = decompstrm_read_be32(stream, &int_data_32)) != DRPM_ERR_OK) goto cleanup; delta->int_data_len = int_data_32; } if (delta->int_data_len > SIZE_MAX) { error = DRPM_ERR_OVERFLOW; goto cleanup; } if (delta->int_data_len > 0) { if ((delta->int_data.bytes = malloc(delta->int_data_len)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if ((error = decompstrm_read(stream, delta->int_data_len, delta->int_data.bytes)) != DRPM_ERR_OK) goto cleanup; } delta->int_data_as_ptrs = false; /* checking internal copies against internal data length */ off = 0; for (uint32_t i = 1; i < int_copies_size; i += 2) { off += delta->int_copies[i]; if (off > delta->int_data_len) { error = DRPM_ERR_FORMAT; goto cleanup; } } /* checking external copies against external data length */ off = 0; for (uint32_t i = 0; i < ext_copies_size; i += 2) { off += (int32_t)delta->ext_copies[i]; if (off > delta->ext_data_len) { error = DRPM_ERR_FORMAT; goto cleanup; } off += delta->ext_copies[i + 1]; if (off == 0 || off > delta->ext_data_len) { error = DRPM_ERR_FORMAT; goto cleanup; } } cleanup: decompstrm_destroy(&stream); return error; } /* Reads part of DeltaRPM specific to rpm-only deltas. */ int readdelta_rpmonly(int filedesc, struct deltarpm *delta) { uint32_t version; uint32_t tgt_nevr_len; ssize_t bytes_read; int error; if ((error = read_be32(filedesc, &version)) != DRPM_ERR_OK) return error; if (!MAGIC_DLT3(version)) return DRPM_ERR_FORMAT; if ((error = read_be32(filedesc, &tgt_nevr_len)) != DRPM_ERR_OK) return error; /* reading target NEVR */ if ((delta->head.tgt_nevr = malloc(tgt_nevr_len + 1)) == NULL) return DRPM_ERR_MEMORY; if ((bytes_read = read(filedesc, delta->head.tgt_nevr, tgt_nevr_len)) < 0) return DRPM_ERR_IO; if ((uint32_t)bytes_read != tgt_nevr_len) return DRPM_ERR_FORMAT; delta->head.tgt_nevr[tgt_nevr_len] = '\0'; /* reading add data */ if ((error = read_be32(filedesc, &delta->add_data_len)) != DRPM_ERR_OK) return error; if ((delta->add_data = malloc(delta->add_data_len)) == NULL) return DRPM_ERR_MEMORY; if ((bytes_read = read(filedesc, delta->add_data, delta->add_data_len)) < 0) return DRPM_ERR_IO; if ((uint32_t)bytes_read != delta->add_data_len) return DRPM_ERR_FORMAT; return DRPM_ERR_OK; } /* Reads part of DeltaRPM specific to standard deltas. */ int readdelta_standard(int filedesc, struct deltarpm *delta) { struct rpm *rpmst; int error; /* reading RPM lead, signature and header */ if ((error = rpm_read(&rpmst, delta->filename, RPM_ARCHIVE_DONT_READ, NULL, NULL, NULL)) != DRPM_ERR_OK) return error; /* reading target compression from header (used for older delta versions) */ if ((error = rpm_get_comp(rpmst, &delta->tgt_comp)) != DRPM_ERR_OK) return error; if (lseek(filedesc, rpm_size_full(rpmst), SEEK_SET) == (off_t)-1) return DRPM_ERR_IO; delta->head.tgt_rpm = rpmst; return DRPM_ERR_OK; } /* Reads DeltaRPM from file. */ int read_deltarpm(struct deltarpm *delta, const char *filename) { int filedesc; uint32_t magic; int error = DRPM_ERR_OK; if (filename == NULL || delta == NULL) return DRPM_ERR_PROG; if ((filedesc = open(filename, O_RDONLY)) == -1) return DRPM_ERR_IO; delta->filename = filename; /* determining type of delta by magic bytes and calling relevant subroutine */ if ((error = read_be32(filedesc, &magic)) != DRPM_ERR_OK) goto cleanup_fail; switch (magic) { case MAGIC_DRPM: delta->type = DRPM_TYPE_RPMONLY; if ((error = readdelta_rpmonly(filedesc, delta)) != DRPM_ERR_OK) goto cleanup_fail; break; case MAGIC_RPM: delta->type = DRPM_TYPE_STANDARD; if ((error = readdelta_standard(filedesc, delta)) != DRPM_ERR_OK) goto cleanup_fail; break; default: error = DRPM_ERR_FORMAT; goto cleanup_fail; } /* the rest of the delta is the same for both types */ if ((error = readdelta_rest(filedesc, delta)) != DRPM_ERR_OK) goto cleanup_fail; goto cleanup; cleanup_fail: free_deltarpm(delta); cleanup: close(filedesc); return error; } /* Converts DeltaRPM data to more readable format. */ int deltarpm_to_drpm(const struct deltarpm *src, struct drpm *dst) { const struct drpm init = {0}; int error; if (src == NULL || dst == NULL) return DRPM_ERR_PROG; *dst = init; dst->version = src->version; dst->type = src->type; dst->comp = src->comp; dst->tgt_size = src->tgt_size; dst->tgt_comp = src->tgt_comp; dst->tgt_header_len = src->tgt_header_len; dst->payload_fmt_off = src->payload_fmt_off; dst->ext_data_len = src->ext_data_len; dst->int_data_len = src->int_data_len; dst->offadj_elems_size = src->offadj_elems_count * 2; dst->int_copies_size = src->int_copies_count * 2; dst->ext_copies_size = src->ext_copies_count * 2; if ((dst->filename = malloc(strlen(src->filename) + 1)) == NULL || (dst->sequence = malloc(src->sequence_len * 2 + 1)) == NULL || (dst->src_nevr = malloc(strlen(src->src_nevr) + 1)) == NULL || (src->tgt_comp_param_len > 0 && (dst->tgt_comp_param = malloc(src->tgt_comp_param_len * 2 + 1)) == NULL) || (dst->tgt_leadsig = malloc(src->tgt_leadsig_len * 2 + 1)) == NULL || (dst->offadj_elems_size > 0 && (dst->offadj_elems = malloc(dst->offadj_elems_size * 4)) == NULL) || (dst->int_copies_size > 0 && (dst->int_copies = malloc(dst->int_copies_size * 4)) == NULL) || (dst->ext_copies_size > 0 && (dst->ext_copies = malloc(dst->ext_copies_size * 4)) == NULL)) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } strcpy(dst->filename, src->filename); strcpy(dst->src_nevr, src->src_nevr); dump_hex(dst->sequence, src->sequence, src->sequence_len); dump_hex(dst->tgt_md5, src->tgt_md5, MD5_DIGEST_LENGTH); dump_hex(dst->tgt_leadsig, src->tgt_leadsig, src->tgt_leadsig_len); if (src->tgt_comp_param_len > 0) dump_hex(dst->tgt_comp_param, src->tgt_comp_param, src->tgt_comp_param_len); if (dst->offadj_elems_size > 0) memcpy(dst->offadj_elems, src->offadj_elems, dst->offadj_elems_size * 4); if (dst->int_copies_size > 0) memcpy(dst->int_copies, src->int_copies, dst->int_copies_size * 4); if (dst->ext_copies_size > 0) memcpy(dst->ext_copies, src->ext_copies, dst->ext_copies_size * 4); if (src->type == DRPM_TYPE_STANDARD) { if ((error = rpm_get_nevr(src->head.tgt_rpm, &dst->tgt_nevr)) != DRPM_ERR_OK) goto cleanup_fail; } else { if ((dst->tgt_nevr = malloc(strlen(src->head.tgt_nevr) + 1)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } strcpy(dst->tgt_nevr, src->head.tgt_nevr); } return DRPM_ERR_OK; cleanup_fail: drpm_free(dst); return error; } void drpm_free(struct drpm *delta) { const struct drpm delta_init = {0}; free(delta->filename); free(delta->src_nevr); free(delta->tgt_nevr); free(delta->sequence); free(delta->tgt_comp_param); free(delta->tgt_leadsig); free(delta->offadj_elems); free(delta->int_copies); free(delta->ext_copies); *delta = delta_init; } drpm-0.5.1/src/drpm_rpm.c000066400000000000000000000757671421165313000152540ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2015 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include #include #include #include #include #include #define BUFFER_SIZE 4096 /* RFC 4880 - Section 9.4. Hash Algorithms */ #define RFC4880_HASH_ALGO_MD5 1 #define RFC4880_HASH_ALGO_SHA256 8 #define RPMSIG_PADDING(offset) PADDING((offset), 8) #define RPMLEAD_SIZE 96 struct rpm { unsigned char lead[RPMLEAD_SIZE]; Header signature; Header header; unsigned char *archive; size_t archive_size; size_t archive_offset; size_t archive_comp_size; }; static void rpm_init(struct rpm *); static void rpm_free(struct rpm *); static int rpm_export_header(struct rpm *, unsigned char **, size_t *); static int rpm_export_signature(struct rpm *, unsigned char **, size_t *); static void rpm_header_unload_region(struct rpm *, rpmTagVal); static int rpm_read_archive(struct rpm *, const char *, off_t, bool, unsigned short *, MD5_CTX *, MD5_CTX *); void rpm_init(struct rpm *rpmst) { if (rpmst == NULL) return; memset(rpmst->lead, 0, RPMLEAD_SIZE); rpmst->signature = NULL; rpmst->header = NULL; rpmst->archive = NULL; rpmst->archive_size = 0; rpmst->archive_offset = 0; rpmst->archive_comp_size = 0; } void rpm_free(struct rpm *rpmst) { if (rpmst == NULL) return; headerFree(rpmst->signature); headerFree(rpmst->header); free(rpmst->archive); rpm_init(rpmst); } int rpm_export_signature(struct rpm *rpmst, unsigned char **signature_ret, size_t *len_ret) { unsigned char *signature; unsigned signature_size; size_t len; unsigned char padding[7] = {0}; unsigned short padding_bytes; *signature_ret = NULL; *len_ret = 0; if ((signature = headerExport(rpmst->signature, &signature_size)) == NULL) { free(signature); return DRPM_ERR_MEMORY; } len = sizeof(rpm_header_magic) + signature_size; padding_bytes = RPMSIG_PADDING(len); len += padding_bytes; if ((*signature_ret = malloc(len)) == NULL) { free(signature); return DRPM_ERR_MEMORY; } memcpy(*signature_ret, rpm_header_magic, sizeof(rpm_header_magic)); memcpy(*signature_ret + sizeof(rpm_header_magic), signature, signature_size); memcpy(*signature_ret + sizeof(rpm_header_magic) + signature_size, padding, padding_bytes); *len_ret = len; free(signature); return DRPM_ERR_OK; } int rpm_export_header(struct rpm *rpmst, unsigned char **header_ret, size_t *len_ret) { unsigned char *header; unsigned header_size; *header_ret = NULL; *len_ret = 0; if ((header = headerExport(rpmst->header, &header_size)) == NULL || (*header_ret = malloc(sizeof(rpm_header_magic) + header_size)) == NULL) { free(header); return DRPM_ERR_MEMORY; } memcpy(*header_ret, rpm_header_magic, sizeof(rpm_header_magic)); memcpy(*header_ret + sizeof(rpm_header_magic), header, header_size); *len_ret = sizeof(rpm_header_magic) + header_size; free(header); return DRPM_ERR_OK; } void rpm_header_unload_region(struct rpm *rpmst, rpmTagVal rpmtag) { Header hdr; HeaderIterator hdr_iter; rpmtd copy_td; rpmtd td = rpmtdNew(); if (headerGet(rpmst->header, rpmtag, td, HEADERGET_DEFAULT)) { headerFree(rpmst->header); rpmst->header = headerNew(); copy_td = rpmtdNew(); hdr = headerCopyLoad(td->data); hdr_iter = headerInitIterator(hdr); while (headerNext(hdr_iter, copy_td)) { if (copy_td->data) headerPut(rpmst->header, copy_td, HEADERPUT_DEFAULT); rpmtdFreeData(copy_td); } headerFreeIterator(hdr_iter); headerFree(hdr); rpmtdFreeData(td); rpmtdFree(copy_td); } rpmtdFree(td); } int rpm_read_archive(struct rpm *rpmst, const char *filename, off_t offset, bool decompress, unsigned short *comp_ret, MD5_CTX *seq_md5, MD5_CTX *full_md5) { struct decompstrm *stream = NULL; int filedesc; unsigned char *archive_tmp; unsigned char buffer[BUFFER_SIZE]; ssize_t bytes_read; MD5_CTX *md5; int error = DRPM_ERR_OK; if ((filedesc = open(filename, O_RDONLY)) < 0) return DRPM_ERR_IO; if (lseek(filedesc, offset, SEEK_SET) != offset) { error = DRPM_ERR_IO; goto cleanup; } if (decompress) { // hack: never updating both MD5s when decompressing md5 = (seq_md5 == NULL) ? full_md5 : seq_md5; if ((error = decompstrm_init(&stream, filedesc, comp_ret, md5, NULL, 0)) != DRPM_ERR_OK || (error = decompstrm_read_until_eof(stream, &rpmst->archive_size, &rpmst->archive)) != DRPM_ERR_OK || (error = decompstrm_get_comp_size(stream, &rpmst->archive_comp_size)) != DRPM_ERR_OK || (error = decompstrm_destroy(&stream)) != DRPM_ERR_OK) goto cleanup; } else { while ((bytes_read = read(filedesc, buffer, BUFFER_SIZE)) > 0) { if ((archive_tmp = realloc(rpmst->archive, rpmst->archive_size + bytes_read)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } if ((seq_md5 != NULL && MD5_Update(seq_md5, buffer, bytes_read) != 1) || (full_md5 != NULL && MD5_Update(full_md5, buffer, bytes_read) != 1)) { error = DRPM_ERR_OTHER; goto cleanup; } rpmst->archive = archive_tmp; memcpy(rpmst->archive + rpmst->archive_size, buffer, bytes_read); rpmst->archive_size += bytes_read; } if (bytes_read < 0) { error = DRPM_ERR_IO; goto cleanup; } rpmst->archive_comp_size = rpmst->archive_size; } cleanup: if (stream != NULL) decompstrm_destroy(&stream); close(filedesc); return error; } /* Reads RPM (or RPM-like file) from file into <*rpmst>. * The archive may be decompressed, read "as is", or not read at all. * If read, the compression method used in the archive is stored in * <*archive_comp>. * Two MD5 checksums may be created. An MD5 digest of the header * and archive will be written to , while * shall be made up of the while file. */ int rpm_read(struct rpm **rpmst, const char *filename, int archive_mode, unsigned short *archive_comp, unsigned char seq_md5_digest[MD5_DIGEST_LENGTH], unsigned char full_md5_digest[MD5_DIGEST_LENGTH]) { FD_t file; const unsigned char magic_rpm[4] = {0xED, 0xAB, 0xEE, 0xDB}; off_t file_pos; bool include_archive; bool decomp_archive = false; MD5_CTX seq_md5; MD5_CTX full_md5; unsigned char *signature = NULL; size_t signature_len; unsigned char *header = NULL; size_t header_len; int error = DRPM_ERR_OK; if (rpmst == NULL || filename == NULL) return DRPM_ERR_PROG; switch (archive_mode) { case RPM_ARCHIVE_DONT_READ: include_archive = false; break; case RPM_ARCHIVE_READ_UNCOMP: include_archive = true; decomp_archive = false; break; case RPM_ARCHIVE_READ_DECOMP: include_archive = true; decomp_archive = true; break; default: return DRPM_ERR_PROG; } if ((*rpmst = malloc(sizeof(struct rpm))) == NULL) return DRPM_ERR_MEMORY; rpm_init(*rpmst); // hack: extra '\0' to prevent rpmlib from compressing (see rpmio.c) if ((file = Fopen(filename, "rb\0")) == NULL) return DRPM_ERR_IO; if (Fread((*rpmst)->lead, 1, RPMLEAD_SIZE, file) != RPMLEAD_SIZE || memcmp((*rpmst)->lead, magic_rpm, 4) != 0 || ((*rpmst)->signature = headerRead(file, HEADER_MAGIC_YES)) == NULL || (file_pos = Ftell(file)) < 0 || Fseek(file, RPMSIG_PADDING(file_pos), SEEK_CUR) < 0 || ((*rpmst)->header = headerRead(file, HEADER_MAGIC_YES)) == NULL) { error = Ferror(file) ? DRPM_ERR_IO : DRPM_ERR_FORMAT; goto cleanup_fail; } if (seq_md5_digest != NULL) { if ((error = rpm_export_header(*rpmst, &header, &header_len)) != DRPM_ERR_OK) goto cleanup_fail; if (MD5_Init(&seq_md5) != 1 || MD5_Update(&seq_md5, header, header_len) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } } if (full_md5_digest != NULL) { if ((error = rpm_export_signature(*rpmst, &signature, &signature_len)) != DRPM_ERR_OK || (header == NULL && (error = rpm_export_header(*rpmst, &header, &header_len)) != DRPM_ERR_OK)) goto cleanup_fail; if (MD5_Init(&full_md5) != 1 || MD5_Update(&full_md5, (*rpmst)->lead, RPMLEAD_SIZE) != 1 || MD5_Update(&full_md5, signature, signature_len) != 1 || MD5_Update(&full_md5, header, header_len) != 1) { error = DRPM_ERR_OTHER; goto cleanup_fail; } } if (include_archive) { if ((file_pos = Ftell(file)) < 0) { error = DRPM_ERR_IO; goto cleanup_fail; } if ((error = rpm_read_archive(*rpmst, filename, file_pos, decomp_archive, archive_comp, (seq_md5_digest != NULL) ? &seq_md5 : NULL, (full_md5_digest != NULL) ? &full_md5 : NULL)) != DRPM_ERR_OK) goto cleanup_fail; } if ((seq_md5_digest != NULL && MD5_Final(seq_md5_digest, &seq_md5) != 1) || (full_md5_digest != NULL && MD5_Final(full_md5_digest, &full_md5) != 1)) { error = DRPM_ERR_OTHER; goto cleanup_fail; } goto cleanup; cleanup_fail: rpm_free(*rpmst); cleanup: free(signature); free(header); Fclose(file); return error; } /* Frees RPM data. */ int rpm_destroy(struct rpm **rpmst) { if (rpmst == NULL || *rpmst == NULL) return DRPM_ERR_PROG; rpm_free(*rpmst); free(*rpmst); *rpmst = NULL; return DRPM_ERR_OK; } /* Reads bytes to from the current offset in the archive. */ int rpm_archive_read_chunk(struct rpm *rpmst, void *buffer, size_t count) { if (rpmst == NULL) return DRPM_ERR_PROG; if (rpmst->archive_offset + count > rpmst->archive_size) return DRPM_ERR_FORMAT; if (buffer != NULL) memcpy(buffer, rpmst->archive + rpmst->archive_offset, count); rpmst->archive_offset += count; return DRPM_ERR_OK; } /* Positions the archive offset at the beginning of the archive. */ int rpm_archive_rewind(struct rpm *rpmst) { if (rpmst == NULL) return DRPM_ERR_PROG; rpmst->archive_offset = 0; return DRPM_ERR_OK; } /* Returns the on-disk size of the RPM file. This will be without * the archive if it wasn't read. */ uint32_t rpm_size_full(struct rpm *rpmst) { if (rpmst == NULL) return 0; unsigned sig_size = headerSizeof(rpmst->signature, HEADER_MAGIC_YES); return RPMLEAD_SIZE + sig_size + RPMSIG_PADDING(sig_size) + headerSizeof(rpmst->header, HEADER_MAGIC_YES) + rpmst->archive_comp_size; } /* Returns the size of the RPM header. */ uint32_t rpm_size_header(struct rpm *rpmst) { if (rpmst == NULL) return 0; return headerSizeof(rpmst->header, HEADER_MAGIC_YES); } /* Fetches a concatenation of the on-disk RPM lead and signature. */ int rpm_fetch_lead_and_signature(struct rpm *rpmst, unsigned char **lead_sig, uint32_t *len_ret) { unsigned char *signature; size_t signature_size; int error = DRPM_ERR_OK; if (rpmst == NULL || lead_sig == NULL || len_ret == NULL) return DRPM_ERR_PROG; *lead_sig = NULL; *len_ret = 0; if ((error = rpm_export_signature(rpmst, &signature, &signature_size)) != DRPM_ERR_OK) goto cleanup; if ((*lead_sig = malloc(RPMLEAD_SIZE + signature_size)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } memcpy(*lead_sig, rpmst->lead, RPMLEAD_SIZE); memcpy(*lead_sig + RPMLEAD_SIZE, signature, signature_size); *len_ret = RPMLEAD_SIZE + signature_size; cleanup: free(signature); return error; } /* Fetches the on-disk RPM header. */ int rpm_fetch_header(struct rpm *rpmst, unsigned char **header_ret, uint32_t *len_ret) { int error; size_t header_size; if ((error = rpm_export_header(rpmst, header_ret, &header_size)) != DRPM_ERR_OK) return error; *len_ret = header_size; return DRPM_ERR_OK; } /* Fetches the archive (in whatever format it was read). */ int rpm_fetch_archive(struct rpm *rpmst, unsigned char **archive_ret, size_t *len) { if (rpmst == NULL || archive_ret == NULL || len == NULL) return DRPM_ERR_PROG; if ((*archive_ret = malloc(rpmst->archive_size)) == NULL) return DRPM_ERR_MEMORY; memcpy(*archive_ret, rpmst->archive, rpmst->archive_size); *len = rpmst->archive_size; return DRPM_ERR_OK; } /* Writes the RPM to . Will not write the archive unless * is true. May also write an MD5 digest of written * data to . If is false, then this will not include * the lead and signature. */ int rpm_write(struct rpm *rpmst, const char *filename, bool include_archive, unsigned char digest[MD5_DIGEST_LENGTH], bool full_md5) { int error = DRPM_ERR_OK; FD_t file; unsigned char *signature = NULL; size_t signature_len; unsigned char *header = NULL; size_t header_len; MD5_CTX md5; if (rpmst == NULL) return DRPM_ERR_PROG; // hack: extra '\0' to prevent rpmlib from compressing (see rpmio.c) if ((file = Fopen(filename, "wb\0")) == NULL) return DRPM_ERR_IO; if ((error = rpm_export_signature(rpmst, &signature, &signature_len)) != DRPM_ERR_OK || (error = rpm_export_header(rpmst, &header, &header_len)) != DRPM_ERR_OK) goto cleanup; if (Fwrite(rpmst->lead, 1, RPMLEAD_SIZE, file) != RPMLEAD_SIZE || Fwrite(signature, 1, signature_len, file) != (ssize_t)signature_len || Fwrite(header, 1, header_len, file) != (ssize_t)header_len) { error = DRPM_ERR_IO; goto cleanup; } if (digest != NULL) { if (MD5_Init(&md5) != 1 || (full_md5 && (MD5_Update(&md5, rpmst->lead, RPMLEAD_SIZE) != 1 || MD5_Update(&md5, signature, signature_len) != 1)) || MD5_Update(&md5, header, header_len) != 1) { error = DRPM_ERR_OTHER; goto cleanup; } } if (include_archive) { if (Fwrite(rpmst->archive, 1, rpmst->archive_size, file) != (ssize_t)rpmst->archive_size) { error = DRPM_ERR_IO; goto cleanup; } if (digest != NULL && MD5_Update(&md5, rpmst->archive, rpmst->archive_size) != 1) { error = DRPM_ERR_OTHER; goto cleanup; } } if (digest != NULL && MD5_Final(digest, &md5) != 1) { error = DRPM_ERR_OTHER; goto cleanup; } cleanup: Fclose(file); free(signature); free(header); return error; } /* Replaces the lead and signature with data import from . */ int rpm_replace_lead_and_signature(struct rpm *rpmst, unsigned char *leadsig, size_t leadsig_len) { const size_t skip = RPMLEAD_SIZE + sizeof(rpm_header_magic); if (rpmst == NULL || leadsig == NULL || leadsig_len < RPM_LEADSIG_MIN_LEN) return DRPM_ERR_PROG; if (memcmp(leadsig + RPMLEAD_SIZE, rpm_header_magic, 4) != 0) return DRPM_ERR_FORMAT; memcpy(rpmst->lead, leadsig, RPMLEAD_SIZE); headerFree(rpmst->signature); if ((rpmst->signature = headerImport(leadsig + skip, 0, HEADERIMPORT_COPY)) == NULL) return DRPM_ERR_FORMAT; return DRPM_ERR_OK; } /* Checks if this is a source RPM. */ bool rpm_is_sourcerpm(struct rpm *rpmst) { return (headerGetString(rpmst->header, RPMTAG_SOURCERPM) == NULL); } /* Fetches the NEVR string from the header. */ int rpm_get_nevr(struct rpm *rpmst, char **nevr) { if (rpmst == NULL || nevr == NULL) return DRPM_ERR_PROG; if ((*nevr = headerGetAsString(rpmst->header, RPMTAG_NEVR)) == NULL) return DRPM_ERR_MEMORY; return DRPM_ERR_OK; } /* Determines the payload compression from information in the header. */ int rpm_get_comp(struct rpm *rpmst, unsigned short *comp) { const char *payload_comp; if (rpmst == NULL || comp == NULL) return DRPM_ERR_PROG; if ((payload_comp = headerGetString(rpmst->header, RPMTAG_PAYLOADCOMPRESSOR)) == NULL) return DRPM_ERR_FORMAT; if (strcmp(payload_comp, "gzip") == 0) { *comp = DRPM_COMP_GZIP; } else if (strcmp(payload_comp, "bzip2") == 0) { *comp = DRPM_COMP_BZIP2; } else if (strcmp(payload_comp, "lzip") == 0) { *comp = DRPM_COMP_LZIP; } else if (strcmp(payload_comp, "lzma") == 0) { *comp = DRPM_COMP_LZMA; } else if (strcmp(payload_comp, "xz") == 0) { *comp = DRPM_COMP_XZ; } else if (strcmp(payload_comp, "zstd") == 0) { *comp = DRPM_COMP_ZSTD; } else { return DRPM_ERR_FORMAT; } return DRPM_ERR_OK; } /* Determines the compression level from the header. */ int rpm_get_comp_level(struct rpm *rpmst, unsigned short *level) { const char *payload_flags; if (rpmst == NULL || level == NULL) return DRPM_ERR_PROG; if ((payload_flags = headerGetString(rpmst->header, RPMTAG_PAYLOADFLAGS)) == NULL) return DRPM_ERR_FORMAT; /* payload_flags first contains compression level as a string (zero terminated), * here we check that its max length is 2 (max compression level is 99) */ if (strlen(payload_flags) > 2) return DRPM_ERR_FORMAT; *level = atoi(payload_flags); return DRPM_ERR_OK; } /* Determines the digest algorithm used for file checksums in the header. */ int rpm_get_digest_algo(struct rpm *rpmst, unsigned short *digestalgo) { int error = DRPM_ERR_OK; rpmtd digest_algo_array; uint32_t *digest_algo; if (rpmst == NULL || digestalgo == NULL) return DRPM_ERR_PROG; digest_algo_array = rpmtdNew(); if (headerGet(rpmst->header, RPMTAG_FILEDIGESTALGO, digest_algo_array, HEADERGET_EXT | HEADERGET_MINMEM) != 1) { *digestalgo = DIGESTALGO_MD5; } else { if ((digest_algo = rpmtdNextUint32(digest_algo_array)) == NULL) { error = DRPM_ERR_FORMAT; goto cleanup; } switch (*digest_algo) { case RFC4880_HASH_ALGO_MD5: *digestalgo = DIGESTALGO_MD5; break; case RFC4880_HASH_ALGO_SHA256: *digestalgo = DIGESTALGO_SHA256; break; default: error = DRPM_ERR_FORMAT; goto cleanup; } } cleanup: rpmtdFreeData(digest_algo_array); rpmtdFree(digest_algo_array); return error; } /* Determines the payload format from the header. */ int rpm_get_payload_format(struct rpm *rpmst, unsigned short *payfmt) { const char *payload_format; if (rpmst == NULL || payfmt == NULL) return DRPM_ERR_PROG; if ((payload_format = headerGetString(rpmst->header, RPMTAG_PAYLOADFORMAT)) == NULL) return DRPM_ERR_MEMORY; if (strcmp(payload_format, "drpm") == 0) { *payfmt = RPM_PAYLOAD_FORMAT_DRPM; } else if (strcmp(payload_format, "cpio") == 0) { *payfmt = RPM_PAYLOAD_FORMAT_CPIO; } else if (strcmp(payload_format, "xar") == 0) { *payfmt = RPM_PAYLOAD_FORMAT_XAR; } else { return DRPM_ERR_FORMAT; } return DRPM_ERR_OK; } /* Replaces the payload format information in the header. */ int rpm_patch_payload_format(struct rpm *rpmst, const char *new_payfmt) { if (rpmst == NULL || new_payfmt == NULL) return DRPM_ERR_PROG; rpm_header_unload_region(rpmst, RPMTAG_HEADERIMMUTABLE); if (headerDel(rpmst->header, RPMTAG_PAYLOADFORMAT) != 0) return DRPM_ERR_FORMAT; if (headerPutString(rpmst->header, RPMTAG_PAYLOADFORMAT, new_payfmt) != 1) return DRPM_ERR_FORMAT; rpmst->header = headerReload(rpmst->header, RPMTAG_HEADERIMMUTABLE); return DRPM_ERR_OK; } /* Fetches a list of file information from the header. */ int rpm_get_file_info(struct rpm *rpmst, struct file_info **files_ret, size_t *count_ret, bool *colors_ret) { int error = DRPM_ERR_OK; const struct file_info file_info_init = {0}; struct file_info *files; size_t count; bool colors; rpmtd filenames; rpmtd fileflags; rpmtd filemd5s; rpmtd filerdevs; rpmtd filesizes; rpmtd filemodes; rpmtd fileverify; rpmtd filelinktos; rpmtd filecolors; const char *name; uint32_t *flags; const char *md5; uint16_t *rdev; uint32_t *size; uint16_t *mode; uint32_t *verify; const char *linkto; uint32_t *color = NULL; if (rpmst == NULL || files_ret == NULL || count_ret == NULL) return DRPM_ERR_PROG; filenames = rpmtdNew(); fileflags = rpmtdNew(); filemd5s = rpmtdNew(); filerdevs = rpmtdNew(); filesizes = rpmtdNew(); filemodes = rpmtdNew(); fileverify = rpmtdNew(); filelinktos = rpmtdNew(); filecolors = rpmtdNew(); if (headerGet(rpmst->header, RPMTAG_FILENAMES, filenames, HEADERGET_EXT) != 1 || headerGet(rpmst->header, RPMTAG_FILEFLAGS, fileflags, HEADERGET_MINMEM) != 1 || headerGet(rpmst->header, RPMTAG_FILEMD5S, filemd5s, HEADERGET_MINMEM) != 1 || headerGet(rpmst->header, RPMTAG_FILERDEVS, filerdevs, HEADERGET_MINMEM) != 1 || headerGet(rpmst->header, RPMTAG_FILESIZES, filesizes, HEADERGET_MINMEM) != 1 || headerGet(rpmst->header, RPMTAG_FILEMODES, filemodes, HEADERGET_MINMEM) != 1 || headerGet(rpmst->header, RPMTAG_FILEVERIFYFLAGS, fileverify, HEADERGET_MINMEM) != 1 || headerGet(rpmst->header, RPMTAG_FILELINKTOS, filelinktos, HEADERGET_MINMEM) != 1) { *count_ret = 0; goto cleanup; } colors = (colors_ret == NULL) ? false : (headerGet(rpmst->header, RPMTAG_FILECOLORS, filecolors, HEADERGET_MINMEM) == 1); count = rpmtdCount(filenames); if (count != rpmtdCount(fileflags) || count != rpmtdCount(filemd5s) || count != rpmtdCount(filerdevs) || count != rpmtdCount(filesizes) || count != rpmtdCount(filemodes) || count != rpmtdCount(fileverify) || count != rpmtdCount(filelinktos) || (colors && count != rpmtdCount(filecolors))) { error = DRPM_ERR_FORMAT; goto cleanup; } if ((files = malloc(count * sizeof(struct file_info))) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } for (size_t i = 0; i < count; i++) files[i] = file_info_init; for (size_t i = 0; i < count; i++) { if ((name = rpmtdNextString(filenames)) == NULL || (flags = rpmtdNextUint32(fileflags)) == NULL || (md5 = rpmtdNextString(filemd5s)) == NULL || (size = rpmtdNextUint32(filesizes)) == NULL || (verify = rpmtdNextUint32(fileverify)) == NULL || (linkto = rpmtdNextString(filelinktos)) == NULL || (colors && (color = rpmtdNextUint32(filecolors)) == NULL) || rpmtdNext(filerdevs) < 0 || rpmtdNext(filemodes) < 0 || (rdev = rpmtdGetUint16(filerdevs)) == NULL || (mode = rpmtdGetUint16(filemodes)) == NULL) { error = DRPM_ERR_FORMAT; goto cleanup_files; } if ((files[i].name = malloc(strlen(name) + 1)) == NULL || (files[i].md5 = malloc(strlen(md5) + 1)) == NULL || (files[i].linkto = malloc(strlen(linkto) + 1)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup_files; } strcpy(files[i].name, name); files[i].flags = *flags; strcpy(files[i].md5, md5); files[i].rdev = *rdev; files[i].size = *size; files[i].mode = *mode; files[i].verify = *verify; strcpy(files[i].linkto, linkto); if (colors) files[i].color = *color; } *files_ret = files; *count_ret = count; if (colors_ret != NULL) *colors_ret = colors; goto cleanup; cleanup_files: for (size_t i = 0; i < count; i++) { free(files[i].name); free(files[i].md5); free(files[i].linkto); } free(files); cleanup: rpmtdFreeData(filenames); rpmtdFreeData(fileflags); rpmtdFreeData(filemd5s); rpmtdFreeData(filerdevs); rpmtdFreeData(filesizes); rpmtdFreeData(filemodes); rpmtdFreeData(fileverify); rpmtdFreeData(filelinktos); rpmtdFreeData(filecolors); rpmtdFree(filenames); rpmtdFree(fileflags); rpmtdFree(filemd5s); rpmtdFree(filerdevs); rpmtdFree(filesizes); rpmtdFree(filemodes); rpmtdFree(fileverify); rpmtdFree(filelinktos); rpmtdFree(filecolors); return error; } /* Calculates the offset of the payload format string in the header. */ int rpm_find_payload_format_offset(struct rpm *rpmst, uint32_t *offset) { unsigned char *header; size_t header_size; uint32_t index_count; int error; if (rpmst == NULL || offset == NULL) return DRPM_ERR_PROG; if ((error = rpm_export_header(rpmst, &header, &header_size)) != DRPM_ERR_OK) return error; error = DRPM_ERR_FORMAT; index_count = parse_be32(header + 8); for (uint32_t i = 0, off = 16; i < index_count && off+16 <= header_size; i++, off += 16) { if (parse_be32(header + off) == RPMTAG_PAYLOADFORMAT) { *offset = parse_be32(header + off + 8); error = DRPM_ERR_OK; goto cleanup; } } cleanup: free(header); return error; } /* Empties the signature. */ int rpm_signature_empty(struct rpm *rpmst) { if (rpmst == NULL) return DRPM_ERR_PROG; headerFree(rpmst->signature); rpmst->signature = headerNew(); return DRPM_ERR_OK; } /* Sets size tag in the signature. * Should be equal to the size all data following the signature. */ int rpm_signature_set_size(struct rpm *rpmst, uint32_t size) { rpmtd tag_data; if (rpmst == NULL) return DRPM_ERR_PROG; tag_data = rpmtdNew(); tag_data->tag = RPMSIGTAG_SIZE; tag_data->type = RPM_INT32_TYPE; tag_data->data = &size; tag_data->count = 1; headerPut(rpmst->signature, tag_data, HEADERPUT_DEFAULT); rpmtdFree(tag_data); return DRPM_ERR_OK; } /* Sets MD5 tag in the signature. * Should be equal to the MD5 sum of all data following the signature. */ int rpm_signature_set_md5(struct rpm *rpmst, unsigned char md5[MD5_DIGEST_LENGTH]) { rpmtd tag_data; if (rpmst == NULL || md5 == NULL) return DRPM_ERR_PROG; tag_data = rpmtdNew(); tag_data->tag = RPMSIGTAG_MD5; tag_data->type = RPM_BIN_TYPE; tag_data->data = md5; tag_data->count = MD5_DIGEST_LENGTH; headerPut(rpmst->signature, tag_data, HEADERPUT_DEFAULT); rpmtdFree(tag_data); return DRPM_ERR_OK; } /* Reloads the signature to accommodate for changes. */ int rpm_signature_reload(struct rpm *rpmst) { rpmst->signature = headerReload(rpmst->signature, RPMTAG_HEADERSIGNATURES); return DRPM_ERR_OK; } /* Fetches the MD5 sum from the signature. */ int rpm_signature_get_md5(struct rpm *rpmst, unsigned char md5[MD5_DIGEST_LENGTH], bool *has_md5) { int error = DRPM_ERR_OK; rpmtd tag_data; if (rpmst == NULL || md5 == NULL || has_md5 == NULL) return DRPM_ERR_PROG; tag_data = rpmtdNew(); if ((*has_md5 = (headerGet(rpmst->signature, RPMSIGTAG_MD5, tag_data, HEADERGET_MINMEM) == 1))) { if (tag_data->count != MD5_DIGEST_LENGTH) { error = DRPM_ERR_FORMAT; goto cleanup; } memcpy(md5, tag_data->data, MD5_DIGEST_LENGTH); } cleanup: rpmtdFree(tag_data); return error; } /* Reads only the header of an installed RPM from the database. * The RPM is identified by its string. */ int rpm_read_header(struct rpm **rpmst, const char *nevr, const char *arch) { int error = DRPM_ERR_OK; rpmts trans = NULL; rpmdbMatchIterator iter = NULL; char *name; char *epoch = NULL; char *version; char *release; char *str = NULL; unsigned char *header = NULL; size_t header_size; if (rpmst == NULL || nevr == NULL) return DRPM_ERR_PROG; if ((*rpmst = malloc(sizeof(struct rpm))) == NULL || (str = malloc(strlen(nevr) + 1)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } rpm_init(*rpmst); strcpy(str, nevr); release = strrchr(str, '-'); if (release == NULL || release == str) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } *release++ = '\0'; version = strrchr(str, ':'); if (version == str) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } if (version == NULL) { version = strrchr(str, '-'); if (version == NULL || version == str) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } *version++ = '\0'; } else { *version++ = '\0'; epoch = strrchr(str, '-'); if (epoch == NULL || epoch == str) { error = DRPM_ERR_FORMAT; goto cleanup_fail; } *epoch++ = '\0'; } name = str; rpmReadConfigFiles(NULL, NULL); trans = rpmtsCreate(); iter = rpmtsInitIterator(trans, RPMTAG_NAME, name, 0); rpmdbSetIteratorRE(iter, RPMTAG_EPOCH, RPMMIRE_STRCMP, epoch); rpmdbSetIteratorRE(iter, RPMTAG_VERSION, RPMMIRE_STRCMP, version); rpmdbSetIteratorRE(iter, RPMTAG_RELEASE, RPMMIRE_STRCMP, release); if (arch) rpmdbSetIteratorRE(iter, RPMTAG_ARCH, RPMMIRE_STRCMP, arch); if (((*rpmst)->header = rpmdbNextIterator(iter)) == NULL) { error = DRPM_ERR_NOINSTALL; goto cleanup_fail; } if ((error = rpm_export_header(*rpmst, &header, &header_size)) != DRPM_ERR_OK) goto cleanup_fail; if (((*rpmst)->header = headerImport(header + sizeof(rpm_header_magic), 0, HEADERIMPORT_COPY)) == NULL) { error = DRPM_ERR_OTHER; goto cleanup_fail; } goto cleanup; cleanup_fail: if (*rpmst != NULL) { rpm_free(*rpmst); free(*rpmst); *rpmst = NULL; } cleanup: rpmdbFreeIterator(iter); rpmtsFree(trans); free(str); return error; } drpm-0.5.1/src/drpm_search.c000066400000000000000000000502411421165313000156770ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2015 Red Hat This program is licensed under the BSD license, read LICENSE.BSD for further information. */ /* Copyright 2004,2005 Michael Schroeder rewritten from bsdiff.c, http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/bsdiff added library interface and hash method, enhanced suffix method. */ /*- * Copyright 2003-2005 Colin Percival * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted providing that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "drpm.h" #include "drpm_private.h" #include #include #include #define MIN_MISMATCHES 32 static size_t match_len(const unsigned char *, size_t, const unsigned char *, size_t); static uint32_t buzhash(const unsigned char *); static int bucketsort(long long *, long long *, size_t, size_t); static void suffix_split(long long *, long long *, size_t, size_t, size_t); static size_t suffix_search(const long long *, const unsigned char *, size_t, const unsigned char *, size_t, size_t, size_t, size_t *); size_t match_len(const unsigned char *old, size_t old_len, const unsigned char *new, size_t new_len) { size_t i; size_t len = MIN(old_len, new_len); for (i = 0; i < len; i++) if (old[i] != new[i]) break; return i; } /********************************* hash *********************************/ #define HSIZESHIFT 4 #define HSIZE (1 << HSIZESHIFT) struct hash { size_t *hash_table; size_t ht_len; }; /* 256 random numbers generated by a quantum source */ static const uint32_t noise[256] = { 0x9BE502A4, 0xBA7180EA, 0x324E474F, 0x0AAB8451, 0x0CED3810, 0x2158A968, 0x6BBD3771, 0x75A02529, 0x41F05C14, 0xC2264B87, 0x1F67B359, 0xCD2D031D, 0x49DC0C04, 0xA04AE45C, 0x6ADE28A7, 0x2D0254FF, 0xDEC60C7C, 0xDEF5C084, 0x0F77FFC8, 0x112021F6, 0x5F6D581E, 0xE35EA3DF, 0x3216BFB4, 0xD5A3083D, 0x7E63E9CD, 0xAA9208F6, 0xDA3F3978, 0xFE0E2547, 0x09DFB020, 0xD97472C5, 0xBBCE2EDE, 0x121AEBD2, 0x0E9FDBEB, 0x7B6F5D9C, 0x84938E43, 0x30694F2D, 0x86B7A7F8, 0xEFAF5876, 0x263812E6, 0xB6E48DDF, 0xCE8ED980, 0x4DF591E1, 0x75257B35, 0x2F88DCFF, 0xA461FE44, 0xCA613B4D, 0xD9803F73, 0xEA056205, 0xCCCA7A89, 0x0F2DBB07, 0xC53E359E, 0xE80D0137, 0x2B2D2A5D, 0xCFC1391A, 0x2BB3B6C5, 0xB66AEA3C, 0x00EA419E, 0xCE5ADA84, 0xAE1D6712, 0x12F576BA, 0x117FCBC4, 0xA9D4C775, 0x25B3D616, 0xEFDA65A8, 0xAFF3EF5B, 0x00627E68, 0x668D1E99, 0x088D0EEF, 0xF8FAC24D, 0xE77457C7, 0x68D3BEB4, 0x921D2ACB, 0x9410EAC9, 0xD7F24399, 0xCBDEC497, 0x98C99AE1, 0x65802B2C, 0x81E1C3C4, 0xA130BB09, 0x17A87BAD, 0xA70367D6, 0x148658D4, 0x02F33377, 0x8620D8B6, 0xBDAC25BD, 0xB0A6DE51, 0xD64C4571, 0xA4185BA0, 0xA342D70F, 0x3F1DC4C1, 0x042DC3CE, 0x0DE89F43, 0xA69B1867, 0x3C064E11, 0xAD1E2C3E, 0x9660E8CD, 0xD36B09CA, 0x4888F228, 0x61A9AC3C, 0xD9561118, 0x3532797E, 0x71A35C22, 0xECC1376C, 0xAB31E656, 0x88BD0D35, 0x423B20DD, 0x38E4651C, 0x3C6397A4, 0x4A7B12D9, 0x08B1CF33, 0xD0604137, 0xB035FDB8, 0x4916DA23, 0xA9349493, 0xD83DAA9B, 0x145F7D95, 0x868531D6, 0xACB18F17, 0x9CD33B6F, 0x193E42B9, 0x26DFDC42, 0x5069D8FA, 0x5BEE24EE, 0x5475D4C6, 0x315B2C0C, 0xF764EF45, 0x01B6F4EB, 0x60BA3225, 0x8A16777C, 0x4C05CD28, 0x53E8C1D2, 0xC8A76CE5, 0x8045C1E6, 0x61328752, 0x2EBAD322, 0x3444F3E2, 0x91B8AF11, 0xB0CEE675, 0x55DBFF5A, 0xF7061EE0, 0x27D7D639, 0xA4AEF8C9, 0x42FF0E4F, 0x62755468, 0x1C6CA3F3, 0xE4F522D1, 0x2765FCB3, 0xE20C8A95, 0x3A69AEA7, 0x56AB2C4F, 0x8551E688, 0xE0BC14C2, 0x278676BF, 0x893B6102, 0xB4F0AB3B, 0xB55DDDA9, 0xA04C521F, 0xC980088E, 0x912AEAC1, 0x08519BAD, 0x991302D3, 0x5B91A25B, 0x696D9854, 0x9AD8B4BF, 0x41CB7E21, 0xA65D1E03, 0x85791D29, 0x89478AA7, 0x4581E337, 0x59BAE0B1, 0xE0FC9DF3, 0x45D9002C, 0x7837464F, 0xDA22DE3A, 0x1DC544BD, 0x601D8BAD, 0x668B0ABC, 0x7A5EBFB1, 0x3AC0B624, 0x5EE16D7D, 0x9BFAC387, 0xBE8EF20C, 0x8D2AE384, 0x819DC7D5, 0x7C4951E7, 0xE60DA716, 0x0C5B0073, 0xB43B3D97, 0xCE9974ED, 0x0F691DA9, 0x4B616D60, 0x8FA9E819, 0x3F390333, 0x6F62FAD6, 0x5A32B67C, 0x3BE6F1C3, 0x05851103, 0xFF28828D, 0xAA43A56A, 0x075D7DD5, 0x248C4B7E, 0x52FDE3EB, 0xF72E2EDA, 0x5DA6F75F, 0x2F5148D9, 0xCAE2AEAE, 0xFDA6F3E5, 0xFF60D8FF, 0x2ADC02D2, 0x1DBDBD4C, 0xD410AD7C, 0x8C284AAE, 0x392EF8E0, 0x37D48B3A, 0x6792FE9D, 0xAD32DDFA, 0x1545F24E, 0x3A260F73, 0xB724CA36, 0xC510D751, 0x4F8DF992, 0x000B8B37, 0x292E9B3D, 0xA32F250F, 0x8263D144, 0xFCAE0516, 0x1EAE2183, 0xD4AF2027, 0xC64AFAE3, 0xE7B34FE4, 0xDF864AEA, 0x80CC71C5, 0x0E814DF3, 0x66CC5F41, 0x853A497A, 0xA2886213, 0x5E34A2EA, 0x0F53BA47, 0x718C484A, 0xFA0F0B12, 0x33CC59FF, 0x72B48E07, 0x8B6F57BC, 0x29CF886D, 0x1950955B, 0xCD52910C, 0x4CECEF65, 0x05C2CBFE, 0x49DF4F6A, 0x1F4C3F34, 0xFADC1A09, 0xF2D65A24, 0x117F5594, 0xDE3A84E6, 0x48DB3024, 0xD10CA9B5 }; /* buzhash by Robert C. Uzgalis * General hash functions. Technical Report TR-92-01, * The University of Hong Kong, 1993 */ uint32_t buzhash(const unsigned char *buf) { uint32_t x = 0x83D31DF4; for (unsigned i = 0; i < HSIZE; i++) x = (x << 1) ^ (x & (1u << 31) ? 1 : 0) ^ noise[*buf++]; return x; } int hash_create(struct hash **hsh, const unsigned char *old, size_t old_len) { size_t *hash_table; size_t ht_len; size_t key; const unsigned char * const old_ptr = old; size_t primes[] = { 65537, 98317, 147481, 221227, 331841, 497771, 746659, 1120001, 1680013, 2520031, 3780053, 5670089, 8505137, 12757739, 19136609, 28704913, 43057369, 64586087, 96879131, 145318741, 217978121, 326967209, 490450837, 735676303, 1103514463, 1655271719, 0xFFFFFFFF }; size_t i; const size_t i_limit = sizeof(primes)/sizeof(*primes) - 1; if ((*hsh = malloc(sizeof(struct hash))) == NULL) return DRPM_ERR_MEMORY; ht_len = 4 * ((old_len + HSIZE - 1) >> HSIZESHIFT); for (i = 0; i < i_limit; i++) { if (ht_len < primes[i]) break; } ht_len = primes[i]; if ((hash_table = calloc(ht_len, sizeof(size_t))) == NULL) { free(*hsh); return DRPM_ERR_MEMORY; } for (size_t off = 0; old_len >= HSIZE; off += HSIZE, old += HSIZE, old_len -= HSIZE) { key = buzhash(old) % ht_len; if (hash_table[key]) { if (hash_table[(key == ht_len - 1) ? 0 : key + 1]) continue; if (memcmp(old, old_ptr + hash_table[key], HSIZE) == 0) continue; key = (key == ht_len - 1) ? 0 : key + 1; } hash_table[key] = off + 1; } (*hsh)->hash_table = hash_table; (*hsh)->ht_len = ht_len; return DRPM_ERR_OK; } void hash_free(struct hash **hsh) { free((*hsh)->hash_table); free(*hsh); } size_t hash_search(struct hash *hsh, const unsigned char *old, size_t old_len, const unsigned char *new, size_t new_len, size_t last_offset, size_t scan, size_t *pos_ret, size_t *len_ret) { size_t *hash_table = hsh->hash_table; size_t ht_len = hsh->ht_len; size_t last_scan = 0; size_t last_pos = 0; size_t last_len = 0; size_t miniscan; size_t scan_start = scan; size_t old_score = 0; size_t old_score_num = 0; size_t old_score_start = 0; size_t pos = 0; size_t len = 0; size_t pos2; size_t len2; uint32_t key; uint32_t key2; uint32_t prekey = (scan <= new_len - HSIZE) ? buzhash(new + scan) : 0; uint32_t xprekey; hash_table = hsh->hash_table; ht_len = hsh->ht_len; scan_start = scan; old_score = old_score_num = old_score_start = 0; prekey = (scan <= new_len - HSIZE) ? buzhash(new + scan) : 0; pos = 0; len = 0; last_pos = last_scan = last_len = 0; while (true) { if (scan >= new_len - HSIZE) { if (last_len >= 32) goto gotit; break; } key = prekey % ht_len; pos = hash_table[key]; if (pos == 0) { scannext: if (last_len >= 32 && scan - last_scan >= HSIZE) goto gotit; prekey = (prekey << 1) ^ (prekey & (1u << 31) ? 1 : 0) ^ noise[new[scan + HSIZE]]; xprekey = noise[new[scan]] ^ (0x83D31DF4 ^ 0x07A63BE9); #if HSIZE % 32 != 0 prekey ^= (xprekey << (HSIZE % 32)) ^ (xprekey >> (32 - (HSIZE % 32))); #else prekey ^= xprekey; #endif scan++; continue; } pos--; if (memcmp(old + pos, new + scan, HSIZE) != 0) { pos = hash_table[key + 1 == ht_len ? 0 : key + 1]; if (pos == 0) goto scannext; pos--; if (memcmp(old + pos, new + scan, HSIZE) != 0) goto scannext; } len = match_len(old + pos + HSIZE, old_len - pos - HSIZE, new + scan + HSIZE, new_len - scan - HSIZE) + HSIZE; if (scan + HSIZE * 4 <= new_len) { key2 = buzhash(new + scan + 3 * HSIZE) % ht_len; pos2 = hash_table[key2]; if (pos2) { if (memcmp(new + scan + 3 * HSIZE, old + pos2 - 1, HSIZE) != 0) { key2 = (key2 == ht_len - 1) ? 0 : key2 + 1; pos2 = hash_table[key2]; } } if (pos2 > 1 + 3 * HSIZE) { pos2 -= 1 + 3 * HSIZE; if (pos2 != pos) { len2 = match_len(old + pos2, old_len - pos2, new + scan, new_len - scan); if (len2 > len) { pos = pos2; len = len2; } } } } if (len > last_len) { last_len = len; last_pos = pos; last_scan = scan; } goto scannext; gotit: scan = last_scan; len = last_len; pos = last_pos; if (scan + last_offset == pos) { scan += len; scan_start = scan; if (scan + HSIZE < new_len) prekey = buzhash(new + scan); last_len = 0; continue; } for (size_t i = scan - scan_start; i > 0 && pos > 0 && scan > 0 && old[pos - 1] == new[scan - 1]; i--) { len++; pos--; scan--; } if (old_score_start + 1 != scan || old_score_num == 0 || old_score_num - 1 > len) { old_score = 0; for (miniscan = scan; miniscan < scan + len; miniscan++) if ((miniscan + last_offset < old_len) && (old[miniscan + last_offset] == new[miniscan])) old_score++; old_score_start = scan; old_score_num = len; } else { if (old_score_start + last_offset < old_len && old[old_score_start + last_offset] == new[old_score_start]) old_score--; old_score_start++; old_score_num--; for (miniscan = old_score_start + old_score_num; old_score_num < len; miniscan++) { if ((miniscan + last_offset < old_len) && (old[miniscan + last_offset] == new[miniscan])) old_score++; old_score_num++; } } if (len - old_score >= 32) break; if (len > 3 * HSIZE + 32) scan += len - (3 * HSIZE + 32); if (scan <= last_scan) scan = last_scan + 1; scan_start = scan; if (scan + HSIZE < new_len) prekey = buzhash(new + scan); last_len = 0; } if (scan >= new_len - HSIZE) { scan = new_len; pos = 0; len = 0; } *pos_ret = pos; *len_ret = len; return scan; } /**************************** suffix sort ****************************/ struct sfxsrt { long long *I; // suffix array size_t F[257]; // key = byte value, // value = where to start looking in suffix array }; int sfxsrt_create(struct sfxsrt **suf, const unsigned char *old, size_t old_len) { int error = DRPM_ERR_OK; long long *I = NULL; long long *V = NULL; size_t h; size_t bucket_len; size_t len; size_t val; size_t l; size_t i; uint32_t oldv; size_t F[257] = {0}; if (suf == NULL || old == NULL) return DRPM_ERR_PROG; if ((*suf = malloc(sizeof(struct sfxsrt))) == NULL || (I = calloc((old_len + 3), sizeof(long long))) == NULL || (V = calloc((old_len + 3), sizeof(long long))) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup_fail; } if (old_len > 0xFFFFFF) { bucket_len = 0x1000002; h = 3; F[old[0]]++; F[old[1]]++; oldv = old[0] << 8 | old[1]; for (size_t i = 2; i < old_len; i++) { F[old[i]]++; oldv = (oldv & 0xFFFF) << 8 | old[i]; V[i - 2] = oldv + 2; } oldv = (oldv & 0xFFFF) << 8; V[old_len - 2] = oldv + 2; oldv = (oldv & 0xFFFF) << 8; V[old_len - 1] = oldv + 2; len = old_len + 2; V[len - 2] = 1; V[len - 1] = 0; } else { bucket_len = 0x10001; h = 2; F[old[0]]++; oldv = old[0]; for (size_t i = 1; i < old_len; i++) { F[old[i]]++; oldv = (oldv & 0xFF) << 8 | old[i]; V[i - 1] = oldv + 1; } oldv = (oldv & 0xFF) << 8; V[old_len - 1] = oldv + 1; len = old_len + 1; V[len - 1] = 0; } val = len; for (unsigned short i = 256; i > 0; val -= F[--i]) F[i] = val; F[0] = val; if ((error = bucketsort(I, V, len, bucket_len)) != DRPM_ERR_OK) goto cleanup_fail; len++; for ( ; I[0] != -(long long)len; h += h) { l = 0; for (i = 0; i < len; ) { if (I[i] < 0) { l -= I[i]; i -= I[i]; } else { if (l > 0) I[i - l] = -(long long)l; l = V[I[i]] + 1 - i; suffix_split(I, V, i, l, h); i += l; l = 0; } } if (l > 0) I[i - l] = -(long long)l; } for (i = 0; i < len; i++) I[V[i]] = i; (*suf)->I = I; memcpy((*suf)->F, F, sizeof(size_t) * 257); goto cleanup; cleanup_fail: free(*suf); free(I); cleanup: free(V); return error; } void sfxsrt_free(struct sfxsrt **suf) { free((*suf)->I); free(*suf); } size_t sfxsrt_search(struct sfxsrt *suf, const unsigned char *old, size_t old_len, const unsigned char *new, size_t new_len, size_t last_offset, size_t scan, size_t *pos_ret, size_t *len_ret) { size_t len = 0; size_t old_score = 0; size_t miniscan = scan; *pos_ret = 0; while (scan < new_len) { len = suffix_search(suf->I, old, old_len, new + scan, new_len - scan, suf->F[new[scan]] + 1, suf->F[new[scan] + 1], pos_ret); const size_t miniscan_limit = MIN(scan + len, old_len - last_offset); for ( ; miniscan < miniscan_limit; miniscan++) if (old[miniscan + last_offset] == new[miniscan]) old_score++; miniscan = scan + len; if (len > 0 && len == old_score) { scan += len; miniscan = scan; old_score = 0; continue; } if (len - old_score > MIN_MISMATCHES) break; if (scan + last_offset < old_len && old[scan + last_offset] == new[scan]) old_score--; scan++; } *len_ret = len; return scan; } int bucketsort(long long *I, long long *V, size_t len, size_t bucket_len) { size_t *B; size_t c, d, i, j, g; if ((B = calloc(bucket_len, sizeof(size_t))) == NULL) return DRPM_ERR_MEMORY; for (i = len; i > 0; i--) { c = V[i - 1]; V[i - 1] = B[c]; B[c] = i; } for (j = bucket_len - 1, i = len; i > 0; j--) { for (d = B[j], g = i; d > 0; i--) { c = d - 1; d = V[c]; V[c] = g; I[i] = (d == 0 && g == i) ? -1 : (long long)c; } } V[len] = 0; I[0] = -1; free(B); return DRPM_ERR_OK; } void suffix_split(long long *I, long long *V, size_t start, size_t len, size_t h) { size_t i, j, k, jj, kk; long long x, tmp; const size_t end = start + len; if (len < 16) { for (k = start; k < end; k += j) { j = 1; x = V[I[k] + h]; for (i = 1; k + i < end; i++) { if (V[I[k+i] + h] < x) { x = V[I[k+i] + h]; j = 0; } if (V[I[k+i] + h] == x) { tmp = I[k+j]; I[k+j] = I[k+i]; I[k+i] = tmp; j++; } } for (i = 0; i < j; i++) V[I[k + i]] = k + j - 1; if (j == 1) I[k] = -1; } return; } x = V[I[start + len/2] + h]; jj = 0; kk = 0; for (i = start; i < end; i++) { if (V[I[i] + h] < x) jj++; if (V[I[i] + h] == x) kk++; } jj += start; kk += jj; i = start; j = 0; k = 0; while (i < jj) { if (V[I[i] + h] < x) { i++; } else if (V[I[i] + h] == x) { tmp = I[i]; I[i] = I[jj + j]; I[jj + j] = tmp; j++; } else { tmp = I[i]; I[i] = I[kk + k]; I[kk + k] = tmp; k++; } } while (jj + j < kk) { if (V[I[jj+j] + h] == x) { j++; } else { tmp = I[jj + j]; I[jj + j] = I[kk + k]; I[kk + k] = tmp; k++; } } if(jj > start) suffix_split(I, V, start, jj - start, h); for (i = 0; i < kk - jj; i++) V[I[jj + i]] = kk - 1; if (jj == kk - 1) I[jj] = -1; if (end > kk) suffix_split(I, V, kk, end - kk, h); } size_t suffix_search(const long long *sfxar, const unsigned char *old, size_t old_len, const unsigned char *new, size_t new_len, size_t start, size_t end, size_t *pos_ret) { size_t halfway; size_t len_1; size_t len_2; if (start > end) return 0; if (start == end) { *pos_ret = sfxar[start]; return match_len(old + sfxar[start], old_len - sfxar[start], new, new_len); } while (end - start >= 2) { halfway = start + (end - start) / 2; if (memcmp(old + sfxar[halfway], new, MIN(new_len, old_len - sfxar[halfway])) < 0) start = halfway; else end = halfway; } len_1 = match_len(old + sfxar[start], old_len - sfxar[start], new, new_len); len_2 = match_len(old + sfxar[end], old_len - sfxar[end], new, new_len); *pos_ret = sfxar[len_1 > len_2 ? start : end]; return MAX(len_1, len_2); } drpm-0.5.1/src/drpm_utils.c000066400000000000000000000117311421165313000155730ustar00rootroot00000000000000/* Authors: Pavel Tobias Matej Chalk Copyright (C) 2014 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include #include #include #include #include #include static bool resize(void **, size_t, size_t, size_t); /* Reads 16-byte integer in network byte order buffer. */ uint16_t parse_be16(const unsigned char buffer[2]) { return (0xFF00 & (buffer[0] << 8)) | (0x00FF & buffer[1]); } /* Reads 32-byte integer in network byte order from buffer. */ uint32_t parse_be32(const unsigned char buffer[4]) { return (0xFF000000 & (buffer[0] << 24)) | (0x00FF0000 & (buffer[1] << 16)) | (0x0000FF00 & (buffer[2] << 8)) | (0x000000FF & buffer[3]); } /* Reads 64-byte integer in network byte order from buffer. */ uint64_t parse_be64(const unsigned char buffer[8]) { return (0xFF00000000000000 & ((uint64_t)buffer[0] << 56)) | (0x00FF000000000000 & ((uint64_t)buffer[1] << 48)) | (0x0000FF0000000000 & ((uint64_t)buffer[2] << 40)) | (0x000000FF00000000 & ((uint64_t)buffer[3] << 32)) | (0x00000000FF000000 & ((uint64_t)buffer[4] << 24)) | (0x0000000000FF0000 & ((uint64_t)buffer[5] << 16)) | (0x000000000000FF00 & ((uint64_t)buffer[6] << 8)) | (0x00000000000000FF & (uint64_t)buffer[7]); } /* Writes 32-byte integer in network byte order to buffer. */ void create_be32(uint32_t in, unsigned char out[4]) { out[0] = in >> 24; out[1] = in >> 16; out[2] = in >> 8; out[3] = in; } /* Writes 64-byte integer in network byte order to buffer. */ void create_be64(uint64_t in, unsigned char out[8]) { out[0] = in >> 56; out[1] = in >> 48; out[2] = in >> 40; out[3] = in >> 32; out[4] = in >> 24; out[5] = in >> 16; out[6] = in >> 8; out[7] = in; } int md5_update_be32(MD5_CTX *md5, uint32_t number) { unsigned char be32[4]; create_be32(number, be32); return MD5_Update(md5, be32, 4); } /* Represents array of bytes pointed to by of size * as human-readable ASCII hexadecimals and stores this string in * (should be at least of size * 2 + 1). */ void dump_hex(char *dest, const unsigned char *source, size_t count) { char digits[] = "0123456789abcdef"; dest[count * 2] = '\0'; while (count--) { dest[count * 2] = digits[source[count] >> 4 & 0x0F]; dest[count * 2 + 1] = digits[source[count] & 0x0F]; } } /* Converts human-readable hexadecimal string () * to array of bytes (), which should have enough space for * strlen() / 2 bytes. * Returns length of array, or -1 on error. */ ssize_t parse_hex(unsigned char *dest, const char *source) { ssize_t byte; size_t count; count = strlen(source) / 2; for (size_t i = 0; i < count; i++, source += 2) { if ((byte = parse_hexnum(source, 2)) < 0) return -1; dest[i] = byte; } return count; } ssize_t parse_hexnum(const char *str, size_t size) { size_t ret = 0; for (size_t i = 0; i < size; i++) { ret *= 16; if (isdigit(str[i])) ret += str[i] - '0'; else if (isxdigit(str[i])) ret += toupper(str[i]) - 'A' + 0xA; else return -1; } return ret; } bool parse_md5(unsigned char *dest, const char *source) { return parse_hex(dest, source) == MD5_DIGEST_LENGTH; } bool parse_sha256(unsigned char *dest, const char *source) { return parse_hex(dest, source) == SHA256_DIGEST_LENGTH; } /* Reallocates memory for <*buffer> bytes at a time>. */ bool resize(void **buffer, size_t members_count, size_t member_size, size_t threshold) { void *buf_tmp; if (members_count % threshold == 0) { if ((buf_tmp = realloc(*buffer, member_size * (members_count + threshold))) == NULL) return false; *buffer = buf_tmp; } return true; } bool resize16(void **buffer, size_t members_count, size_t member_size) { return resize(buffer, members_count, member_size, 16); } bool resize32(void **buffer, size_t members_count, size_t member_size) { return resize(buffer, members_count, member_size, 32); } drpm-0.5.1/src/drpm_write.c000066400000000000000000000322311421165313000155630ustar00rootroot00000000000000/* Authors: Matej Chalk Copyright (C) 2015 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "drpm.h" #include "drpm_private.h" #include #include #include #include #include /* Wrapper for struct compstrm. Used to prepend uncompressed header. */ struct compstrm_wrapper { struct compstrm *strm; // compression stream int filedesc; // file descriptor size_t uncomp_len; // length of uncompressed data size_t uncomp_left; // how much uncompressed data left to write unsigned char *uncomp_data; // uncompressed data }; /* Writes 32-byte integer in network byte order to file. */ int write_be32(int filedesc, uint32_t number) { unsigned char nbo[4]; create_be32(number, nbo); if (write(filedesc, nbo, 4) != 4) return DRPM_ERR_IO; return DRPM_ERR_OK; } /* Writes 64-byte integer in network byte order to file. */ int write_be64(int filedesc, uint64_t number) { unsigned char nbo[8]; create_be64(number, nbo); if (write(filedesc, nbo, 8) != 8) return DRPM_ERR_IO; return DRPM_ERR_OK; } /* Writes out the DeltaRPM. */ int write_deltarpm(struct deltarpm *delta) { int error = DRPM_ERR_OK; int filedesc = -1; struct compstrm *stream = NULL; uint32_t tgt_nevr_len; uint32_t src_nevr_len; char version[5]; uint32_t tgt_comp; uint32_t int_copies_size; uint32_t ext_copies_size; unsigned char *header = NULL; uint32_t header_size; MD5_CTX md5; unsigned char md5_digest[MD5_DIGEST_LENGTH] = {0}; unsigned char *strm_data = NULL; size_t strm_data_len; if (delta->type != DRPM_TYPE_STANDARD && delta->type != DRPM_TYPE_RPMONLY) return DRPM_ERR_PROG; version[0] = 'D'; version[1] = 'L'; version[2] = 'T'; version[3] = '0' + delta->version; version[4] = '\0'; src_nevr_len = strlen(delta->src_nevr) + 1; if ((error = compstrm_init(&stream, -1, delta->comp, (int)delta->comp_level)) != DRPM_ERR_OK || (error = compstrm_write(stream, 4, version)) != DRPM_ERR_OK || (error = compstrm_write_be32(stream, src_nevr_len)) != DRPM_ERR_OK || (error = compstrm_write(stream, src_nevr_len, delta->src_nevr)) != DRPM_ERR_OK || (error = compstrm_write_be32(stream, delta->sequence_len)) != DRPM_ERR_OK || (error = compstrm_write(stream, delta->sequence_len, delta->sequence)) != DRPM_ERR_OK || (error = compstrm_write(stream, MD5_DIGEST_LENGTH, delta->tgt_md5)) != DRPM_ERR_OK) goto cleanup; if (delta->version >= 2) { if (!deltarpm_encode_comp(&tgt_comp, delta->tgt_comp, delta->tgt_comp_level)) { error = DRPM_ERR_PROG; goto cleanup; } if ((error = compstrm_write_be32(stream, delta->tgt_size)) != DRPM_ERR_OK || (error = compstrm_write_be32(stream, tgt_comp)) != DRPM_ERR_OK || (error = compstrm_write_be32(stream, delta->tgt_comp_param_len)) != DRPM_ERR_OK || (error = compstrm_write(stream, delta->tgt_comp_param_len, delta->tgt_comp_param)) != DRPM_ERR_OK) goto cleanup; if (delta->version >= 3) { if ((error = compstrm_write_be32(stream, delta->tgt_header_len)) != DRPM_ERR_OK || (error = compstrm_write_be32(stream, delta->offadj_elems_count)) != DRPM_ERR_OK) goto cleanup; /* offadj_elems and later int_copies and ext_copies are all pairs of numbers, * so in order to get the actual size we mupliply their count by 2. * We start with only even elements to store just the first numbers from pairs together * and then come all the second numbers together.*/ uint32_t offadj_elems_size = delta->offadj_elems_count * 2; for (uint32_t i = 0; i < offadj_elems_size; i += 2) { if ((error = compstrm_write_be32(stream, delta->offadj_elems[i])) != DRPM_ERR_OK) goto cleanup; } for (uint32_t j = 1; j < offadj_elems_size; j += 2) { if ((error = compstrm_write_be32(stream, (int32_t)delta->offadj_elems[j] < 0 ? TWOS_COMPLEMENT(delta->offadj_elems[j]) | INT32_MIN : delta->offadj_elems[j])) != DRPM_ERR_OK) goto cleanup; } } } if ((error = compstrm_write_be32(stream, delta->tgt_leadsig_len)) != DRPM_ERR_OK || (error = compstrm_write(stream, delta->tgt_leadsig_len, delta->tgt_leadsig)) != DRPM_ERR_OK || (error = compstrm_write_be32(stream, delta->payload_fmt_off)) != DRPM_ERR_OK || (error = compstrm_write_be32(stream, delta->int_copies_count)) != DRPM_ERR_OK || (error = compstrm_write_be32(stream, delta->ext_copies_count)) != DRPM_ERR_OK) goto cleanup; int_copies_size = delta->int_copies_count * 2; ext_copies_size = delta->ext_copies_count * 2; for (uint32_t i = 0; i < int_copies_size; i += 2) { if ((error = compstrm_write_be32(stream, delta->int_copies[i])) != DRPM_ERR_OK) goto cleanup; } for (uint32_t j = 1; j < int_copies_size; j += 2) { if ((error = compstrm_write_be32(stream, delta->int_copies[j])) != DRPM_ERR_OK) goto cleanup; } for (uint32_t i = 0; i < ext_copies_size; i += 2) { if ((error = compstrm_write_be32(stream, (int32_t)delta->ext_copies[i] < 0 ? TWOS_COMPLEMENT(delta->ext_copies[i]) | INT32_MIN : delta->ext_copies[i])) != DRPM_ERR_OK) goto cleanup; } for (uint32_t j = 1; j < ext_copies_size; j += 2) { if ((error = compstrm_write_be32(stream, delta->ext_copies[j])) != DRPM_ERR_OK) goto cleanup; } if (delta->version >= 3) { if ((error = compstrm_write_be64(stream, delta->ext_data_len)) != DRPM_ERR_OK) goto cleanup; } else { if ((error = compstrm_write_be32(stream, (uint32_t)delta->ext_data_len)) != DRPM_ERR_OK) goto cleanup; } if (delta->type == DRPM_TYPE_STANDARD) { if ((error = compstrm_write_be32(stream, delta->add_data_len)) != DRPM_ERR_OK || (error = compstrm_write(stream, delta->add_data_len, delta->add_data)) != DRPM_ERR_OK) goto cleanup; } else { if ((error = compstrm_write_be32(stream, 0)) != DRPM_ERR_OK) goto cleanup; } if (delta->version >= 3) { if ((error = compstrm_write_be64(stream, delta->int_data_len)) != DRPM_ERR_OK) goto cleanup; } else { if ((error = compstrm_write_be32(stream, (uint32_t)delta->int_data_len)) != DRPM_ERR_OK) goto cleanup; } if (delta->int_data_as_ptrs) { for (uint32_t i = 0; i < delta->int_copies_count; i++) { if ((error = compstrm_write(stream, delta->int_copies[i * 2 + 1], delta->int_data.ptrs[i])) != DRPM_ERR_OK) goto cleanup; } } else { if ((error = compstrm_write(stream, delta->int_data_len, delta->int_data.bytes)) != DRPM_ERR_OK) goto cleanup; } if ((error = compstrm_finish(stream, &strm_data, &strm_data_len)) != DRPM_ERR_OK) goto cleanup; switch (delta->type) { case DRPM_TYPE_STANDARD: if ((error = rpm_fetch_header(delta->head.tgt_rpm, &header, &header_size)) != DRPM_ERR_OK) return error; if (MD5_Init(&md5) != 1 || MD5_Update(&md5, header, header_size) != 1 || MD5_Update(&md5, strm_data, strm_data_len) != 1 || MD5_Final(md5_digest, &md5) != 1) return DRPM_ERR_OTHER; if ((error = rpm_signature_empty(delta->head.tgt_rpm)) != DRPM_ERR_OK || (error = rpm_signature_set_size(delta->head.tgt_rpm, header_size + strm_data_len)) != DRPM_ERR_OK || (error = rpm_signature_set_md5(delta->head.tgt_rpm, md5_digest)) != DRPM_ERR_OK || (error = rpm_signature_reload(delta->head.tgt_rpm)) != DRPM_ERR_OK || (error = rpm_write(delta->head.tgt_rpm, delta->filename, false, NULL, false)) != DRPM_ERR_OK) return error; if ((filedesc = open(delta->filename, O_WRONLY | O_APPEND)) < 0) return DRPM_ERR_IO; break; case DRPM_TYPE_RPMONLY: if ((filedesc = creat(delta->filename, CREAT_MODE)) < 0) return DRPM_ERR_IO; if (write(filedesc, "drpm", 4) != 4 || write(filedesc, version, 4) != 4) { error = DRPM_ERR_IO; goto cleanup; } tgt_nevr_len = strlen(delta->head.tgt_nevr) + 1; if ((error = write_be32(filedesc, tgt_nevr_len)) != DRPM_ERR_OK) goto cleanup; if (write(filedesc, delta->head.tgt_nevr, tgt_nevr_len) != (ssize_t)tgt_nevr_len) { error = DRPM_ERR_IO; goto cleanup; } if ((error = write_be32(filedesc, delta->add_data_len)) != DRPM_ERR_OK) goto cleanup; if (write(filedesc, delta->add_data, delta->add_data_len) != (ssize_t)delta->add_data_len) { error = DRPM_ERR_IO; goto cleanup; } break; } if (write(filedesc, strm_data, strm_data_len) != (ssize_t)strm_data_len) error = DRPM_ERR_IO; cleanup: if (error == DRPM_ERR_OK) error = compstrm_destroy(&stream); else compstrm_destroy(&stream); free(header); free(strm_data); close(filedesc); return error; } int write_seqfile(struct deltarpm *delta, const char *filename) { FILE *file; char *sequence = NULL; int error = DRPM_ERR_OK; if ((file = fopen(filename, "w")) == NULL) return DRPM_ERR_IO; if ((sequence = malloc(delta->sequence_len * 2 + 1)) == NULL) { error = DRPM_ERR_MEMORY; goto cleanup; } dump_hex(sequence, delta->sequence, delta->sequence_len); fprintf(file, "%s-%s\n", delta->src_nevr, sequence); cleanup: free(sequence); fclose(file); return error; } /* Wrapper functions for compstrm. Used to prepend uncompressed header. */ int compstrm_wrapper_init(struct compstrm_wrapper **csw, size_t uncomp_len, int filedesc, unsigned short comp, int level) { int error; if (csw == NULL || filedesc < 0) return DRPM_ERR_PROG; if ((*csw = malloc(sizeof(struct compstrm_wrapper))) == NULL || ((*csw)->uncomp_data = malloc(uncomp_len)) == NULL) { free(*csw); *csw = NULL; return DRPM_ERR_MEMORY; } if ((error = compstrm_init(&(*csw)->strm, filedesc, comp, level)) != DRPM_ERR_OK) { free((*csw)->uncomp_data); free(*csw); *csw = NULL; return error; } (*csw)->filedesc = filedesc; (*csw)->uncomp_len = uncomp_len; (*csw)->uncomp_left = uncomp_len; return DRPM_ERR_OK; } int compstrm_wrapper_destroy(struct compstrm_wrapper **csw) { if (csw == NULL || *csw == NULL) return DRPM_ERR_PROG; compstrm_destroy(&(*csw)->strm); free((*csw)->uncomp_data); free(*csw); return DRPM_ERR_OK; } int compstrm_wrapper_write(struct compstrm_wrapper *csw, const unsigned char *buffer, size_t buffer_len) { size_t write_len; if (csw == NULL || csw->strm == NULL || csw->filedesc < 0) return DRPM_ERR_PROG; if (csw->uncomp_left > 0) { if (buffer_len == 0) return DRPM_ERR_OK; if (buffer == NULL) return DRPM_ERR_PROG; write_len = MIN(csw->uncomp_left, buffer_len); if (write(csw->filedesc, buffer, write_len) != (ssize_t)write_len) return DRPM_ERR_IO; memcpy(csw->uncomp_data + csw->uncomp_len - csw->uncomp_left, buffer, write_len); buffer += write_len; buffer_len -= write_len; csw->uncomp_left -= write_len; } return compstrm_write(csw->strm, buffer_len, buffer); } int compstrm_wrapper_finish(struct compstrm_wrapper *csw, unsigned char **data, size_t *data_len) { int error; unsigned char *data_tmp; if (csw == NULL) return DRPM_ERR_PROG; if ((error = compstrm_finish(csw->strm, data, data_len)) != DRPM_ERR_OK) return error; if ((data_tmp = realloc(*data, csw->uncomp_len + *data_len)) == NULL) { free(*data); return DRPM_ERR_MEMORY; } memmove(data_tmp + csw->uncomp_len, data_tmp, *data_len); memcpy(data_tmp, csw->uncomp_data, csw->uncomp_len); *data = data_tmp; *data_len += csw->uncomp_len; return DRPM_ERR_OK; } drpm-0.5.1/test/000077500000000000000000000000001421165313000134325ustar00rootroot00000000000000drpm-0.5.1/test/CMakeLists.txt000066400000000000000000000035721421165313000162010ustar00rootroot00000000000000set(DRPM_TEST_SOURCES drpm_api_tests.c) foreach(sourcefile ${DRPM_SOURCES}) list(APPEND DRPM_TEST_SOURCES "../src/${sourcefile}") endforeach() set(DRPM_TEST_FILES refdrpm-copy.sha256 refseqfile-copy.txt) set(DRPM_TEST_RPM_PACKAGE_NAMES drpm cmocka) foreach(name ${DRPM_TEST_RPM_PACKAGE_NAMES}) list(APPEND DRPM_TEST_FILES "${name}-old.rpm" "${name}-new.rpm") endforeach() set(DRPM_TEST_ARGS_CMP_FILES -d ${CMAKE_CURRENT_BINARY_DIR}) set(DRPM_TEST_ARGS_VALGRIND --error-exitcode=1 --read-var-info=yes --leak-check=full --show-leak-kinds=all --track-origins=yes --suppressions=${CMAKE_CURRENT_SOURCE_DIR}/lzma.supp) set(CMAKE_BUILD_TYPE RelWithDebInfo) file( COPY ${DRPM_TEST_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR} ) find_program(BASH_PROGRAM bash) find_program(MAKEDELTARPM_PROGRAM makedeltarpm) find_program(VALGRIND_PROGRAM valgrind) if (NOT MAKEDELTARPM_PROGRAM) list(APPEND DRPM_TEST_ARGS_CMP_FILES -s) endif() if (HAVE_LZLIB_DEVEL) list(APPEND DRPM_TEST_ARGS_CMP_FILES -l) endif() add_executable(drpm_api_tests ${DRPM_TEST_SOURCES}) set_source_files_properties(${DRPM_TEST_SOURCES} PROPERTIES COMPILE_FLAGS "-std=c99 -pedantic -Wall -Wextra -DHAVE_CONFIG_H -I${CMAKE_BINARY_DIR}" ) target_link_libraries(drpm_api_tests ${DRPM_LINK_LIBRARIES} ${CMOCKA_LIBRARIES}) add_test( NAME drpm_api_tests WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ./drpm_api_tests ) if (BASH_PROGRAM) add_test( NAME drpm_cmp_files WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/drpm_cmp_files.sh ${DRPM_TEST_ARGS_CMP_FILES} ) set_tests_properties(drpm_cmp_files PROPERTIES DEPENDS drpm_api_tests) endif() if (VALGRIND_PROGRAM) add_test( NAME drpm_memcheck WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND valgrind ${DRPM_TEST_ARGS_VALGRIND} ./drpm_api_tests ) endif() drpm-0.5.1/test/cmocka-new.rpm000066400000000000000000001033341421165313000162020ustar00rootroot00000000000000libcmocka-1.0.1-1.fc21>t  DH`ptUAN?T}( .eZd-B` z \+;GP7ZC0;9:=T&kU}H0ˋ+g<@@`?pQsT2L&K#=)Bi@'ɺ°6Q.M"8dzHǝJ:hu-bD U`9AP]S%">&T 5R.LxYA龡,3BژH'c\`Ok8q'S@y0)w,6P4`\ᡘ)@q 'bz("?]DP>)E >hɈ 2"Pty?%4t(<'!ӹ&O V.ҟAw 7Ij+ܦ4d;ѯYqQɼ1< }}_{̠"AJ7Y&nynp ~ N]VHOA~Yz“y8b979b0e4c1f5d7a4e29793d0a2841383bc1ee55qdUAN?TYj^wQ\mn1~g'M?4s"[3TV FzVQUnO fZE4p[vs5I`%Pd{P)4tBlƛZ<VI%i>ƉB h aypLU&-zq3rϙ3_\poCvٖ?Zj7 '{._"]}R}Ŧ-`6["':/Yom75% IK\1]|g v@ ƶ8v3&+Z?%M{=hrncNt)ъieꄢHb }0AhԹӪ#DoaU^cm|"ft; o0gB/Gn[Z%sT 8ssS~y2 / 287-8h[A_AyD 3bŢ >>$?d   Y+:B Qgy     8TwH    ( 8 9 (: >@GHIXY\0]L^bdefltuvwxy Clibcmocka1.0.11.fc21Lightweight library to simplify and generalize unit tests for CThere are a variety of C unit testing frameworks available however many of them are fairly complex and require the latest compiler technology. Some development requires the use of old compilers which makes it difficult to use some unit testing frameworks. In addition many unit testing frameworks assume the code being tested is an application or module that is targeted to the same platform that will ultimately execute the test. Because of this assumption many frameworks require the inclusion of standard C library headers in the code module being tested which may collide with the custom or incomplete implementation of the C library utilized by the code under test. CMocka only requires a test application is linked with the standard C library which minimizes conflicts with standard C library headers. Also, CMocka tries to avoid the use of some of the newer features of C compilers. This results in CMocka being a relatively small library that can be used to test a variety of exotic code. If a developer wishes to simply test an application with the latest compiler then other unit testing frameworks may be preferable. This is the successor of Google's Cmockery.Ubuildvm-27.phx2.fedoraproject.org֘Fedora ProjectFedora ProjectASL 2.0Fedora ProjectDevelopment/Librarieshttp://cmocka.orglinuxi686c,^ A큤UUUTGOU~Q8435030e4ff0fa1ac6b6de7c776a3bfd5ee98c9f27d13bc4d0103496ec16494f5f09b495c89b6e39877021aed3f687e380a914099e3e3be9069b0d93cfd1ff04cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30d1ffacc2e185b5c8101a4ec9ce02e0a01ece6c440e6f3610577723f6a9eb25fd588e139c1aea8b22b0d55bcbc3d75a826b272debfb23e6ee66752d1ace085140libcmocka.so.0.3.1rootrootrootrootrootrootrootrootrootrootrootrootrootrootcmocka-1.0.1-1.fc21.src.rpmlibcmockalibcmocka(x86-32)libcmocka.so.0@@@@@@@@@@@    @/sbin/ldconfig/sbin/ldconfigld-linux.so.2ld-linux.so.2(GLIBC_2.3)libc.so.6libc.so.6(GLIBC_2.0)libc.so.6(GLIBC_2.1)libc.so.6(GLIBC_2.1.3)libc.so.6(GLIBC_2.11)libc.so.6(GLIBC_2.3.4)libc.so.6(GLIBC_2.4)librt.so.1librt.so.1(GLIBC_2.2)rpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)rtld(GNU_HASH)3.0.4-14.6.0-14.0-15.2-1cmockery24.12.0.1UTSGSS,SG@Rz/@QQLQ(@Q@P9@PPDAndreas Schneider - 1.0.1-1Andreas Schneider - 1.0.0-1Fedora Release Engineering - 0.4.1-3Fedora Release Engineering - 0.4.1-2- Andreas Schneider - 0.4.1-1- Andreas Schneider - 0.4.0-1- Andreas Schneider - 0.3.2-1Fedora Release Engineering - 0.3.1-2- Andreas Schneider - 0.3.0-2- Andreas Schneider - 0.3.0-1Fedora Release Engineering - 0.2.0-4- Andreas Schneider - 0.2.0-3- Andreas Schneider - 0.2.0-2- Andreas Schneider - 0.2.0-1- Update to version 1.0.1: * Added a macro for assert_ptr_equal(). * Fixed test_realloc() if 0 size is passed. * Fixed objects packaging bug. * Fixed building with newer gcc versions.- Update to version 1.0.0: * Added new test runner with group fixtures. The old runner is deprecated * Added an extensible message output formatter * Added jUnit XML message output * Added subunit message output * Added Test Anything Protocol message output * Added skip() command * Added test_realloc() * Added a cmockery compat header * Fixed a lot of bugs on Windows- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild- Update to version 0.4.1.- Update to version 0.4.0.- Update to version 0.3.2. - Include API documentation.- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild- Update to version 0.3.1. - Fixed cmocka issues on big endian. - resolves: #975044- Update to version 0.3.0.- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild- Fixed typo in Source URL.- Fixed Source URL. - Fixed package groups.- Initial version 0.2.0/sbin/ldconfig/sbin/ldconfig1.0.1-1.fc211.0.1-1.fc21libcmocka.so.0libcmocka.so.0.3.1libcmockaAUTHORSCOPYINGChangeLogREADME/usr/lib//usr/share/doc//usr/share/doc/libcmocka/-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tablescpioxz2i686-redhat-linux-gnuELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=774b5099f5beed922a1c742f804e3cbe7b463f8b, strippeddirectoryASCII text PR RRR RR RRR RRR? 7zXZ !#, W] b2u B0RœĈSV6#^m7TnF69;J)V̚$+. $B<^$wuIy_bZ5X.lld[9Bo<ܔA1&jb;з|#LA5S%X*U2u&͏'`j_xY5 Tp*w(X**΢iD2b}9%B]llex"tFB6'xaӾDvćJfO3 ij(6(c^d]nMz^ *Ho\ڏpA!/zn8Yʤ胂t׸*ɳotS(,[sf>P&aXV?IjRA/Ľ̻iCҌ?X+]x ݱ%bnKIzyf e_O]#^-Qi-*ASܜdWЗ?5Ct7z-y)mH+Vic,-ξj}~(wKUL4,wsxj΁B v쭘͊SYTԼ'Pלᵠ_A95s[s ^6P$80Y GgAtD!Jc9j&SD Ij#@x=$QuK*Gx)zC3Ra 2@[mV0m s6Rt" t_Ѽ\puo/$AΌJ%ωOر4ݰŠA4k|P$Tm|D3YaH,n7In&dPg/<.$2LNceM17Q|=3YbE Nzۭ8~Y~rg0tD!:B/I?)$D&MmgpKS`b|bI!9"3["ضw¹pe ;#kg!a6Q}F&E!"HM=ÿ3Yk42~Q5}ZЖ 蛳^3"`[S"qHL{ZXB+ciU]xcWvɘ ᣪG f]֖~,6SØF%4==iI:/W@hiCA4׺[+Gy"%$Ű8I#<N;giI0 ݄}sdCf)Z]oG3|۵q 4!}Pnpo!Kq!f۸?QvGז!XLTt!@J2Q=>_JýaFR8{l/d<71$Fk>}KVRT)3{Jj[h:.5@bR7ͷq;|.7߁Noo8 jŃ'!N̝%{N*?X<ï Ǽԛc{f}\({%k>쳆Dn/@a]$q):{~s{a]*g@(YNOOĴ&%YL <ƭi?~z~-he4V*q1i,l}s}="G[WU/7-zzI< Nsv?;xJ~s3^Kw{g ϞC3_U&Ts.j~l($΍7|j+/z=aO?}Cj.,A>_-XDuIrJfNkTKT r! RQ9+Հu\ EյPdjeC,zX[IϻXSl0'OpC ^(+?>ԁ*]oYg_ ‰[Ƭc}~oZVVw;F6S&wr@E!X_|'pzSfO<;\I1Eiw_B),Iy?GfaW}e5:̓I}a+cPA NJˋЀ7}6C@X-QI7)Kt uRyګv+ZTWފh5%D*7aJ"pZwG]OV]dSk,~tn:,8":tbƌzǥOlwd9OJs"g8Bbn!Zkn'!+Ez(aOd`焷»$K5xe=;$_Ng oqA ^^zV:}ԥTflls]lKR_{obJV;}/fZ~< gJyv=5Q ,RI.>aK\byE_^D{q"Ohik@NIa,9f6\"`_zY鸌*E]MH3$nQXbX!Ys)ۅ*.}NyY]{ެOhig\*:f!*!aLS7uR PZ؏:zroS?ƭ:A$1~[̵n{&^^}^@И+Bֻ4;Cԗ +U0ͬavTXhlr[Kd"xUg=A o !KrI*8דV4>)&{ar|y7j1WuδDMjWC[l qcz|03DeMPS>_qYu,:X[Z'lܒ(kqzɗjvSZ P3 HOljU3y&~FYP!]{Jr PN"*@-@UKf=!?1NCa=6HMd̏4$xݶA'bm X;8dJAp/Pm꿸Pų5Q6CFs >+΂ޟ]%$xԙ gVR9X00z˛8*G2| Θb}'h\U]6m*~w,Aapкe3^b&}"/D$gݐb&ƾb#陱?z-ZE-* irxrYp~tuoۚo1|V+Nh2.w.~HF<g^y/`8T^1)Y|Ԩ ~6M;G'm OBNx= _z)3S-<+2~A[L%v^bܪΟ⨝(oo^Ttz1\Iq=u<-v"|= S֢l2LL6P7:AWlQz  'v <-Q佽 .pmr9/sXsXae5^Ug.Wm`ņaGh#ԉL-p,kuu[Ȯ; )0C֨*{=pYzL% ˆ˶q"N\YjI.U=ZJ@Z6ul al UlVͪi}  ^Vr5#kͫ;C%M\ZBJ3 5UJ*r4't_h\ח/A`v٨u_U` eW (!^"V3R 2Wfz*+`to6:aX#D62NĒ7M7}ArurqWx,9#X/Q4jξ7z*=XO"CPfwZ~@zpZy[oc{|1j(txQ(H{6 #Y2 1m5 }nxVxG%OuGp_g 䛑,^z/$&Μi9@gR!DwW\k\PƼeÖ_<Ȼi~U"QKA4!=[&͇9̘CTLt(qX,ҚNԧK̥; -"4nKmKQ0Ɔpt sQExuc" ;,%]2\ Nv#M">nD;"<NbeD/A1_oF70Ni|,o֜,#Ny(TEV(; 6"xlsVJSCm6:ů |K2q"%7zݳE2/=#iJ4A+ ?K*L>ԻRhYu1vptZnkA^0gvCܳIW?%-y`BP"YYߢ>5 sj E>SG>s@NGe‡qd$'%P\zj+;֣w\^+s Cت鬅5BFݦZ6ؗ4", hX<;#pyGѲ2 L4ت6?>%j) }ԑEǠ.H"Ti8'`ԐCaќ}C7p*.iH aQX 1DJ6Tx{@2}#tRʶIaepPbe:"([ -kP)iRx6>NVɘfaR v% Xr84 ^煩P1'}`n@F̻>gbW58TjՃny90,kTp㈹ETfKe'laa{/Ϣz\qH{ir9M:bHvZtJ,ΆџNbK~k e}ntC Vmugbِ S?1()뻂PoPP R\ M/Zw"@ ] VT K\Npvތxd2ŋM \=$jhJ5Ruܸp.fy\g#[tH8 ?)c_ bI7aWm?28A:![fWwX1݅:u|GrzyE4HRKKv}o)x *5,N@/ѻGI,K:ȉ{wM @K{Uԟ G:n'?{r ހX& ڷ)y6e06#Egpg (HA,EymY;eςN(gܛؑ)JM܆P<y=e&n=HfxRa*"":t\`=qPE1*FN!>#Ogi۾T>lF G 1j }u?@͋CX2_2.;72'acUBA Y4 &U=}DLƫ̱a[WngkXI7JR#hC fI|TTz9 .OCc3+ޮN7^ A~ތs&:B#q6A12׬g>t5ƨNId&zO*cdUO49i =dr6`1һ6.S(p__/ c:PYEᥗ 4 rR CO`p\pQ{Z[Sb~x rKl˯%Bix/Ke[p5q9|_$+?`뫿ew2jIS&n{|y+k&ڪgHѾ;%{s֯4Am兣#y n|MІs`i3_N|VQw០f9emveCDr"v='ӷ<"mk|ñfKDUU n~ EvNE^bt#"¸<K1fGE`J!!m N?+Ҩdj!k\@+RI9%=0mg:Sۯ_"ɭ$D`ZQ-63\Hj*,+EdLOOdBD>>P! eK_H/cu!+83xab1o%G-ns,<59p<{BW9RX5{PZo_˰Yj:PO[Bq"#4P r[tDר8T T`pdNu)Vtl̛PKMw*G"jdn%/;uݚ]JfikENPz{chL.r g- 6ֿcRFޘ7GQil+Sh@Pr:d&"ĂlNܶ2N(O2t[,?5FBwPiv}{Pb+F{FaQBF"gz ۩bN*kí=/Йen7dR/̟=g"lAL(J6t4M3Š\!1[z0'jRCyaIO_ 5P'W*N6|282_k%=̒^,&A*"#:^'lͧ+Y%nA\y]uk,uF |{Ho3Z,RO0f3_Sj<2o69Hqfˋg[6K2pӓXGe(?և[jhw0Avh:$ mwX6e/ѪV~Ί]x aw;{tc]#j}X`dî:WEBF"*ZlP2- n]i=;k;Ms) i̢*l{Wc0c[B*U y 6%c r^jr2JS'6dES}Ok04GYm]~/[*;]?A};rRESomiKlaN>p_8e7OCy+L?㴈hfoa^Z [_:m&Dz(6?*AoDJŲqD*fI}EanŇ/+}Gvt7v]W3H++E߫,=eSK">15ΝJ9Xd pQbD\cjY1 h-$Z K=Vg&4hpLIva>X7q5 JGUY& TʄwkCfʶ0RzۺeH^}vV KVF &&2q|Dp4[-`*wN&Lr:'[Gah︜맡c同lʲҿ,t(ׅisE#=H6drZ<3{ܔlꍴ4/iYyЙT7H+_W٬/( H̱Б wP8켕0FE1g0#4/oϦ[M'SJ˷~CYlRbgZ/}44ʖ_af;\Q,ȵ&@:SŮ!G!oHoC,I&,=g64c;nC6wQ5v M!]vedAgHbSzŨ'Sޏ). J_oK`7$KkmueuwgcN?jVUf ԦξC6Dm2L‹@e֞pX#k nkDce(yr&~bfWծ(y=EF[=$BRF&X҃;uckCxeo<6Nf.Fu*bM = F\P'[U9)2E5ؘ*)T%YvnYԼ:a|٘`|"WG5wPs#j:Gj0a93sfa{=սnOEb|mΛp%ݡeɬ(εɳHU[=!AIK2h2쩂ǐ(vM\n<NV V25g#ۙR]K*x>;,;F&4 )+oa5m/.6(RN`n '"\EN!e*5n`0p9mauÇm=^`!>Z&) 1?#/K,/K&3ɎYyBe㝠@t\!3Vj%%hml=uxÕ֨6iﰹ^h俁/_b=TwMσaߥԱ!sàz60,!c&ZcAQِEys0@gǴZ4gE|xl}S@vJ-湖b,=؆.Z Lj28^4g7 핇Pш `x>SA_YhR)ۤG`k &oĽBsjMLۚU0) 1]l}%fO%41en=wsԇOgo L뚒] Sě46azvL,mmQM}t4tKEud" :Q/HK;1Qx}PkCрشeGTmbk:i.'H7yqka#|" 1mD[#hjd{ު0vo tY+%ЄJbB`Ow:/|&l6.92ylaTdй13=n<.,XPrDU_DSK7 B#d88-qchmPߜD2$1[Oh׊_g0D֚ΝXf+JR`ɧSMYȞj2瓶jYpd)q!PnK?ĕ =ln-bO?/t:{a +jV}g@{\>1.%,*a]~U?Ӝi/JWRcjlC׶櫴✞.JffLK6Eͱi?HiI8c0ÓΛ(1[+p'{S7rqZw nvUrO_EDrkAf5!qW~[5P\l_+SgYA@9 J}Sd!Zh,K&Na;H@AO`'l'dݘ&RRa+8^Tt뇹.31@eg7iH2'6 acq\)6?_hE=EhB+,-ʟ-Mb<9(ؙ1)R<NH"Y]XٌH^x%YŦU2$V2%Af= LUN|Fg 3;yUdaq=F0pT+BW3#(ԺU.p1q$ 5̊i'A)K t:s9YA`M=Qc40ԡj1+ㅈ?:pg@ E+6iv\-م4 )ex]Ow_<%T Xs쟨N\9rxy 8/ʦ"(yAi<+4;֊X|M,02eǸ)u]rMBQ{j°e@ElT Ƅ",R..-U3J#q6󈧹?ѻ @'umPXy,Ḿ (Xltdqg,wÃ|d ,448^鏒A dvbϧ 6Svv쏕@!rAsL[K iב@_T贜&.O 5_,oqgeRp6g5 z3cߎ^1G}%659(_Pb߃ƈxpS=RP$C  jׂ֛ hyUkp&=(Bam-CT_ :oc7h[^;!0VςT:r$ir^ܥc/dΏtQBׅaT e0YCSiI9><J!0w'߀IW%/XjS4oԩ1OUvvf!TԱoIVS1lgٲ<1FA9!(@S/ΆVTLط9lXEaofHKC ~B~pBg#p:B 2hq7GogtZ50@(] ux[vQE+i TNM?i8"PlZ@%n¯(OW=ckQrMrs3˄-%Ļ"Vx,UWf*~BBl fYQ UQQv,4Fn*~O2CM'EeS,4KeiLu/I"\v}w~eEvR\nY!>j{KAbיlpK9GٮgR#4۝ianK^Hxrt@k fT/ -ޢ>ceǺmm"*!mME W9M FLحʔ8> f+ T\*}Ya-Lݨ$Z--$}՗ 4,m,pԌ<>U$fgHvJ"Ln,5Sc.=3>И#`*iwwK'm<3 ,ˆ?> aaP2|;Q88Le|s_,gD& ?%6abTS-$H>LHG`m=-.pJ ZU(X%CA+zF_y)[?4~Y TpUdڶ O *u_1!Hp!t0QQ{車K'[_)jՑu4TSQiO[q 2s*D߷oMP<傥wIԿRv&7e+ T USPv&<0bk7-doI~\ E _.X1hLcnʷQM;9R?IhH2(,oX&R_JςԌ5+l׃u{~_;Aqڭxd%d+G ~ґL]ti tM}4PP`}r{c\n2 [}R= p d@!*,p]> Pu Yi6 a:tbЙ8o) ˛$b~ΆpH91,i 4:7u;odo @ePfY KҼ\Nj5B2?~De%J p3iLvs,dp߭ehYL9`L$b{︢?/!jl4``n6tĢxžk:,it^)жzǛ\"MxʼZz "=4#u܋KvPUFؖMj4§T%e,0 Yo! D4$*`cmiATNssޔb\$SY֭ZͦR?Bd-0w15[ퟅXg: b+.^T4B}NЅYg,UuTT*r)zqÑnx_`8g~< Ev)n ?CAcHzjς@*BZkSƘƧĔ9a|hYҨD%{^(|BCA({KZ&%T}=Okڞsx =sY^׵W]}ja#NͩuXv)B-%%hҥS/~qIsQGʞ v=5Ҕ5=,N#${'3ڄGs[ʼ!7VqF DjO,R( JN6 *Hx]v d9ex cM=҂[2!9sz $yOڳxyoI@-ng{-.$1="I`U@|ſxHNgWȠ_F(XhS'N}B$07w +tj#^fK̮ oN|L֦\w\o&="0g21A2r6RN < Щ8ơvi`6}{NϋYu/=ڏW΄D\ф9y2~"X?JySP{rmA:){~*iďIMH]H(ݖ3`k0|w;$:U,'g"bdƞa* wn( J8HVvjJ9"Z?cJz#-$s|`Iv'`n59t,8ŀWY~+7 `s R:3i8<)r~Mb: g|ⶵɒ_Hd_7"v^5d&}xFy70W)?ιV+ME,AJyqj o3owQSY.t AW})>t@Lw`R◻Cnwψȕ k|(?E%av(9:16Y+dp1X$iXgT3(8~Z@5+8i uar4<ij,WiH3c*$YyJ6R&.Wϕ> 45v]໯[z'½Gq]" kyq 3[˨fݹM\7#?\)Lw?3a=גsB7|/etO[JjHyV}LX5t`'BUz iLa`+fUXj1/b`2&^%x4M*F8׀Fjm b֕eM ?ׇ&l4EU\݄B {"E]b6>X.#W*7g*W[CʣmݳX&0Ţ36⟾p:p5/dpE[ҝ|>1}$foq^g^ z^*s4c"29+l _)f,qQ, L۶ YZdrpm-0.5.1/test/cmocka-old.rpm000066400000000000000000000705641421165313000161770ustar00rootroot00000000000000libcmocka-0.4.1-3.fc21>t  DH`ptS ωN?TںG jbg[\td^ajcۜAAؘ$rjV? {5̪u ڦé&Jʏ#"p;dwnv#"' ɱ HhyKPךBkF; J2R d՞tX Vÿd^B[0bH88236052ee7feb39b4820bb378b1fdcff43c46fb[S ωN?Ty6gZOɥm1eqn#/4UW,4)πKB0?NrPtz־$R+ $;?Cd Gbp4Yv6ʪES/ D(lQkёK"KHɊgf o~@G|Y[itXBW0YϿ'X5%jŸ:եD\8d2Sች0tYXspb%~P}eB8#b{F/K6NM"R k$SfDL& K޷j#6nTvl)xpYH ۫A%),;eƁN%v%J!tZvNaإUBiPfUkAT$e_?&B Y6ͷ\/> j QlP󠆡=>f9o)7c([J5]WiIvX`?|GG%+S+F YyIΆպȘRG2%@>;?d   Y+:B Qgy     8Tw 4  < ( a8 l 9 : 0 >@GHIXY\]$^qbdye~fltuvw|xyClibcmocka0.4.13.fc21Lightweight library to simplify and generalize unit tests for CThere are a variety of C unit testing frameworks available however many of them are fairly complex and require the latest compiler technology. Some development requires the use of old compilers which makes it difficult to use some unit testing frameworks. In addition many unit testing frameworks assume the code being tested is an application or module that is targeted to the same platform that will ultimately execute the test. Because of this assumption many frameworks require the inclusion of standard C library headers in the code module being tested which may collide with the custom or incomplete implementation of the C library utilized by the code under test. CMocka only requires a test application is linked with the standard C library which minimizes conflicts with standard C library headers. Also, CMocka tries to avoid the use of some of the newer features of C compilers. This results in CMocka being a relatively small library that can be used to test a variety of exotic code. If a developer wishes to simply test an application with the latest compiler then other unit testing frameworks may be preferable. This is the successor of Google's Cmockery.Sbuildhw-02.phx2.fedoraproject.orgHFedora ProjectFedora ProjectASL 2.0Fedora ProjectDevelopment/Librarieshttp://cmocka.orglinuxi686z_,^A큤SSSQ OS}%Q75164e9050812b5ebbf246e39e2890efa43267b1dd87917f71e52f33c15def076ac6a8d9f63462a10f1a955de57c36f1f9af89d5f44685c2d1d7145530e26ddecfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d301b94d02e97b07ac3ed086ae47aeaa16f20b5d1adbea32ed916e58eaca3fcce5d588e139c1aea8b22b0d55bcbc3d75a826b272debfb23e6ee66752d1ace085140libcmocka.so.0.2.1rootrootrootrootrootrootrootrootrootrootrootrootrootrootcmocka-0.4.1-3.fc21.src.rpmlibcmockalibcmocka(x86-32)libcmocka.so.0@@@@@@   @ /sbin/ldconfig/sbin/ldconfiglibc.so.6libc.so.6(GLIBC_2.0)libc.so.6(GLIBC_2.1.3)libc.so.6(GLIBC_2.11)libc.so.6(GLIBC_2.3.4)libc.so.6(GLIBC_2.4)rpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rtld(GNU_HASH)rpmlib(PayloadIsXz)3.0.4-14.6.0-14.0-15.2-14.11.90SGSS,SG@Rz/@QQLQ(@Q@P9@PPDFedora Release Engineering - 0.4.1-3Fedora Release Engineering - 0.4.1-2- Andreas Schneider - 0.4.1-1- Andreas Schneider - 0.4.0-1- Andreas Schneider - 0.3.2-1Fedora Release Engineering - 0.3.1-2- Andreas Schneider - 0.3.0-2- Andreas Schneider - 0.3.0-1Fedora Release Engineering - 0.2.0-4- Andreas Schneider - 0.2.0-3- Andreas Schneider - 0.2.0-2- Andreas Schneider - 0.2.0-1- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild- Update to version 0.4.1.- Update to version 0.4.0.- Update to version 0.3.2. - Include API documentation.- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild- Update to version 0.3.1. - Fixed cmocka issues on big endian. - resolves: #975044- Update to version 0.3.0.- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild- Fixed typo in Source URL.- Fixed Source URL. - Fixed package groups.- Initial version 0.2.0/sbin/ldconfig/sbin/ldconfig0.4.1-3.fc210.4.1-3.fc21libcmocka.so.0libcmocka.so.0.2.1libcmockaAUTHORSCOPYINGChangeLogREADME/usr/lib//usr/share/doc//usr/share/doc/libcmocka/-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tablescpioxz2i686-redhat-linux-gnuELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=4431f7b8de4e93cc0a801bb4777619d323f8b0f5, strippeddirectoryASCII textPRRRRRRR ?P7zXZ !#,රE] b2u B0RœèU+Xxz|FS{{HCvI¨,xS c8Lx~@s@rV-8:`cXInyS &bf*V@|ARcz1ko߅Tt8ľ3p*q!226CL+uxY7>=΃Pͨ;w5ق[ bvG`'M@ 62^Wh GABx<42yʾKC{Q' 0s;Tz< {1 ,߯2lS9ٿ-%,K_&̠18@)7K-Q2g)E{+z|tJQ;ܜV(Ϝ/BC='^fCcq8Փ${3uLJKe9zdA#3At樈d~e'zǾ"-"|-PrMX>zEnU%Ak$u1ʺ nn'llNςP#grFK jz]/P`qAkB&!A ⑘=l0%`ȿJoα= kkaDmT׉(x\ãBީ1|N|$ɴX̱HN#rn.ѮP~ f#>tHe6"֎L5Whkjc©*49^ -[{¡'ׁ,^Rj#{f=^1qgF//ݜ6hXfDF"(Qt |]v5>f|OAur@`Q]&Tpݫ&+*^O*RBV9=kgK]D~ ~`J~N xo:X4OD~?_g9s?5[TKgOM ROmq xePid{pyEr}*8eɓj`I"jHW6l෹)"(Uoa)3%Lт0 kxS~SJ3|Z2eU6q[0!TD%߽!ShYA߫t>4x+bHMKK٨PNCS?bݛ&ȏzgctXa3 })ᕠVXqd0ųa1=&Dނ#;-'9Ǥw8P2-??#*r낼p;N9T.S 8 ց[Pp#X_@nki"c bʫv>? iU~;MK_84DWɱ=F&,}B1+ڣ'7בCؠU\MkK3|,1xvnW2+ j3/2az_lN eSW9FD)Z :pzl:Ær'C%6[bKB2}5B[is@Ƿ?U Uh(,LG;r4|W}>=/o; ;#/8T~j2'=/%Omk nTƒ;VX~}8ieL^`{cPr(:zI0 7#i 9[>~dTh0摀 RDe}kT.F }< ~I'0ܔa֎fa />Zt#xm)?ސPN:@ջkQF>S-,G u:T*ybcW)@B6TЙ9u#zCn^eh|ޛl3`'ܢfI.S𩪸50غ Y#Pke%޽ÕIѠQH}/qE]ؼ/iuKXYw \Ni]ҶU>FPLawTp Ĥ>J æ_fN)V`V`T>5bz,XY4`8j6UgP2>Y%SʏK*nprn`" vޫw͆"rB?%uAE9˪KPz#_Mb-L1i!FA]!*ZHyҫ M/D_5 V鯬5uW|bؙ=ofe0 Ry$I@TrUi:P 99Վ.SI$oT/'")D=ΐU8eςA:"C( p{>G3nq;f@9.|;.)P)HlqVd;/3Sޢ~#ŀИKTK}'^E$}`jN7ȪwS,.(C 7bB)a0K`WvRaǢc=8ULL:pU~G q' zJzxjPZS%P3UTA~o\&?/YQsڰɣBwGiM-ۺ2S6/ %(B&i({l.X}=&G?K:KL7(NRB0W騦՝ Ƨ=%^*֎^ڞ6@ObPmEA O2+yL*SzN=mV} "y.8ዎ zw7wls&[u(, *jQ&F2x,&0{P:?&f%i2#<8?(LʎZpXQzrȟ?MZ~ޑxd=ee6og*>g֕D,eD<|56|nQpL*0C#{ӄlz1o maw\r\~塅5vLּcy"}̒ ½5#sʅ9)jNg!!_z]na֣úrq19M5AK0rwukzz?F*1EiqF^hn;MBvNKl`P0a|(T痔Ӈ! V,k3l>&x2HH,=^$Rc-37sI~v2 $¦H05bH)`.֙F&fRQ6o6mMi)]z%qx&k2.C-t@F0pGdd{°T#oLDzޟRUx<qf =[7`*—mqނk2r%`U0 vϪC?qd T䫷\9--``qW6")Y7zW@|p9";IF EIFpG<(KoAW _~g؍ s*n5Qb\a_%d*FBU)D$>$TӣuQO1,?4 Ԯ9 3Px1$*T+c*aj2ԠQy TbdB*Y7cc@jIC6ʲϚ^~oR3C'7y\as;Ly:FST׋Np"פQ$/J_i­i0XT&$b‰/+CV@(uXˏvgUĀ$$鏪w!e DW?.PU!)F|[y$ݙ&y?-+(~H"-)` E8]{qA߯O>lW_[fM&q U" 4W^C=1f % vӉޣ totPpM>uUW聽 Yŏ`CFѺ$Y~Ti('Wjlb}h_t2`HƍzZ!U?3X4 w X+vcqC07E= k5:*X+htܖz ) wT˝AT RN.=HstzX`h?m,O@|;GDDUDڼx` ]5JT1" XB<Y-.⺪K^ Z)cT09>Ѯ.]>2ŝf ںONP<7.K4OnOA'b{O$w}A@f4ByMkm6?G = jݢxiH9O)s\ IsYMh[j E? sǏ>WVf0EHiF`U0zzR, ۞/@,ĊkmW_!lUm:yvTAlXlEY9>z\~p++gЀwwxf/$ t zdXV哔(Z%Mnrhzh) 斟xH `"/6Vj6~PrR@QXj8U.p'$;?"JMjԕ|KwBU0u4iC ?Mn UWԊIWk'+f0#=`R@+8䈉:{`jK!bf_ӽjnBA=C'uFPn#I)-k7ux'~Us;n3T,=9# a[%`e)8 u"1r#$HB'] T*)sL mKO>:jrD UI[E <)  hƜ ^)>wG;'< IϡL2{Yl ŧKܱ^E|ϸ68JڔŻԓ9 ,(`[>-lC?ɤ X<}]6*@h: ORC M] Ց*|@uw#+SDBݣ}e뮽?o(8 S|~!ajgvV> Y*g ND=3+ 'xU5ULk2FR!j^Ⱦ)C_}#=UvkKAl2b87S:#@_6(\BD~ x >j| sAۿۅ֌1 vjߚ 30 LhS0ߢZ֮20J~bY$=dۏhxsp&z`eu)(opgD_ οW7D2U1J./םO:ےFZ>(!6k1ag] 'r}'mH| 7DDI*sd2"7,k1J03H 2=c)) -ܯ= iyUgGhGU>t6%6GE%Uŵ aAMS#{ cXH\"{ ҟ !s?ϕ=PET%StC]i$( olvP"A{{GNPaX* ];]L\͐M_A /|F{sA= o 9;k76G/\E(_f]д7hSz v&rV9x9IRV=Zx2ಢw'@&՛Hd$N^tĽoHdr̯zɚ}Wsfh@nR^\&HG!NԢXrt5'JevRout#5dGY͝/d]قɈ FhVxTL>F좤TDKςfޙBR.oK;ee)RX9VnAߟtsFDA3MtL'+!qc{pg^8:OɦiIkɇ;~o#nVȫi/wwA%mvw aUej?w6+ ՕqE1K{'~>`}9ָ/H ӺCs)FR'#o'64y)\M0;3 d֦l>'&-#:(k+P6:n\ V=# &@0T~uu5̐A03Kn`7헏o!OpQ*i}Wu!P5;^բnq a)~|%)!Yw$>uJr=[ց{)嚳xޱX:,8]W1 Z9H@ l KBkq @\"U]A{@JNb)"Lts"@U÷NOS  ^2LˌOIr[Ph@14al>AK[4`2jx.}f}=M*4CȈC5Ho>Ca$ou5 5`0 T\'S1}ۅ7&N]بT?am+)]Badx6w% aAW :UCAw 8ɥZRI.\`~5&\+zPMrb?`Hm"OZ|LF?׹?o*tbZ@uVeB bNd2MOQ%D i˵,pyG1^_X]*++m;ɡdb^r 28l>(@*rMa n[vѤ&WZqCO o7"߈PC|\/}*]Gw9Z%Q7g5b=OCcش.)t?U=$}NYٜvT 3eUصֳ`.Ύg dr&_5E}vh?\>98w(I|.7?P7bJ" HΊ?w˯i[8qBa"љ0ɦ(e_ ʞ|@!>>y8s=;b'fϫW쇨r؀gMI\x/~1x~$Ǻh*pD`mxW!Nb5ˈuܛ {Jy Z|eaf_j)@FDN<*cPs<o>f")R\J{|xyW&+뙜P r&RdRT%3$3bPSA(=8vلI ,WÀhO4鵍ƌZɕEe̓<2FNΐ@UkiUN˒N*cG{c )9bK8ZV7Ľ"k-_ГM{BJ3w 9ie #\VMEN MlnXfК!'a䒥/s˾) hytu7sam z(6W :AuRy,0jCLK'/r%z[t ,7# k2NØD٩5ʹeq pNUNg$T]0H)Z~{Q=&uwvωzk%$.΅RQoM"?A1JK!$z3f4Au3+`6O!NӐ92], c ŁW\J!L1JsCe$0M)`jGDu̹Jsk I>G[ՋU0rR5gXVZϛ2gWGN \ J锓axɵ)g_D`*aET/ Vg쵨~K|9;תμa =N(4f#+"3LgȽ# kmU$Rg;gP#j<=F] { |/9<.эz^D ͚aq'!Hp]6aln涫-si\m ;X 3&G,frˇ=_ڥBI'M JGwoM= b~mR?4FQA/s >dO;GПͨkĪ|C:P^vԈ6gldcqBH#0ڟo߈ KX/[ˢy{. d5Ɵ ?ehxK8#JBSy^+w^=OL_V ZcD?;`|6ҚO >ĝYCƓ/CK뙿u042|Yf:HnOLK1R2_ S:rk7Y݋/Y hA<Ϡk{/ oH>~2[HJhM&˂,[p2 d+i <@G{/dP;;FSKIJV =! nOXJȯS܋f+H屼Nd4S^Ս_1SqIpsÐZ?8 wyo}U hjȺϾ(gxaI ;m;c,`I9˸$_}0m2/p>odט/4_~Lxy0XYzzkvLCFl ;kw&D픕!5j3޷K)9@x;So~Ϸő;U @W^vF.DdX1hj9O p  o-:ɂ0Մ;~QĬ*gMO)v:2#svbjON XdxA NHu<6'k.[AI%w3ɿƓv>8wV zI@Ki4JuP|yk‹|id ˥5mG<{#J8=?8k|lV[q7!vܯnm`[0exFy ?_^ZBqJkVtrJcQ ]UgfNc-` bF 2PֽZoUG=QYm+؂jsXG ll)מ,/L 2 ~@r9'=]{Ȝ,_waH<;~ 65YJ 8d[΍4I\zZB }p6J~N/bu*̋I'O?PZcgLy^Mם>mzQ @ri{Y[YAK?=DMT1 @~R{czu AZ;jwEl=M:/ֵ|F %6:X$= i)52;Gm(2R_ `!4+W4;lM1yWvi7$NX(M836,5/^Zv7W՟kG!h1ߏm0s'7CW)ڈ8-EJjV__\M^Əy%- wi]#3yUMˈ6 qŵ}^g)5$;?/tעTB.99K-*meįMk=L#p$?f*2|4r  ܶ;(,;[[x?YbR'-&qFERX?78${5 yyqx&ZL0SE3 Փ)jk9ƚoH]Nk2=1nz43AxD6kA(PH%p庅TA=(7).~و}tΆ?v3 $OLٲ,nr<@wCR.F(hBpO~ix@}QRȸLf@`I%:L0v 5 .c/A&˂̲IWBhGb01e[{rp& CP^{4P:CsDnߝjfW'a'5]IUOn^-vߖH 5׋`^ZKVN0qb2ʾVo=R]zˊKLfvs~>xL?Uz/GS ^0J&[F!~@UfuL"9KOݑI,R'FW#ġUw;3"[5^D.DPE\ )CW|漹qQ7%$u1SG :M \gZaI+Ba- gֹZ0X`5,~$c[k8n ?t_̊YkEuvZDW='Ca_iV&[i@)HIh0Mn?L?-^3bxIEBL^ -2 <ꏄA+3b 7/e̼T:ceݬ0)/'Aq}YD8X0= ̆Ah\_̴6@#1ʚG.3V+d^.gjYM;T{, %7YQwӷsqEKRSuVh 7I"YҸZ#*`Yn {c>< A?mY3L7yc!}:|t? IZ:j̿?9>*ConAVmcT؎n)h\7>bPJxgeMQu|7 F1]ͨpvkE#v5:Q] PzOL?r!qZG[z+n傆'kL\ Nڣmu$ٰHO9!Q8T<ג룔Cy~2,|xtke̲Ncх@#< i$8ύpΖ̩2NX9ڪnsxS_ LAFOt\"Iyq2~nQW'׉OF+$x-5R9 U˂_YYul) |OMTYMB#@ey3K=5C^:Y=P1_0ƥ ybL(2<;Xڟ63#mz\o8imNqrT<&<xO ſIo qqZkd.)hl;'P)vOL9 -m X%,c+ H{3xdۍos FND_S Eߝ'vM/+§p586wl̋*_hvWLoԹgc1x{]郧d-:׃)m5zQK <!Wa 2mWN'P@Myg~&GGپ˷cn]ڒ8,Ky'У8.=Z 4ִ@Jӝ|⾋< $BEf]xLLwN5rETd@&ͳp&_aXeĭݐHC/Jm[.kI}@K8ᜈi+6WW TZ+1 k[{L暺{."QWqv|Ҭșn,d%+o~=s/!ORpX=r V5Λ28ɰ37MB>&Jbq7]\S_567!)KĖ (Iiږgk YIs˗b0uBתn`?wyC)./iWwQ''`a ,m2>]uLxf/az}Viwn=9ʍKxC#07MXJCq;͆>@R[uJ:i)rWS:Uqsx aKʖVeu&7V?}Ug#q_r[=ENWոѧDV6{rk a: ]_ uJȕ@w㓈[z.z;▩Mm)~tPgd& O{[כ%.mAdH,=iY7btyLb(Ձ KSW YLH19i]ϊ(o,+1oI1O驱&!ƒ$ܲvbrnTFFm$}"( QA1P*X%Pm9ƓN cØ5>TwU<̡ 'q]lQlB46:v`ԧCa'XqNFZik4/C5Dw>6~xhU;!~WJqO(]% ~a L0&]Q>TlgїëђZs~Qh>^ib ;ލ6WRfȤSp|O-_fKϓt,۞ *=7v=* %2å9ж:;t ܌52|a{AKo"E%<:: Z:OCh͋GZ <_jdˠWF/D}F\sTB/> BӍm1Kh:L_vJĚn S4+=C吢 [<'!_R;d _ =Ǔ(iAR,kH.Nd&P!0h|^)Q}LL̸lۭ)[5>8p ~bɒtfȍСm["  >xkV|DBΖcA6hC<5Cxm<<֋!3U<==v+ z#2\@u+,z~;4{zePXR& ]R}ۀMT/fVvߟp^G|1<|gklށU] mUXEN g^4NQJO׫en1ƒ!7~|wWSMvpaeEP *awAD] (2,*ђ4\;oex{Lz9lıpU g+Q2\E{2uAdꎸj'`,歃 Og!SKfeK8W(&t=Jy0B(\&%X3trtu,NZJkH-Hڒ8:̏O1VV1mg%eM֑=I#EہM-~p&+0+uc5;`xMu›D]g0uϾ{#YOs ~mWF|{}#ol޲OZ]y<Ϥdo5 <1KM}|Ҷ+e,e l Mv2X{kIlU,MH 1eE#~+YSA,?|RY~`j0ISfYH @=mI 1ZjWSTU8 6YT-~wa CR! <*C[:!_wrm8o>?B/OP@S٣p+sؕT4RO4i&#Op 0EosEx瞯5c DRe \qYbKMBݓ kʅSD88W?_v;KV''+ ~bܪ7GȵS;GdD/LSY-0ο'-qtv?X:doK$W # S#wŻG,)j7D\v?6Ƹ͇4g\\fRd }PW}/&. )>4 Bk*Y'=T*Oed1(A+Ii@%[3Xq_$;mc YZdrpm-0.5.1/test/drpm-new.rpm000066400000000000000000000163641421165313000157150ustar00rootroot00000000000000drpm-devel-0.3.0-1.fc20T>D ,0@83940378b6ea1217292ccdfdbbcea87ab1d22c22}'2fir :>8 ? d   ;      $@(8 9D:tFGHIXY\4]@^Zbd Ne Sf Vl Xt pu |v w x y z  Cdrpm-devel0.3.01.fc20C interface for the drpm libraryThe drpm-devel package provides a C interface (drpm.h) for the drpm library.V dhcp129-203.brq.redhat.com8~LGPLv3+Unspecifiedhttp://fedorahosted.org/drpmlinuxx86_647 U9V UΖ4eaed2231f32bd534595e3a1f157d5156e4ade700646ddffaa241b9eb667ab5574c8145b311a485aa422e1a568642a9883630a76c4d4f0d249eb4ac99266c288libdrpm.so.0rootrootrootrootrootrootdrpm-0.3.0-1.fc20.src.rpmdrpm-develdrpm-devel(x86-64)pkgconfig(drpm)@@    /usr/bin/pkg-configdrpm(x86-64)libdrpm.so.0()(64bit)rpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)0.3.0-1.fc203.0.4-14.6.0-14.0-15.2-14.11.3U6@U@U(UJ@U@U.@TT@T@T@T@T~@T[bTZ@Matej Chalk 0.3.0-1Matej Chalk 0.2.1-1Matej Chalk 0.2.0-2Matej Chalk 0.2.0-1Matej Chalk 0.1.3-4Matej Chalk 0.1.3-3Matej Chalk 0.1.3-2Matej Chalk 0.1.3-1Matej Chalk 0.1.2-4Matej Chalk 0.1.2-3Matej Chalk 0.1.2-2Matej Chalk 0.1.2-1Matej Chalk 0.1.1-1Matej Chalk 0.1.0-1- Bumped minor version (deltarpm creation added)- Added openssl dependency- Fixed bug in test suite- Bumped minor version- Memory test only for architectures that have valgrind (#1232157)- Added cmocka and valgrind package dependencies- Added check section- Bumped version to 0.1.3 - Added CMake tool- Enabled hardened build- Added unversioned .so to package to enable linking with -ldrpm- Removed unversioned .so from package - Included copies of both GPLv3 and LGPLv3- Bumped version to 0.1.2 - Added drpm.pc file for pkgconfig tool- Bumped version to 0.1.1- Initial RPM releasedhcp129-203.brq.redhat.com 14436025820.3.0-1.fc200.3.0-1.fc200.3.0drpm.hlibdrpm.sodrpm.pc/usr/include//usr/lib64//usr/lib64/pkgconfig/-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=genericcpioxz2x86_64-redhat-linux-gnuC source, ASCII textpkgconfig fileRPRW"2i/(2=?7zXZ !#,: ] b2u Q{JGʼhxXK${V<$X>+AUIH`p˽D%&T!#x߻rJҜ;ÒtaHu1S;P1\e(NE~u^#Hz/V0\lTX*p,(* psSyeqKͷ'i.C5.}6DY"b؃t+}p=yD> >y"vCTWGgN2XA0Nꨡ_ڲyTS s򹢽5J$\`Q_H0>Xѡ}Q5鉆;n8XrGj*M*d?[c19{TBLAK~Rty9I %;';LwF!{MYpw?a_?/T[AE<"h⯺V_!ڱACJ *׺Tԛu2|Qܟa@o]T:]9i6%InE?^2aR v%idzl{p(N4fRR;p+hVXp:XΘAR Q2=A 5b#vYdX$VV1$E2TEDbԾLu(| !͢|edV G1ar %JfgMU>j,[cxbr¡t#|UQty7wsSg:U@r)қ3]K%n#>YlfAGxa:y 'na*Z.#DrUNVyeW60s/Ew<@u !|[in0J|V;#;dVZOE~j/WۂNO[1S*yW7$O56'oص&,YQ Vj\}sp;4$7WQW=ueH4a7kdn( 'E+A|`}14FNJ[x(xH0xKgf"U4/,doz!Ie@ju8®02citvݬuȴRIQ&;JU/5^lB(eVw^F[7@.ݙV]"D4q|JC+^8a&PB_x1RQeijMa]h6v9woc0cM}wpC᧝JL<9<_&hUIUMǁ2bCH;~x: n?ţ!ۡRjTfA\ͫl܆R?SA挦gˀvrHAdTEw>yI(pG;8E뗊&V&tGRjk^ʡeYr &A)r$Sqs[$/[Qœ̣+8/>}ш`)5 gM)^m]q I;зixpt v+]ɲgumѰYJ({ u "Z"Cb?X);4 {7wz?s]rLTٸ|Q<"j!'f]5U$YD0Fu9ʸFL+5?ч@i.j^mvcƖ~8>Ok$L倭 Iv9.b8"M8C: CfBbSGFEfiRᅨs*2ؼ97!L8Y*}$r@EzҴ3L.4x=VL_fDZ/#sW/c ^Rcyi_$ޤG6a".}dEnT63<ԐjK f/+{X-tKIWQ:>ڲ&|`qmP"z25ZڶA^POT 4:`FRMXk~-6GWG sy&aoSРz|Y_gx#(z./r M<]p,jʆlx*ɦ! Vc2򒗟kx*)P][fűz^jțd?S9,Fb+3Xڻ&xN?s탉Ϙb]>-MoDgD8pY^E?+Y']d , .*oRJZ2.#Ae1am.=X!40|Ӂ)TnQVеW~};bН:hށR-Hbm,C[y>9inuܶ YZdrpm-0.5.1/test/drpm-old.rpm000066400000000000000000000124441421165313000156750ustar00rootroot00000000000000drpm-devel-0.1.3-1.fc20T>D ,0@b764f40149355772137f18213718b00883f9a0d5 b?YL7jZCw8>8\?Ld   ;      $@(8 90 : F*GPH\IhXlYx\]^bdefltuvwx y,z8HCdrpm-devel0.1.31.fc20C interface for the drpm libraryThe drpm-devel package provides a C interface (drpm.h) for the drpm library.U~dhcp129-203.brq.redhat.comLGPLv3+Unspecifiedhttp://fedorahosted.org/drpmlinuxx86_64~ UyU~T\5245de81520623fe0f5f183e717142b13666c7c56e111090b8b2ab0d97a78f57a7448e2653d7698befa5f46c38f96539869910e744c9f0e6927f4e3912a64079libdrpm.so.0rootrootrootrootrootrootdrpm-0.1.3-1.fc20.src.rpmdrpm-develdrpm-devel(x86-64)pkgconfig(drpm)@@    /usr/bin/pkg-configdrpm(x86-64)libdrpm.so.0()(64bit)rpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)0.1.3-1.fc203.0.4-14.6.0-14.0-15.2-14.11.3U.@TT@T@T@T@T~@T[bTZ@Matej Chalk 0.1.3-3Matej Chalk 0.1.3-2Matej Chalk 0.1.3-1Matej Chalk 0.1.2-4Matej Chalk 0.1.2-3Matej Chalk 0.1.2-2Matej Chalk 0.1.2-1Matej Chalk 0.1.1-1Matej Chalk 0.1.0-1- Added cmocka and valgrind package dependencies- Added check section- Bumped version to 0.1.3 - Added CMake tool- Enabled hardened build- Added unversioned .so to package to enable linking with -ldrpm- Removed unversioned .so from package - Included copies of both GPLv3 and LGPLv3- Bumped version to 0.1.2 - Added drpm.pc file for pkgconfig tool- Bumped version to 0.1.1- Initial RPM releasedhcp129-203.brq.redhat.com 14343595480.1.3-1.fc200.1.3-1.fc200.1.3drpm.hlibdrpm.sodrpm.pc/usr/include//usr/lib64//usr/lib64/pkgconfig/-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=genericcpioxz2x86_64-redhat-linux-gnuC source, ASCII textpkgconfig fileRPRkw|բsv?7zXZ !#,7] b2u Q{Jy]->L_V26. zG>+mm7#o({PUn[Q6fG{%DHJƩSxv0Ad`:r)^Ug7g=Է 3gA|~f׽p+eaz-oM+ 17^h߰1k$b׭O*ԽWy'Xe$=5z{9Y̢<*4TvbuEi!]ϵe-]XNܻ6.C}3kseY|\%e˨O(4wra~H@^7'(qۂ]gj\h( I[_1nwP_\E ݎǂ~xAGү@Dov63KTW(Z`mmO <Ϩ{͉^B ۷`092z)95{{# Hֺ:;nr@2gX/ᐑClWt#eJ}4`V~D@a{8:KX˺ҍB "pΛ`VyPnRtv>8=6nEfYK/۸wʢ ȟ `4$2ZwJ$U|"2np߽&gIn]5l*1iuw#=l Sa^T-Uw8N:!Za S.`'lrahdjq8x[ Bx)8/ J qH ͼDĆIa)c<ֽZHrX>Av8+ȸDk,µz4>]PML1P/ſ]\`\ҷ#N.o NB/b%(n,f*K $X HN&Y[v-`s:siNcfmzTh7}'qB:G'EgߘiUOwi _3_;ʗ.܅_J$<Ϲv_KTv~۰It0G 'x=?M2(S9N㨳;f ը5dR#5̛'AQ0aRzv-RL+a"8z܇Ԃ:3ꐡ{,&J0R"Ö/im5|z4IJ| k Copyright (C) 2016 Red Hat This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include "../src/drpm.h" #include #include #include #include #include #include #include #include #include #include #include #define DELTARPM_COUNT 6 #define DELTARPM_NODIFF "nodiff.drpm" #define DELTARPM_IDENTITY "identity.drpm" #define DELTARPM_RPMONLY "rpmonly.drpm" #define DELTARPM_STANDARD "standard.drpm" #define DELTARPM_RPMONLY_NOADDBLK "rpmonly-noaddblk.drpm" #define DELTARPM_STANDARD_LZIP "standard-lzip.drpm" #define DELTARPM_STANDARD_ZSTD "standard-zstd.drpm" #define OLDRPM_1 "drpm-old.rpm" #define NEWRPM_1 "drpm-new.rpm" #define OLDRPM_2 "cmocka-old.rpm" #define NEWRPM_2 "cmocka-new.rpm" #define RPMOUT_STANDARD "standard.rpm" #define RPMOUT_RPMONLY_NOADDBLK "rpmonly-noaddblk.rpm" #define RPMOUT_STANDARD_LZIP "standard-lzip.rpm" #define RPMOUT_STANDARD_ZSTD "standard-zstd.rpm" #define SEQFILE "seqfile.txt" // garbage collector for drpm_read tests struct read_deltas { unsigned short index; drpm *deltas[DELTARPM_COUNT]; char *filenames[DELTARPM_COUNT]; char *src_nevrs[DELTARPM_COUNT]; char *tgt_nevrs[DELTARPM_COUNT]; char *sequences[DELTARPM_COUNT]; char *tgt_md5s[DELTARPM_COUNT]; char *tgt_leads[DELTARPM_COUNT]; unsigned long *int_copies_arrays[DELTARPM_COUNT]; unsigned long *ext_copies_arrays[DELTARPM_COUNT]; }; static off_t filesize(const char *path) { struct stat stats; if (stat(path, &stats) != 0) return -1; return stats.st_size; } /***************************** drpm_make ******************************/ static int make_setup(void **state) { drpm_make_options *opts; if (drpm_make_options_init(&opts) != DRPM_ERR_OK) return -1; *state = opts; return 0; } static int make_teardown(void **state) { drpm_make_options *opts = *state; if (drpm_make_options_destroy(&opts) != DRPM_ERR_OK) return -1; return 0; } // equivalent to: makedeltarpm -u -r static void make_nodiff(void **state) { drpm_make_options *opts = *state; assert_int_equal(DRPM_ERR_OK, drpm_make_options_defaults(opts)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_type(opts, DRPM_TYPE_RPMONLY)); assert_int_equal(DRPM_ERR_OK, drpm_make(OLDRPM_1, NULL, DELTARPM_NODIFF, opts)); } // equivalent to: makedeltarpm -u -V 2 -z uncompressed static void make_identity(void **state) { drpm_make_options *opts = *state; assert_int_equal(DRPM_ERR_OK, drpm_make_options_defaults(opts)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_version(opts, 2)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_delta_comp(opts, DRPM_COMP_NONE, DRPM_COMP_LEVEL_DEFAULT)); assert_int_equal(DRPM_ERR_OK, drpm_make(NULL, NEWRPM_1, DELTARPM_IDENTITY, opts)); } // equivalent to: makedeltarpm -r -z bzip2.7,lzma static void make_rpmonly(void **state) { drpm_make_options *opts = *state; assert_int_equal(DRPM_ERR_OK, drpm_make_options_defaults(opts)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_type(opts, DRPM_TYPE_RPMONLY)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_delta_comp(opts, DRPM_COMP_BZIP2, 7)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_addblk_comp(opts, DRPM_COMP_LZMA, DRPM_COMP_LEVEL_DEFAULT)); assert_int_equal(DRPM_ERR_OK, drpm_make(OLDRPM_2, NEWRPM_2, DELTARPM_RPMONLY, opts)); } // equivalent to: makedeltarpm -s static void make_standard(void **state) { drpm_make_options *opts = *state; assert_int_equal(DRPM_ERR_OK, drpm_make_options_defaults(opts)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_seqfile(opts, SEQFILE));; assert_int_equal(DRPM_ERR_OK, drpm_make(OLDRPM_1, NEWRPM_1, DELTARPM_STANDARD, opts)); } // equivalent to: makedeltarpm -r -z gzip,off static void make_rpmonly_noaddblk(void **state) { drpm_make_options *opts = *state; assert_int_equal(DRPM_ERR_OK, drpm_make_options_defaults(opts)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_type(opts, DRPM_TYPE_RPMONLY)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_delta_comp(opts, DRPM_COMP_GZIP, DRPM_COMP_LEVEL_DEFAULT)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_forbid_addblk(opts)); assert_int_equal(DRPM_ERR_OK, drpm_make(OLDRPM_2, NEWRPM_2, DELTARPM_RPMONLY_NOADDBLK, opts)); } #ifdef HAVE_LZLIB_DEVEL // testing lzip support (not in makedeltarpm) static void make_standard_lzip(void **state) { drpm_make_options *opts = *state; assert_int_equal(DRPM_ERR_OK, drpm_make_options_defaults(opts)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_delta_comp(opts, DRPM_COMP_LZIP, DRPM_COMP_LEVEL_DEFAULT));; assert_int_equal(DRPM_ERR_OK, drpm_make(OLDRPM_2, NEWRPM_2, DELTARPM_STANDARD_LZIP, opts)); } #endif #ifdef WITH_ZSTD // testing zstd support static void make_standard_zstd(void **state) { drpm_make_options *opts = *state; assert_int_equal(DRPM_ERR_OK, drpm_make_options_defaults(opts)); assert_int_equal(DRPM_ERR_OK, drpm_make_options_set_delta_comp(opts, DRPM_COMP_ZSTD, DRPM_COMP_LEVEL_DEFAULT));; assert_int_equal(DRPM_ERR_OK, drpm_make(OLDRPM_2, NEWRPM_2, DELTARPM_STANDARD_ZSTD, opts)); } #endif /***************************** drpm_read ******************************/ static int read_setup(void **state) { const struct read_deltas drpms_init = { .index = 0, .deltas = {NULL}, .filenames = {NULL}, .src_nevrs = {NULL}, .tgt_nevrs = {NULL}, .sequences = {NULL}, .tgt_md5s = {NULL}, .tgt_leads = {NULL}, .int_copies_arrays = {NULL}, .ext_copies_arrays = {NULL}, }; struct read_deltas *drpms; if ((drpms = malloc(sizeof(struct read_deltas))) == NULL) return -1; *drpms = drpms_init; *state = drpms; return 0; } static int read_teardown(void **state) { struct read_deltas *drpms = *state; while (drpms->index-- > 0) { drpm_destroy(&drpms->deltas[drpms->index]); free(drpms->filenames[drpms->index]); free(drpms->src_nevrs[drpms->index]); free(drpms->tgt_nevrs[drpms->index]); free(drpms->sequences[drpms->index]); free(drpms->tgt_md5s[drpms->index]); free(drpms->tgt_leads[drpms->index]); free(drpms->int_copies_arrays[drpms->index]); free(drpms->ext_copies_arrays[drpms->index]); } free(drpms); return 0; } static void read_nodiff(void **state) { struct read_deltas *drpms = *state; unsigned index = drpms->index++; drpm *delta = NULL; const char *delta_name = DELTARPM_NODIFF; char *filename; unsigned version; unsigned type; unsigned comp; char *sequence; char *src_nevr; char *tgt_nevr; unsigned long tgt_size; char *tgt_md5; unsigned tgt_comp; unsigned long tgt_header_len; char *tgt_lead; unsigned long *int_copies; unsigned long int_copies_size; unsigned long *ext_copies; unsigned long ext_copies_size; unsigned long long ext_data_len; unsigned long long int_data_len; assert_int_equal(DRPM_ERR_OK, drpm_read(&drpms->deltas[index], delta_name)); delta = drpms->deltas[index]; assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_VERSION, &version)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TYPE, &type)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_COMP, &comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TGTCOMP, &tgt_comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTSIZE, &tgt_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTHEADERLEN, &tgt_header_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_EXTDATALEN, &ext_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_INTDATALEN, &int_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_FILENAME, &drpms->filenames[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SEQUENCE, &drpms->sequences[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SRCNEVR, &drpms->src_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTNEVR, &drpms->tgt_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTMD5, &drpms->tgt_md5s[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTLEAD, &drpms->tgt_leads[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_INTCOPIES, &drpms->int_copies_arrays[index], &int_copies_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_EXTCOPIES, &drpms->ext_copies_arrays[index], &ext_copies_size)); filename = drpms->filenames[index]; sequence = drpms->sequences[index]; src_nevr = drpms->src_nevrs[index]; tgt_nevr = drpms->tgt_nevrs[index]; tgt_md5 = drpms->tgt_md5s[index]; tgt_lead = drpms->tgt_leads[index]; int_copies = drpms->int_copies_arrays[index]; ext_copies = drpms->ext_copies_arrays[index]; assert_non_null(filename); assert_non_null(sequence); assert_non_null(src_nevr); assert_non_null(tgt_nevr); assert_non_null(tgt_md5); assert_non_null(tgt_lead); // no internal/external copies for no-diff deltas assert_null(int_copies); assert_null(ext_copies); assert_string_equal(delta_name, filename); assert_int_equal(DRPM_TYPE_RPMONLY, type); // rpm-only assert_int_equal(3, version); assert_int_equal(MD5_DIGEST_LENGTH * 2, strlen(sequence)); // NEVRs should equal for identity deltas assert_string_equal(src_nevr, tgt_nevr); assert_int_equal(filesize(OLDRPM_1), tgt_size); assert_int_equal(MD5_DIGEST_LENGTH * 2, strlen(tgt_md5)); assert_int_not_equal(0, tgt_header_len); // header "in diff" (rpm-only) assert_not_in_range(strlen(tgt_lead), 0, (96 + 16) - 1); // no diff data assert_int_equal(0, int_copies_size); assert_int_equal(0, ext_copies_size); assert_int_equal(0, int_data_len); assert_int_equal(0, ext_data_len); } static void read_identity(void **state) { struct read_deltas *drpms = *state; unsigned index = drpms->index++; drpm *delta = NULL; const char *delta_name = DELTARPM_IDENTITY; char *filename; unsigned version; unsigned type; unsigned comp; char *sequence; char *src_nevr; char *tgt_nevr; unsigned long tgt_size; char *tgt_md5; unsigned tgt_comp; unsigned long tgt_header_len; char *tgt_lead; unsigned long *int_copies; unsigned long int_copies_size; unsigned long *ext_copies; unsigned long ext_copies_size; unsigned long long ext_data_len; unsigned long long int_data_len; assert_int_equal(DRPM_ERR_OK, drpm_read(&drpms->deltas[index], delta_name)); delta = drpms->deltas[index]; assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_VERSION, &version)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TYPE, &type)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_COMP, &comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TGTCOMP, &tgt_comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTSIZE, &tgt_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTHEADERLEN, &tgt_header_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_EXTDATALEN, &ext_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_INTDATALEN, &int_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_FILENAME, &drpms->filenames[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SEQUENCE, &drpms->sequences[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SRCNEVR, &drpms->src_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTNEVR, &drpms->tgt_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTMD5, &drpms->tgt_md5s[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTLEAD, &drpms->tgt_leads[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_INTCOPIES, &drpms->int_copies_arrays[index], &int_copies_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_EXTCOPIES, &drpms->ext_copies_arrays[index], &ext_copies_size)); filename = drpms->filenames[index]; sequence = drpms->sequences[index]; src_nevr = drpms->src_nevrs[index]; tgt_nevr = drpms->tgt_nevrs[index]; tgt_md5 = drpms->tgt_md5s[index]; tgt_lead = drpms->tgt_leads[index]; int_copies = drpms->int_copies_arrays[index]; ext_copies = drpms->ext_copies_arrays[index]; assert_non_null(filename); assert_non_null(sequence); assert_non_null(src_nevr); assert_non_null(tgt_nevr); assert_non_null(tgt_md5); assert_non_null(tgt_lead); assert_string_equal(delta_name, filename); assert_int_equal(DRPM_TYPE_STANDARD, type); // standard assert_int_equal(2, version); // version 2 assert_int_equal(DRPM_COMP_NONE, comp); // uncompressed assert_not_in_range(strlen(sequence), 0, (MD5_DIGEST_LENGTH * 2) - 1); // NEVRs should equal for identity deltas assert_string_equal(src_nevr, tgt_nevr); assert_int_equal(filesize(NEWRPM_1), tgt_size); assert_int_equal(MD5_DIGEST_LENGTH * 2, strlen(tgt_md5)); assert_int_equal(0, tgt_header_len); // header not in diff assert_not_in_range(strlen(tgt_lead), 0, (96 + 16) - 1); assert_true(int_copies_size % 2 == 0); assert_true(ext_copies_size % 2 == 0); unsigned long int_copies_count = int_copies_size / 2; unsigned long ext_copies_count = ext_copies_size / 2; unsigned long count; unsigned long long off; count = 0; off = 0; for (unsigned long i = 0; i < int_copies_count; i++) { count += int_copies[2 * i]; assert_false(count > ext_copies_count); off += int_copies[2 * i + 1]; assert_false(off > int_data_len); } off = 0; for (unsigned long i = 0; i < ext_copies_count; i++) { off += (int32_t)ext_copies[2 * i]; assert_false(off > ext_data_len); off += ext_copies[2 * i + 1]; assert_int_not_equal(0, off); assert_false(off > ext_data_len); } } static void read_rpmonly(void **state) { struct read_deltas *drpms = *state; unsigned index = drpms->index++; drpm *delta = NULL; const char *delta_name = DELTARPM_RPMONLY; char *filename; unsigned version; unsigned type; unsigned comp; char *sequence; char *src_nevr; char *tgt_nevr; unsigned long tgt_size; char *tgt_md5; unsigned tgt_comp; unsigned long tgt_header_len; char *tgt_lead; unsigned long *int_copies; unsigned long int_copies_size; unsigned long *ext_copies; unsigned long ext_copies_size; unsigned long long ext_data_len; unsigned long long int_data_len; assert_int_equal(DRPM_ERR_OK, drpm_read(&drpms->deltas[index], delta_name)); delta = drpms->deltas[index]; assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_VERSION, &version)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TYPE, &type)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_COMP, &comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TGTCOMP, &tgt_comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTSIZE, &tgt_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTHEADERLEN, &tgt_header_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_EXTDATALEN, &ext_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_INTDATALEN, &int_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_FILENAME, &drpms->filenames[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SEQUENCE, &drpms->sequences[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SRCNEVR, &drpms->src_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTNEVR, &drpms->tgt_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTMD5, &drpms->tgt_md5s[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTLEAD, &drpms->tgt_leads[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_INTCOPIES, &drpms->int_copies_arrays[index], &int_copies_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_EXTCOPIES, &drpms->ext_copies_arrays[index], &ext_copies_size)); filename = drpms->filenames[index]; sequence = drpms->sequences[index]; src_nevr = drpms->src_nevrs[index]; tgt_nevr = drpms->tgt_nevrs[index]; tgt_md5 = drpms->tgt_md5s[index]; tgt_lead = drpms->tgt_leads[index]; int_copies = drpms->int_copies_arrays[index]; ext_copies = drpms->ext_copies_arrays[index]; assert_non_null(filename); assert_non_null(sequence); assert_non_null(src_nevr); assert_non_null(tgt_nevr); assert_non_null(tgt_md5); assert_non_null(tgt_lead); assert_string_equal(delta_name, filename); assert_int_equal(DRPM_TYPE_RPMONLY, type); // rpm-only assert_int_equal(3, version); assert_int_equal(DRPM_COMP_BZIP2, comp); // bzip2 compressed assert_int_equal(MD5_DIGEST_LENGTH * 2, strlen(sequence)); assert_int_equal(filesize(NEWRPM_2), tgt_size); assert_int_equal(MD5_DIGEST_LENGTH * 2, strlen(tgt_md5)); assert_int_not_equal(0, tgt_header_len); // header in diff assert_not_in_range(strlen(tgt_lead), 0, (96 + 16) - 1); assert_true(int_copies_size % 2 == 0); assert_true(ext_copies_size % 2 == 0); unsigned long int_copies_count = int_copies_size / 2; unsigned long ext_copies_count = ext_copies_size / 2; unsigned long count; unsigned long long off; count = 0; off = 0; for (unsigned long i = 0; i < int_copies_count; i++) { count += int_copies[2 * i]; assert_false(count > ext_copies_count); off += int_copies[2 * i + 1]; assert_false(off > int_data_len); } off = 0; for (unsigned long i = 0; i < ext_copies_count; i++) { off += (int32_t)ext_copies[2 * i]; assert_false(off > ext_data_len); off += ext_copies[2 * i + 1]; assert_int_not_equal(0, off); assert_false(off > ext_data_len); } } static void read_standard(void **state) { struct read_deltas *drpms = *state; unsigned index = drpms->index++; drpm *delta = NULL; const char *delta_name = DELTARPM_STANDARD; char *filename; unsigned version; unsigned type; unsigned comp; char *sequence; char *src_nevr; char *tgt_nevr; unsigned long tgt_size; char *tgt_md5; unsigned tgt_comp; unsigned long tgt_header_len; char *tgt_lead; unsigned long *int_copies; unsigned long int_copies_size; unsigned long *ext_copies; unsigned long ext_copies_size; unsigned long long ext_data_len; unsigned long long int_data_len; assert_int_equal(DRPM_ERR_OK, drpm_read(&drpms->deltas[index], delta_name)); delta = drpms->deltas[index]; assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_VERSION, &version)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TYPE, &type)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_COMP, &comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TGTCOMP, &tgt_comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTSIZE, &tgt_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTHEADERLEN, &tgt_header_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_EXTDATALEN, &ext_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_INTDATALEN, &int_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_FILENAME, &drpms->filenames[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SEQUENCE, &drpms->sequences[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SRCNEVR, &drpms->src_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTNEVR, &drpms->tgt_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTMD5, &drpms->tgt_md5s[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTLEAD, &drpms->tgt_leads[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_INTCOPIES, &drpms->int_copies_arrays[index], &int_copies_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_EXTCOPIES, &drpms->ext_copies_arrays[index], &ext_copies_size)); filename = drpms->filenames[index]; sequence = drpms->sequences[index]; src_nevr = drpms->src_nevrs[index]; tgt_nevr = drpms->tgt_nevrs[index]; tgt_md5 = drpms->tgt_md5s[index]; tgt_lead = drpms->tgt_leads[index]; int_copies = drpms->int_copies_arrays[index]; ext_copies = drpms->ext_copies_arrays[index]; assert_non_null(filename); assert_non_null(sequence); assert_non_null(src_nevr); assert_non_null(tgt_nevr); assert_non_null(tgt_md5); assert_non_null(tgt_lead); assert_string_equal(delta_name, filename); assert_int_equal(DRPM_TYPE_STANDARD, type); // standard assert_int_equal(3, version); assert_int_equal(tgt_comp, comp); // delta compression same as RPM assert_not_in_range(strlen(sequence), 0, (MD5_DIGEST_LENGTH * 2) - 1); assert_int_equal(filesize(NEWRPM_1), tgt_size); assert_int_equal(MD5_DIGEST_LENGTH * 2, strlen(tgt_md5)); assert_int_equal(0, tgt_header_len); // header not in diff assert_not_in_range(strlen(tgt_lead), 0, (96 + 16) - 1); assert_true(int_copies_size % 2 == 0); assert_true(ext_copies_size % 2 == 0); unsigned long int_copies_count = int_copies_size / 2; unsigned long ext_copies_count = ext_copies_size / 2; unsigned long count; unsigned long long off; count = 0; off = 0; for (unsigned long i = 0; i < int_copies_count; i++) { count += int_copies[2 * i]; assert_false(count > ext_copies_count); off += int_copies[2 * i + 1]; assert_false(off > int_data_len); } off = 0; for (unsigned long i = 0; i < ext_copies_count; i++) { off += (int32_t)ext_copies[2 * i]; assert_false(off > ext_data_len); off += ext_copies[2 * i + 1]; assert_int_not_equal(0, off); assert_false(off > ext_data_len); } } static void read_rpmonly_noaddblk(void **state) { struct read_deltas *drpms = *state; unsigned index = drpms->index++; drpm *delta = NULL; const char *delta_name = DELTARPM_RPMONLY_NOADDBLK; char *filename; unsigned version; unsigned type; unsigned comp; char *sequence; char *src_nevr; char *tgt_nevr; unsigned long tgt_size; char *tgt_md5; unsigned tgt_comp; unsigned long tgt_header_len; char *tgt_lead; unsigned long *int_copies; unsigned long int_copies_size; unsigned long *ext_copies; unsigned long ext_copies_size; unsigned long long ext_data_len; unsigned long long int_data_len; assert_int_equal(DRPM_ERR_OK, drpm_read(&drpms->deltas[index], delta_name)); delta = drpms->deltas[index]; assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_VERSION, &version)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TYPE, &type)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_COMP, &comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TGTCOMP, &tgt_comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTSIZE, &tgt_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTHEADERLEN, &tgt_header_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_EXTDATALEN, &ext_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_INTDATALEN, &int_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_FILENAME, &drpms->filenames[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SEQUENCE, &drpms->sequences[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SRCNEVR, &drpms->src_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTNEVR, &drpms->tgt_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTMD5, &drpms->tgt_md5s[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTLEAD, &drpms->tgt_leads[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_INTCOPIES, &drpms->int_copies_arrays[index], &int_copies_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_EXTCOPIES, &drpms->ext_copies_arrays[index], &ext_copies_size)); filename = drpms->filenames[index]; sequence = drpms->sequences[index]; src_nevr = drpms->src_nevrs[index]; tgt_nevr = drpms->tgt_nevrs[index]; tgt_md5 = drpms->tgt_md5s[index]; tgt_lead = drpms->tgt_leads[index]; int_copies = drpms->int_copies_arrays[index]; ext_copies = drpms->ext_copies_arrays[index]; assert_non_null(filename); assert_non_null(sequence); assert_non_null(src_nevr); assert_non_null(tgt_nevr); assert_non_null(tgt_md5); assert_non_null(tgt_lead); assert_string_equal(delta_name, filename); assert_int_equal(DRPM_TYPE_RPMONLY, type); // rpm-only assert_int_equal(3, version); assert_int_equal(DRPM_COMP_GZIP, comp); // gzip compression assert_int_equal(MD5_DIGEST_LENGTH * 2, strlen(sequence)); assert_int_equal(filesize(NEWRPM_2), tgt_size); assert_int_equal(MD5_DIGEST_LENGTH * 2, strlen(tgt_md5)); assert_int_not_equal(0, tgt_header_len); // header in diff assert_not_in_range(strlen(tgt_lead), 0, (96 + 16) - 1); assert_true(int_copies_size % 2 == 0); assert_true(ext_copies_size % 2 == 0); unsigned long int_copies_count = int_copies_size / 2; unsigned long ext_copies_count = ext_copies_size / 2; unsigned long count; unsigned long long off; count = 0; off = 0; for (unsigned long i = 0; i < int_copies_count; i++) { count += int_copies[2 * i]; assert_false(count > ext_copies_count); off += int_copies[2 * i + 1]; assert_false(off > int_data_len); } off = 0; for (unsigned long i = 0; i < ext_copies_count; i++) { off += (int32_t)ext_copies[2 * i]; assert_false(off > ext_data_len); off += ext_copies[2 * i + 1]; assert_int_not_equal(0, off); assert_false(off > ext_data_len); } } #ifdef HAVE_LZLIB_DEVEL static void read_standard_lzip(void **state) { struct read_deltas *drpms = *state; unsigned index = drpms->index++; drpm *delta = NULL; const char *delta_name = DELTARPM_STANDARD_LZIP; char *filename; unsigned version; unsigned type; unsigned comp; char *sequence; char *src_nevr; char *tgt_nevr; unsigned long tgt_size; char *tgt_md5; unsigned tgt_comp; unsigned long tgt_header_len; char *tgt_lead; unsigned long *int_copies; unsigned long int_copies_size; unsigned long *ext_copies; unsigned long ext_copies_size; unsigned long long ext_data_len; unsigned long long int_data_len; assert_int_equal(DRPM_ERR_OK, drpm_read(&drpms->deltas[index], delta_name)); delta = drpms->deltas[index]; assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_VERSION, &version)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TYPE, &type)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_COMP, &comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TGTCOMP, &tgt_comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTSIZE, &tgt_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTHEADERLEN, &tgt_header_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_EXTDATALEN, &ext_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_INTDATALEN, &int_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_FILENAME, &drpms->filenames[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SEQUENCE, &drpms->sequences[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SRCNEVR, &drpms->src_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTNEVR, &drpms->tgt_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTMD5, &drpms->tgt_md5s[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTLEAD, &drpms->tgt_leads[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_INTCOPIES, &drpms->int_copies_arrays[index], &int_copies_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_EXTCOPIES, &drpms->ext_copies_arrays[index], &ext_copies_size)); filename = drpms->filenames[index]; sequence = drpms->sequences[index]; src_nevr = drpms->src_nevrs[index]; tgt_nevr = drpms->tgt_nevrs[index]; tgt_md5 = drpms->tgt_md5s[index]; tgt_lead = drpms->tgt_leads[index]; int_copies = drpms->int_copies_arrays[index]; ext_copies = drpms->ext_copies_arrays[index]; assert_non_null(filename); assert_non_null(sequence); assert_non_null(src_nevr); assert_non_null(tgt_nevr); assert_non_null(tgt_md5); assert_non_null(tgt_lead); assert_string_equal(delta_name, filename); assert_int_equal(DRPM_TYPE_STANDARD, type); // standard assert_int_equal(3, version); assert_int_equal(DRPM_COMP_LZIP, comp); // lzip compression assert_not_in_range(strlen(sequence), 0, (MD5_DIGEST_LENGTH * 2) - 1); assert_int_equal(filesize(NEWRPM_2), tgt_size); assert_int_equal(MD5_DIGEST_LENGTH * 2, strlen(tgt_md5)); assert_int_equal(0, tgt_header_len); // header not in diff assert_not_in_range(strlen(tgt_lead), 0, (96 + 16) - 1); assert_true(int_copies_size % 2 == 0); assert_true(ext_copies_size % 2 == 0); unsigned long int_copies_count = int_copies_size / 2; unsigned long ext_copies_count = ext_copies_size / 2; unsigned long count; unsigned long long off; count = 0; off = 0; for (unsigned long i = 0; i < int_copies_count; i++) { count += int_copies[2 * i]; assert_false(count > ext_copies_count); off += int_copies[2 * i + 1]; assert_false(off > int_data_len); } off = 0; for (unsigned long i = 0; i < ext_copies_count; i++) { off += (int32_t)ext_copies[2 * i]; assert_false(off > ext_data_len); off += ext_copies[2 * i + 1]; assert_int_not_equal(0, off); assert_false(off > ext_data_len); } } #endif #ifdef WITH_ZSTD static void read_standard_zstd(void **state) { struct read_deltas *drpms = *state; unsigned index = drpms->index++; drpm *delta = NULL; const char *delta_name = DELTARPM_STANDARD_ZSTD; char *filename; unsigned version; unsigned type; unsigned comp; char *sequence; char *src_nevr; char *tgt_nevr; unsigned long tgt_size; char *tgt_md5; unsigned tgt_comp; unsigned long tgt_header_len; char *tgt_lead; unsigned long *int_copies; unsigned long int_copies_size; unsigned long *ext_copies; unsigned long ext_copies_size; unsigned long long ext_data_len; unsigned long long int_data_len; assert_int_equal(DRPM_ERR_OK, drpm_read(&drpms->deltas[index], delta_name)); delta = drpms->deltas[index]; assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_VERSION, &version)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TYPE, &type)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_COMP, &comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_uint(delta, DRPM_TAG_TGTCOMP, &tgt_comp)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTSIZE, &tgt_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong(delta, DRPM_TAG_TGTHEADERLEN, &tgt_header_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_EXTDATALEN, &ext_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_ullong(delta, DRPM_TAG_INTDATALEN, &int_data_len)); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_FILENAME, &drpms->filenames[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SEQUENCE, &drpms->sequences[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_SRCNEVR, &drpms->src_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTNEVR, &drpms->tgt_nevrs[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTMD5, &drpms->tgt_md5s[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_string(delta, DRPM_TAG_TGTLEAD, &drpms->tgt_leads[index])); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_INTCOPIES, &drpms->int_copies_arrays[index], &int_copies_size)); assert_int_equal(DRPM_ERR_OK, drpm_get_ulong_array(delta, DRPM_TAG_EXTCOPIES, &drpms->ext_copies_arrays[index], &ext_copies_size)); filename = drpms->filenames[index]; sequence = drpms->sequences[index]; src_nevr = drpms->src_nevrs[index]; tgt_nevr = drpms->tgt_nevrs[index]; tgt_md5 = drpms->tgt_md5s[index]; tgt_lead = drpms->tgt_leads[index]; int_copies = drpms->int_copies_arrays[index]; ext_copies = drpms->ext_copies_arrays[index]; assert_non_null(filename); assert_non_null(sequence); assert_non_null(src_nevr); assert_non_null(tgt_nevr); assert_non_null(tgt_md5); assert_non_null(tgt_lead); assert_string_equal(delta_name, filename); assert_int_equal(DRPM_TYPE_STANDARD, type); // standard assert_int_equal(3, version); assert_int_equal(DRPM_COMP_ZSTD, comp); // zstd compression assert_not_in_range(strlen(sequence), 0, (MD5_DIGEST_LENGTH * 2) - 1); assert_int_equal(filesize(NEWRPM_2), tgt_size); assert_int_equal(MD5_DIGEST_LENGTH * 2, strlen(tgt_md5)); assert_int_equal(0, tgt_header_len); // header not in diff assert_not_in_range(strlen(tgt_lead), 0, (96 + 16) - 1); assert_true(int_copies_size % 2 == 0); assert_true(ext_copies_size % 2 == 0); unsigned long int_copies_count = int_copies_size / 2; unsigned long ext_copies_count = ext_copies_size / 2; unsigned long count; unsigned long long off; count = 0; off = 0; for (unsigned long i = 0; i < int_copies_count; i++) { count += int_copies[2 * i]; assert_false(count > ext_copies_count); off += int_copies[2 * i + 1]; assert_false(off > int_data_len); } off = 0; for (unsigned long i = 0; i < ext_copies_count; i++) { off += (int32_t)ext_copies[2 * i]; assert_false(off > ext_data_len); off += ext_copies[2 * i + 1]; assert_int_not_equal(0, off); assert_false(off > ext_data_len); } } #endif /************************ drpm_check_sequence *************************/ static int check_setup(void **state) { ssize_t line_len; FILE *file; char *sequence = NULL; size_t alloced = 0; if ((file = fopen(SEQFILE, "r")) == NULL) return -1; line_len = getline(&sequence, &alloced, file); fclose(file); if (line_len < 0) return -1; if (sequence[line_len] == '\n') sequence[line_len] = '\0'; *state = sequence; return 0; } static int check_teardown(void **state) { free(*state); return 0; } // can only check uninstalled RPMs static void check_sequence(void **state) { assert_int_equal(DRPM_ERR_OK, drpm_check_sequence(OLDRPM_1, (const char *)*state, DRPM_CHECK_NONE)); } /***************************** drpm_apply *****************************/ static void apply_standard(void **state) { (void)state; assert_int_equal(DRPM_ERR_OK, drpm_apply(OLDRPM_1, DELTARPM_STANDARD, RPMOUT_STANDARD)); } static void apply_rpmonly_noaddblk(void **state) { (void)state; assert_int_equal(DRPM_ERR_OK, drpm_apply(OLDRPM_2, DELTARPM_RPMONLY_NOADDBLK, RPMOUT_RPMONLY_NOADDBLK)); } #ifdef HAVE_LZLIB_DEVEL static void apply_standard_lzip(void **state) { (void)state; assert_int_equal(DRPM_ERR_OK, drpm_apply(OLDRPM_2, DELTARPM_STANDARD_LZIP, RPMOUT_STANDARD_LZIP)); } #endif #ifdef WITH_ZSTD static void apply_standard_zstd(void **state) { (void)state; assert_int_equal(DRPM_ERR_OK, drpm_apply(OLDRPM_2, DELTARPM_STANDARD_ZSTD, RPMOUT_STANDARD_ZSTD)); } #endif /***************************** run tests ******************************/ int main() { int failed; const struct CMUnitTest make_tests[] = { cmocka_unit_test(make_nodiff), cmocka_unit_test(make_identity), cmocka_unit_test(make_rpmonly), cmocka_unit_test(make_standard), cmocka_unit_test(make_rpmonly_noaddblk), #ifdef HAVE_LZLIB_DEVEL cmocka_unit_test(make_standard_lzip) #endif #ifdef WITH_ZSTD cmocka_unit_test(make_standard_zstd) #endif }; const struct CMUnitTest read_tests[] = { cmocka_unit_test(read_nodiff), cmocka_unit_test(read_identity), cmocka_unit_test(read_rpmonly), cmocka_unit_test(read_standard), cmocka_unit_test(read_rpmonly_noaddblk), #ifdef HAVE_LZLIB_DEVEL cmocka_unit_test(read_standard_lzip) #endif #ifdef WITH_ZSTD cmocka_unit_test(read_standard_zstd) #endif }; const struct CMUnitTest check_tests[] = { cmocka_unit_test(check_sequence) }; const struct CMUnitTest apply_tests[] = { cmocka_unit_test(apply_standard), cmocka_unit_test(apply_rpmonly_noaddblk), #ifdef HAVE_LZLIB_DEVEL cmocka_unit_test(apply_standard_lzip) #endif #ifdef WITH_ZSTD cmocka_unit_test(apply_standard_zstd) #endif }; failed = cmocka_run_group_tests_name("drpm_make()", make_tests, make_setup, make_teardown); if (failed) return failed; failed = cmocka_run_group_tests_name("drpm_read()", read_tests, read_setup, read_teardown); if (failed) return failed; failed = cmocka_run_group_tests_name("drpm_check_sequence()", check_tests, check_setup, check_teardown); if (failed) return failed; failed = cmocka_run_group_tests_name("drpm_apply()", apply_tests, NULL, NULL); if (failed) return failed; return 0; } drpm-0.5.1/test/drpm_cmp_files.sh000077500000000000000000000075031421165313000167610ustar00rootroot00000000000000#!/usr/bin/env bash script=$0 function usage { echo "usage: ${script} [ -d ] [ -s ] [ -l ]" } prefix="" skip=false lzip=false while getopts "hd:sl" opt; do case $opt in h) usage exit 0 ;; d) prefix="${OPTARG%/}/" ;; s) skip=true ;; l) lzip=true ;; *) usage exit 1 ;; esac done shift $((OPTIND-1)) pack1=drpm pack2=cmocka oldrpm1="${prefix}${pack1}-old.rpm" newrpm1="${prefix}${pack1}-new.rpm" oldrpm2="${prefix}${pack2}-old.rpm" newrpm2="${prefix}${pack2}-new.rpm" tmpdelta="${prefix}tmp.drpm" copyseq="${prefix}refseqfile-copy.txt" refseq="${prefix}refseqfile.txt" cmpseq="${prefix}seqfile.txt" copyDRPMsha256="${prefix}refdrpm-copy.sha256" refDRPMsha256="${prefix}refdrpm.sha256" cmpDRPMsha256="${prefix}drpm.sha256" refRPMsha256="${prefix}refrpm.sha256" cmpRPMsha256="${prefix}rpm.sha256" declare -a deltas=("${prefix}nodiff.drpm" \ "${prefix}identity.drpm" \ "${prefix}rpmonly.drpm" \ "${prefix}standard.drpm" \ "${prefix}rpmonly-noaddblk.drpm") if [ $lzip = true ]; then deltas+=("${prefix}standard-lzip.drpm") fi rpmstandard="${prefix}standard.rpm" rpmrpmonly="${prefix}rpmonly-noaddblk.rpm" rpmlzip="${prefix}standard-lzip.rpm" if ! [ -f $oldrpm1 ] || ! [ -f $newrpm1 ] || ! [ -f $oldrpm2 ] || ! [ -f $newrpm2 ]; then echo "setup error: missing RPM files" exit 1 fi if ! [ -f $copyseq ] || ! [ -f $copyDRPMsha256 ]; then echo "setup error: missing backup copies of makedeltarpm output" exit 1 fi for delta in ${deltas[@]}; do if ! [ -f ${delta} ]; then echo "previous error: missing deltarpm: ${delta}" exit 1 fi done if ! [ -f ${cmpseq} ]; then echo "previous error: missing sequence file" exit 1 fi if ! [ -f ${rpmstandard} ] || ! [ -f ${rpmrpmonly} ]; then echo "previous error: missing RPM files" exit 1 fi if [ $lzip = true ] && ! [ -f ${rpmlzip} ]; then echo "previous error: missing RPM files (lzip)" exit 1 fi rm -f ${refDRPMsha256} ${cmpDRPMsha256} ${refRPMsha256} ${cmpRPMsha256} if [ $skip = true ]; then cp ${copyDRPMsha256} ${refDRPMsha256} cp ${copyseq} ${refseq} else declare -a makeargs=("-u -r ${oldrpm1}" \ "-u -V 2 -z uncompressed ${newrpm1}" \ "-r -z bzip2.7,lzma ${oldrpm2} ${newrpm2}" \ "-s ${refseq} ${oldrpm1} ${newrpm1}" \ "-r -z gzip,off ${oldrpm2} ${newrpm2}") oldIFS=${IFS} IFS="" for args in ${makeargs[@]}; do command="makedeltarpm ${args} ${tmpdelta}" eval ${command} if ! [ $? ]; then echo "failed with command '${command}'" exit 1 fi sha256sum ${tmpdelta} | awk '{ print $1 }' >> ${refDRPMsha256} done IFS=${oldIFS} fi for delta in ${deltas[@]}; do sha256sum ${delta} | awk '{ print $1 }' >> ${cmpDRPMsha256} done sha256sum ${newrpm1} | awk '{ print $1 }' >> ${refRPMsha256} sha256sum ${newrpm2} | awk '{ print $1 }' >> ${refRPMsha256} sha256sum ${rpmstandard} | awk '{ print $1 }' >> ${cmpRPMsha256} sha256sum ${rpmrpmonly} | awk '{ print $1 }' >> ${cmpRPMsha256} if [ $lzip = true ]; then sha256sum ${newrpm2} | awk '{ print $1 }' >> ${refRPMsha256} sha256sum ${rpmlzip} | awk '{ print $1 }' >> ${cmpRPMsha256} fi ret=0 diff ${refDRPMsha256} ${cmpDRPMsha256} if ! [ $? ]; then ret=$? fi diff ${refseq} ${cmpseq} if [ $ret ] && ! [ $? ]; then ret=$? fi diff ${refRPMsha256} ${cmpRPMsha256} if [ $ret ] && ! [ $? ]; then ret=$? fi rm -f ${refDRPMsha256} ${cmpDRPMsha256} ${refRPMsha256} ${cmpRPMsha256} \ ${refseq} ${cmpseq} ${deltas[*]} ${deltatmp} exit $ret drpm-0.5.1/test/lzma.supp000066400000000000000000000001141421165313000153020ustar00rootroot00000000000000{ Memcheck:Cond ... obj:/usr/*lib*/liblzma.so.* ... } drpm-0.5.1/test/refdrpm-copy.sha256000066400000000000000000000005051421165313000167730ustar00rootroot00000000000000fb05ae81b23289ceaab0fdf3f4e9a7ba56a7dd0d80524c3a40ebe3850aa7befe 5718917020d49bce5acd5cc1b09eb38d847d0cd70bfa85e163e9155c2ca21ca4 03994fd740ce85e3c2eb6a1eb02c3a70deae2eecd39bcb344072aee757b907f7 b28ef1bc40807f525b08d7bf4aaede67de3a1df80ce5e63f42acd368c45bc904 079910bb32a6591d24fc66b569cb28f3d6040ad36b8639688c4ddf327d4199a5 drpm-0.5.1/test/refseqfile-copy.txt000066400000000000000000000000731421165313000172700ustar00rootroot00000000000000drpm-devel-0.1.3-1.fc20-cf0e96ee2e35f2906b64a39a9356bc5730