From 3ccc42f0c0f51fa5c3246b1a1e9aca6ff8b155b5 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Sat, 23 Feb 2008 10:01:07 +0000 Subject: [PATCH] Effective base of libisoburn+xorriso 0.1.1 2008.02.22.124732 --- libisofs/branches/thomas/AUTHORS | 3 + libisofs/branches/thomas/COPYING | 280 ++ libisofs/branches/thomas/COPYRIGHT | 19 + libisofs/branches/thomas/ChangeLog | 1 + libisofs/branches/thomas/INSTALL | 234 ++ libisofs/branches/thomas/Makefile.am | 180 + libisofs/branches/thomas/NEWS | 20 + libisofs/branches/thomas/README | 341 ++ libisofs/branches/thomas/Roadmap | 33 + libisofs/branches/thomas/TODO | 35 + libisofs/branches/thomas/acinclude.m4 | 22 + libisofs/branches/thomas/bootstrap | 10 + libisofs/branches/thomas/configure.ac | 155 + libisofs/branches/thomas/demo/cat.c | 74 + libisofs/branches/thomas/demo/cat_buffer.c | 127 + libisofs/branches/thomas/demo/ecma119_tree.c | 136 + libisofs/branches/thomas/demo/iso.c | 176 + libisofs/branches/thomas/demo/iso_cat.c | 95 + libisofs/branches/thomas/demo/iso_grow.c | 257 ++ libisofs/branches/thomas/demo/iso_modify.c | 109 + libisofs/branches/thomas/demo/iso_ms.c | 114 + libisofs/branches/thomas/demo/iso_read.c | 167 + libisofs/branches/thomas/demo/lsl.c | 130 + libisofs/branches/thomas/demo/tree.c | 107 + libisofs/branches/thomas/doc/Tutorial | 506 +++ libisofs/branches/thomas/doc/Wiki | 32 + .../branches/thomas/doc/devel/1. Overview | 0 .../branches/thomas/doc/devel/2. Features | 193 + .../branches/thomas/doc/devel/3. Use Cases | 193 + libisofs/branches/thomas/doc/devel/4. Design | 0 .../thomas/doc/devel/5. Implementation | 0 libisofs/branches/thomas/doc/devel/README | 7 + .../thomas/doc/devel/UML/BuilderSec.png | Bin 0 -> 25202 bytes .../thomas/doc/devel/UML/BuilderSec.violet | 821 +++++ .../thomas/doc/devel/UML/builder.violet | 884 +++++ .../thomas/doc/devel/UML/builder.violet.png | Bin 0 -> 34098 bytes .../doc/devel/UML/burn_source.class.violet | 634 ++++ .../thomas/doc/devel/UML/burn_source.png | Bin 0 -> 35652 bytes .../thomas/doc/devel/UML/eltorito.violet | 552 +++ .../thomas/doc/devel/UML/eltorito.violet.png | Bin 0 -> 24861 bytes .../thomas/doc/devel/UML/iso_tree.violet | 748 ++++ .../thomas/doc/devel/UML/iso_tree.violet.png | Bin 0 -> 38189 bytes .../thomas/doc/devel/UML/nglibisofs.violet | 1059 ++++++ .../thomas/doc/devel/UML/stream.violet | 492 +++ .../thomas/doc/devel/UML/stream.violet.png | Bin 0 -> 21694 bytes .../branches/thomas/doc/devel/codestyle.xml | 91 + .../thomas/doc/devel/cookbook/ISO 9660-1999 | 119 + libisofs/branches/thomas/doc/doxygen.conf.in | 1298 +++++++ libisofs/branches/thomas/libisofs-1.pc.in | 11 + libisofs/branches/thomas/libisofs/buffer.c | 328 ++ libisofs/branches/thomas/libisofs/buffer.h | 95 + libisofs/branches/thomas/libisofs/builder.c | 208 ++ libisofs/branches/thomas/libisofs/builder.h | 81 + .../branches/thomas/libisofs/data_source.c | 195 + libisofs/branches/thomas/libisofs/ecma119.c | 1579 ++++++++ libisofs/branches/thomas/libisofs/ecma119.h | 476 +++ .../branches/thomas/libisofs/ecma119_tree.c | 846 +++++ .../branches/thomas/libisofs/ecma119_tree.h | 90 + libisofs/branches/thomas/libisofs/eltorito.c | 913 +++++ libisofs/branches/thomas/libisofs/eltorito.h | 103 + libisofs/branches/thomas/libisofs/filesrc.c | 393 ++ libisofs/branches/thomas/libisofs/filesrc.h | 84 + libisofs/branches/thomas/libisofs/fs_image.c | 2642 ++++++++++++++ libisofs/branches/thomas/libisofs/fs_local.c | 645 ++++ libisofs/branches/thomas/libisofs/fsource.c | 111 + libisofs/branches/thomas/libisofs/fsource.h | 32 + libisofs/branches/thomas/libisofs/image.c | 277 ++ libisofs/branches/thomas/libisofs/image.h | 112 + libisofs/branches/thomas/libisofs/iso1999.c | 1016 ++++++ libisofs/branches/thomas/libisofs/iso1999.h | 59 + libisofs/branches/thomas/libisofs/joliet.c | 1081 ++++++ libisofs/branches/thomas/libisofs/joliet.h | 56 + .../branches/thomas/libisofs/libiso_msgs.c | 439 +++ .../branches/thomas/libisofs/libiso_msgs.h | 682 ++++ libisofs/branches/thomas/libisofs/libisofs.h | 3165 +++++++++++++++++ libisofs/branches/thomas/libisofs/messages.c | 428 +++ libisofs/branches/thomas/libisofs/messages.h | 49 + libisofs/branches/thomas/libisofs/node.c | 884 +++++ libisofs/branches/thomas/libisofs/node.h | 306 ++ libisofs/branches/thomas/libisofs/rockridge.c | 1208 +++++++ libisofs/branches/thomas/libisofs/rockridge.h | 267 ++ .../branches/thomas/libisofs/rockridge_read.c | 419 +++ libisofs/branches/thomas/libisofs/stream.c | 434 +++ libisofs/branches/thomas/libisofs/stream.h | 145 + libisofs/branches/thomas/libisofs/tree.c | 751 ++++ libisofs/branches/thomas/libisofs/tree.h | 21 + libisofs/branches/thomas/libisofs/util.c | 1264 +++++++ libisofs/branches/thomas/libisofs/util.h | 438 +++ .../branches/thomas/libisofs/util_htable.c | 340 ++ .../branches/thomas/libisofs/util_rbtree.c | 296 ++ libisofs/branches/thomas/libisofs/writer.h | 43 + libisofs/branches/thomas/test/mocked_fsrc.c | 378 ++ libisofs/branches/thomas/test/mocked_fsrc.h | 31 + libisofs/branches/thomas/test/test.c | 26 + libisofs/branches/thomas/test/test.h | 14 + libisofs/branches/thomas/test/test_image.c | 354 ++ libisofs/branches/thomas/test/test_node.c | 690 ++++ .../branches/thomas/test/test_rockridge.c | 1395 ++++++++ libisofs/branches/thomas/test/test_stream.c | 155 + libisofs/branches/thomas/test/test_tree.c | 566 +++ libisofs/branches/thomas/test/test_util.c | 1072 ++++++ libisofs/branches/thomas/version.h.in | 3 + 102 files changed, 38150 insertions(+) create mode 100644 libisofs/branches/thomas/AUTHORS create mode 100644 libisofs/branches/thomas/COPYING create mode 100644 libisofs/branches/thomas/COPYRIGHT create mode 100644 libisofs/branches/thomas/ChangeLog create mode 100644 libisofs/branches/thomas/INSTALL create mode 100644 libisofs/branches/thomas/Makefile.am create mode 100644 libisofs/branches/thomas/NEWS create mode 100644 libisofs/branches/thomas/README create mode 100644 libisofs/branches/thomas/Roadmap create mode 100644 libisofs/branches/thomas/TODO create mode 100644 libisofs/branches/thomas/acinclude.m4 create mode 100755 libisofs/branches/thomas/bootstrap create mode 100644 libisofs/branches/thomas/configure.ac create mode 100644 libisofs/branches/thomas/demo/cat.c create mode 100644 libisofs/branches/thomas/demo/cat_buffer.c create mode 100644 libisofs/branches/thomas/demo/ecma119_tree.c create mode 100644 libisofs/branches/thomas/demo/iso.c create mode 100644 libisofs/branches/thomas/demo/iso_cat.c create mode 100644 libisofs/branches/thomas/demo/iso_grow.c create mode 100644 libisofs/branches/thomas/demo/iso_modify.c create mode 100644 libisofs/branches/thomas/demo/iso_ms.c create mode 100644 libisofs/branches/thomas/demo/iso_read.c create mode 100644 libisofs/branches/thomas/demo/lsl.c create mode 100644 libisofs/branches/thomas/demo/tree.c create mode 100755 libisofs/branches/thomas/doc/Tutorial create mode 100755 libisofs/branches/thomas/doc/Wiki create mode 100644 libisofs/branches/thomas/doc/devel/1. Overview create mode 100644 libisofs/branches/thomas/doc/devel/2. Features create mode 100644 libisofs/branches/thomas/doc/devel/3. Use Cases create mode 100644 libisofs/branches/thomas/doc/devel/4. Design create mode 100644 libisofs/branches/thomas/doc/devel/5. Implementation create mode 100644 libisofs/branches/thomas/doc/devel/README create mode 100644 libisofs/branches/thomas/doc/devel/UML/BuilderSec.png create mode 100644 libisofs/branches/thomas/doc/devel/UML/BuilderSec.violet create mode 100644 libisofs/branches/thomas/doc/devel/UML/builder.violet create mode 100644 libisofs/branches/thomas/doc/devel/UML/builder.violet.png create mode 100644 libisofs/branches/thomas/doc/devel/UML/burn_source.class.violet create mode 100644 libisofs/branches/thomas/doc/devel/UML/burn_source.png create mode 100644 libisofs/branches/thomas/doc/devel/UML/eltorito.violet create mode 100644 libisofs/branches/thomas/doc/devel/UML/eltorito.violet.png create mode 100644 libisofs/branches/thomas/doc/devel/UML/iso_tree.violet create mode 100644 libisofs/branches/thomas/doc/devel/UML/iso_tree.violet.png create mode 100644 libisofs/branches/thomas/doc/devel/UML/nglibisofs.violet create mode 100644 libisofs/branches/thomas/doc/devel/UML/stream.violet create mode 100644 libisofs/branches/thomas/doc/devel/UML/stream.violet.png create mode 100644 libisofs/branches/thomas/doc/devel/codestyle.xml create mode 100644 libisofs/branches/thomas/doc/devel/cookbook/ISO 9660-1999 create mode 100644 libisofs/branches/thomas/doc/doxygen.conf.in create mode 100644 libisofs/branches/thomas/libisofs-1.pc.in create mode 100644 libisofs/branches/thomas/libisofs/buffer.c create mode 100644 libisofs/branches/thomas/libisofs/buffer.h create mode 100644 libisofs/branches/thomas/libisofs/builder.c create mode 100644 libisofs/branches/thomas/libisofs/builder.h create mode 100644 libisofs/branches/thomas/libisofs/data_source.c create mode 100644 libisofs/branches/thomas/libisofs/ecma119.c create mode 100644 libisofs/branches/thomas/libisofs/ecma119.h create mode 100644 libisofs/branches/thomas/libisofs/ecma119_tree.c create mode 100644 libisofs/branches/thomas/libisofs/ecma119_tree.h create mode 100644 libisofs/branches/thomas/libisofs/eltorito.c create mode 100644 libisofs/branches/thomas/libisofs/eltorito.h create mode 100644 libisofs/branches/thomas/libisofs/filesrc.c create mode 100644 libisofs/branches/thomas/libisofs/filesrc.h create mode 100644 libisofs/branches/thomas/libisofs/fs_image.c create mode 100644 libisofs/branches/thomas/libisofs/fs_local.c create mode 100644 libisofs/branches/thomas/libisofs/fsource.c create mode 100644 libisofs/branches/thomas/libisofs/fsource.h create mode 100644 libisofs/branches/thomas/libisofs/image.c create mode 100644 libisofs/branches/thomas/libisofs/image.h create mode 100644 libisofs/branches/thomas/libisofs/iso1999.c create mode 100644 libisofs/branches/thomas/libisofs/iso1999.h create mode 100644 libisofs/branches/thomas/libisofs/joliet.c create mode 100644 libisofs/branches/thomas/libisofs/joliet.h create mode 100644 libisofs/branches/thomas/libisofs/libiso_msgs.c create mode 100644 libisofs/branches/thomas/libisofs/libiso_msgs.h create mode 100644 libisofs/branches/thomas/libisofs/libisofs.h create mode 100644 libisofs/branches/thomas/libisofs/messages.c create mode 100644 libisofs/branches/thomas/libisofs/messages.h create mode 100644 libisofs/branches/thomas/libisofs/node.c create mode 100644 libisofs/branches/thomas/libisofs/node.h create mode 100644 libisofs/branches/thomas/libisofs/rockridge.c create mode 100644 libisofs/branches/thomas/libisofs/rockridge.h create mode 100644 libisofs/branches/thomas/libisofs/rockridge_read.c create mode 100644 libisofs/branches/thomas/libisofs/stream.c create mode 100644 libisofs/branches/thomas/libisofs/stream.h create mode 100644 libisofs/branches/thomas/libisofs/tree.c create mode 100644 libisofs/branches/thomas/libisofs/tree.h create mode 100644 libisofs/branches/thomas/libisofs/util.c create mode 100644 libisofs/branches/thomas/libisofs/util.h create mode 100644 libisofs/branches/thomas/libisofs/util_htable.c create mode 100644 libisofs/branches/thomas/libisofs/util_rbtree.c create mode 100644 libisofs/branches/thomas/libisofs/writer.h create mode 100644 libisofs/branches/thomas/test/mocked_fsrc.c create mode 100644 libisofs/branches/thomas/test/mocked_fsrc.h create mode 100644 libisofs/branches/thomas/test/test.c create mode 100644 libisofs/branches/thomas/test/test.h create mode 100644 libisofs/branches/thomas/test/test_image.c create mode 100644 libisofs/branches/thomas/test/test_node.c create mode 100644 libisofs/branches/thomas/test/test_rockridge.c create mode 100644 libisofs/branches/thomas/test/test_stream.c create mode 100644 libisofs/branches/thomas/test/test_tree.c create mode 100644 libisofs/branches/thomas/test/test_util.c create mode 100644 libisofs/branches/thomas/version.h.in diff --git a/libisofs/branches/thomas/AUTHORS b/libisofs/branches/thomas/AUTHORS new file mode 100644 index 00000000..4b186770 --- /dev/null +++ b/libisofs/branches/thomas/AUTHORS @@ -0,0 +1,3 @@ +Vreixo Formoso +Mario Danic +Thomas Schmitt diff --git a/libisofs/branches/thomas/COPYING b/libisofs/branches/thomas/COPYING new file mode 100644 index 00000000..5a965fbc --- /dev/null +++ b/libisofs/branches/thomas/COPYING @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/libisofs/branches/thomas/COPYRIGHT b/libisofs/branches/thomas/COPYRIGHT new file mode 100644 index 00000000..8d562a3d --- /dev/null +++ b/libisofs/branches/thomas/COPYRIGHT @@ -0,0 +1,19 @@ +Vreixo Formoso , +Mario Danic , +Thomas Schmitt +Copyright (C) 2007-2008 Vreixo Formoso, Mario Danic, Thomas Schmitt + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/libisofs/branches/thomas/ChangeLog b/libisofs/branches/thomas/ChangeLog new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/libisofs/branches/thomas/ChangeLog @@ -0,0 +1 @@ + diff --git a/libisofs/branches/thomas/INSTALL b/libisofs/branches/thomas/INSTALL new file mode 100644 index 00000000..5458714e --- /dev/null +++ b/libisofs/branches/thomas/INSTALL @@ -0,0 +1,234 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/libisofs/branches/thomas/Makefile.am b/libisofs/branches/thomas/Makefile.am new file mode 100644 index 00000000..6492a714 --- /dev/null +++ b/libisofs/branches/thomas/Makefile.am @@ -0,0 +1,180 @@ +pkgconfigdir=$(libdir)/pkgconfig +libincludedir=$(includedir)/libisofs + +lib_LTLIBRARIES = libisofs/libisofs.la + +## ========================================================================= ## + +# Build libraries + +libisofs_libisofs_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) +libisofs_libisofs_la_SOURCES = \ + libisofs/builder.h \ + libisofs/builder.c \ + libisofs/node.h \ + libisofs/node.c \ + libisofs/tree.h \ + libisofs/tree.c \ + libisofs/image.h \ + libisofs/image.c \ + libisofs/fsource.h \ + libisofs/fsource.c \ + libisofs/fs_local.c \ + libisofs/fs_image.c \ + libisofs/messages.h \ + libisofs/messages.c \ + libisofs/libiso_msgs.h \ + libisofs/libiso_msgs.c \ + libisofs/stream.h \ + libisofs/stream.c \ + libisofs/util.h \ + libisofs/util.c \ + libisofs/util_rbtree.c \ + libisofs/util_htable.c \ + libisofs/filesrc.h \ + libisofs/filesrc.c \ + libisofs/ecma119.h \ + libisofs/ecma119.c \ + libisofs/ecma119_tree.h \ + libisofs/ecma119_tree.c \ + libisofs/writer.h \ + libisofs/buffer.h \ + libisofs/buffer.c \ + libisofs/rockridge.h \ + libisofs/rockridge.c \ + libisofs/rockridge_read.c \ + libisofs/joliet.h \ + libisofs/joliet.c \ + libisofs/eltorito.h \ + libisofs/eltorito.c \ + libisofs/iso1999.h \ + libisofs/iso1999.c \ + libisofs/data_source.c +libinclude_HEADERS = \ + libisofs/libisofs.h + +## ========================================================================= ## + +## Build demo applications +noinst_PROGRAMS = \ + demo/lsl \ + demo/cat \ + demo/catbuffer \ + demo/tree \ + demo/ecma119tree \ + demo/iso \ + demo/isoread \ + demo/isocat \ + demo/isomodify \ + demo/isoms \ + demo/isogrow + +demo_lsl_CPPFLAGS = -Ilibisofs +demo_lsl_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_lsl_SOURCES = demo/lsl.c + +demo_cat_CPPFLAGS = -Ilibisofs +demo_cat_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_cat_SOURCES = demo/cat.c + +demo_catbuffer_CPPFLAGS = -Ilibisofs +demo_catbuffer_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_catbuffer_SOURCES = demo/cat_buffer.c + +demo_tree_CPPFLAGS = -Ilibisofs +demo_tree_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_tree_SOURCES = demo/tree.c + +demo_ecma119tree_CPPFLAGS = -Ilibisofs +demo_ecma119tree_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_ecma119tree_SOURCES = demo/ecma119_tree.c + +demo_iso_CPPFLAGS = -Ilibisofs +demo_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_iso_SOURCES = demo/iso.c + +demo_isoread_CPPFLAGS = -Ilibisofs +demo_isoread_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_isoread_SOURCES = demo/iso_read.c + +demo_isocat_CPPFLAGS = -Ilibisofs +demo_isocat_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_isocat_SOURCES = demo/iso_cat.c + +demo_isomodify_CPPFLAGS = -Ilibisofs +demo_isomodify_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_isomodify_SOURCES = demo/iso_modify.c + +demo_isoms_CPPFLAGS = -Ilibisofs +demo_isoms_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_isoms_SOURCES = demo/iso_ms.c + +demo_isogrow_CPPFLAGS = -Ilibisofs -Ilibburn +demo_isogrow_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lburn +demo_isogrow_SOURCES = demo/iso_grow.c + + +## Build unit test + +check_PROGRAMS = \ + test/test + +test_test_CPPFLAGS = -Ilibisofs +test_test_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lcunit +test_test_LDFLAGS = -L.. -lm + +test_test_SOURCES = \ + test/test.h \ + test/test.c \ + test/test_node.c \ + test/test_image.c \ + test/test_tree.c \ + test/test_util.c \ + test/test_rockridge.c \ + test/test_stream.c \ + test/mocked_fsrc.h \ + test/mocked_fsrc.c + +## ========================================================================= ## + +## Build documentation (You need Doxygen for this to work) + +docdir = $(DESTDIR)$(prefix)/share/doc/$(PACKAGE)-$(VERSION) + +doc: doc/html + +doc/html: doc/doxygen.conf + $(RM) -r doc/html; \ + doxygen doc/doxygen.conf; + +install-data-local: + if [ -d doc/html ]; then \ + $(mkinstalldirs) $(docdir)/html; \ + $(INSTALL_DATA) doc/html/* $(docdir)/html; \ + fi + +uninstall-local: + rm -rf $(docdir) + +## ========================================================================= ## + +# Extra things +nodist_pkgconfig_DATA = \ + libisofs-1.pc + +EXTRA_DIST = \ + libisofs-1.pc.in \ + version.h.in \ + doc/doxygen.conf.in \ + doc/Tutorial \ + README \ + AUTHORS \ + COPYRIGHT \ + COPYING \ + NEWS \ + INSTALL \ + TODO \ + ChangeLog \ + Roadmap + diff --git a/libisofs/branches/thomas/NEWS b/libisofs/branches/thomas/NEWS new file mode 100644 index 00000000..8acff7bc --- /dev/null +++ b/libisofs/branches/thomas/NEWS @@ -0,0 +1,20 @@ +== unknown == + +Libisofs v0.6.4 +=============== + +- + +== Fri Feb 22 2008 == + +Libisofs v0.6.2.1 +================= + +- FIX: missing buffer.h in tarball + +== Thu Feb 14 2008 == + +Libisofs v0.6.2 +================ + +- Initial release diff --git a/libisofs/branches/thomas/README b/libisofs/branches/thomas/README new file mode 100644 index 00000000..1ead917f --- /dev/null +++ b/libisofs/branches/thomas/README @@ -0,0 +1,341 @@ +------------------------------------------------------------------------------ + libisofs +------------------------------------------------------------------------------ + +Released under GPL (see COPYING file for details). + +Copyright (C) 2008 Vreixo Formoso, Mario Danic, Thomas Schmitt + +libisofs is part of the libburnia project (libburnia-project.org) +------------------------------------------------------------------------------ + +libisofs is a library to create an ISO-9660 filesystem, and supports extensions +like RockRidge or Joliet. It is also a full featured ISO-9660 editor, allowing +you to modify an ISO image or multisession disc, including file addition and +removal, change of file names and attributes, etc + +Features: +--------- + +- Image creation + - Creates ISO-9660 images from local files. + - Support for RockRidge and Joliet extensions. + - Support for ISO-9660:1999 (version 2) + - Support for El-Torito bootable images. + - Full featured edition of file names and attributes on the image. + - Several options to relax ISO-9660 constraints. + - Special options for images intended for distribution (suitable default + modes for files, hiding of real timestamps...) +- Multisession + - Support for growing an existing image + - Full-featured edition of the image files, including: addition of new + files, removing of existent files, moving files, renaming files, + change file attributes (permissions, timestamps...) + - Support for "emulated multisession" or image growing, suitable for non + multisession media such as DVD+RW +- Image modification + - It can create a completely new image from files on another image. + - Full-featured edition of image contents +- Others + - Handling of different input and output charset + - Good integration with libburn for image burning. + - Reliable, good handling of different kind of errors. + +Requirements: +------------- + +- libburn 0.4.2 headers must be installed at compile time. It is not required + at runtime. + +Know bugs: +---------- + +Multisession and image growing can lead to undesired results in several cases: + +a) Images with unsupported features, such as: + - UDF. + - HSF/HFS+ or other Mac extensions. + - El-Torito with multiple entries. + - ECMA-119 with extended attributes, multiple extends per file. + - Non El-Torito boot info. + - zisofs compressed images. + - ... + In all these cases, the resulting new image (or new session) could lack some + features of the original image. + In some cases libisofs will issue warning messages, or even refuse to grow + or modify the image. Others remain undetected. Images created with libisofs + do not have this problems. + +b) Bootable El-Torito images may have several problems, that result in a new + image that is not bootable, or that boots from an outdated session. In many + cases it is recommended to add boot info again in the new session. + + - isolinux images won't be bootable after a modify. This is because + isolinux images need to have hardcoded the root dir lba. libisofs cannot + know whether an image is an isolinux image or not, so the user is + responsible to tell libisofs that it must patch the image, with the + el_torito_patch_isolinux_image() function. This problem could also exists + on other boot images. + - Most boot images are highly dependent of the image contents, so if the + user moves or removes some files on image it is possible they won't boot + anymore. + - There is no safer way to modify hidden boot images, as the size of the + boot image can't be figured out. + +c) Generated images could have different ECMA-119 low level names, due to + different way to mangle names, to new files added that force old files to + be renamed, to different relaxed contraints... This only affect the + ISO-9660 info, not the RR names, so it shouldn't be a problem in most + cases. If your app. relies on low level ISO-9660 names, you will need to + ensure all node names are valid ISO names (maybe together with some + relaxed contraints), otherwise libisofs might arbitrarily change the names. + + +------------------------------------------------------------------------------ + + Download, Build and Installation + +libisofs code is mantained in a Bazaar repository at Launchpad +(https://launchpad.net/libisofs/). You can download it with: + +$ bzr branch lp:libisofs + +Our build system is based on autotools. For preparing the build you will need +autotools of at least version 1.7. If you have download the code from the +repository, first of all you need to execute + + ./autogen.sh + +on toplevel dir to execute autotools. + +Alternatively you may unpack a release tarball for which you do not need +autotools installed. + +To build libisofs it should be sufficient to go into its toplevel directory +and execute + + ./configure --prefix=/usr + make + +To make the libraries accessible for running resp. developing applications + make install + +See INSTALL file for further details. + + +------------------------------------------------------------------------------ + + Overview of libburnia-project.org + +libburnia-project.org is an open-source software project for reading, mastering +and writing optical discs. +For now this means only CD media and all single layer DVD media except DVD+R. + +The project comprises of several more or less interdependent parts which +together strive to be a usable foundation for application development. +These are libraries, language bindings, and middleware binaries which emulate +classical (and valuable) Linux tools. + +Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems +we would need : login on a development machine resp. a live OS on CD or DVD, +advise from a system person about the equivalent of Linux sg or FreeBSD CAM, +volunteers for testing of realistic use cases. + +We have a workable code base for burning CD and most single layer DVD. +The burn API is quite comprehensively documented and can be used to build a +presentable application. +We have a functional binary which emulates parts of cdrecord in order to +prove that usability, and in order to allow you to explore libburnia's scope +by help of existing cdrecord frontends. + +The project components (list subject to growth, hopefully): + +- libburn is the library by which preformatted data get onto optical media. + It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or + /dev/hdX (e.g. on kernel 2.6). + libburn is the foundation of our cdrecord emulation. Its code is + independent of cdrecord. Its DVD capabilities are learned from + studying the code of dvd+rw-tools and MMC-5 specs. No code but only + the pure SCSI knowledge has been taken from dvd+rw-tools, though. + +- libisofs is the library to pack up hard disk files and directories into a + ISO 9660 disk image. This may then be brought to media via libburn. + libisofs is to be the foundation of our upcoming mkisofs emulation. + +- cdrskin is a limited cdrecord compatibility wrapper for libburn. + Cdrecord is a powerful GPL'ed burn program included in Joerg + Schilling's cdrtools. cdrskin strives to be a second source for + the services traditionally provided by cdrecord. Additionally it + provides libburn's DVD capabilities, where only -sao is compatible + with cdrecord. + cdrskin does not contain any bytes copied from cdrecord's sources. + Many bytes have been copied from the message output of cdrecord + runs, though. + See cdrskin/README and man cdrskin/cdrskin.1 for more. + +- test is a collection of application gestures and examples given by the + authors of the library features. The main API example for libburn + is test/libburner.c . + Explore these examples if you look for inspiration. + +We plan to be a responsive upstream. Bear with us. We are still practicing. + + +------------------------------------------------------------------------------ +Project history as far as known to me: + +- Founded in 2002 as it seems. See mailing list archives + http://lists.freedesktop.org/archives/libburn/ + The site of this founder team is reachable and offers download of a + (somewhat outdated) tarball and from CVS : + http://icculus.org/burn/ + Copyright holders and most probably founders: + Derek Foreman and Ben Jansens. + +- I came to using libburn in 2005. Founded the cdrskin project and submitted + necessary patches which were accepted or implemented better. Except one + remaining patch which prevented cdrskin from using vanilla libburn from CVS. + The cdrskin project site is reachable and offers download of the heavily + patched (elsewise outdated) tarball under the name cdrskin-0.1.2 : + http://scdbackup.sourceforge.net/cdrskin_eng.html + It has meanwhile moved to use vanilla libburn.pykix.org , though. + Version 0.1.4 constitutes the first release of this kind. + +- In July 2006 our team mate Mario Danic announced a revival of libburn + which by about nearly everybody else was perceived as unfriendly fork. + Derek Foreman four days later posted a message which expressed his + discontent. + The situation first caused me to publically regret it and then - after i + got the opportunity to move in with cdrskin - gave me true reason to + personally apologize to Derek Foreman, Ben Jansens and the contibutors at + icculus.org/burn. Posted to both projects: + http://lists.freedesktop.org/archives/libburn/2006-August/000446.html + http://mailman-mail1.webfaction.com/pipermail/libburn-hackers/2006-August/000024.html + +- Mid August 2006 project cdrskin established a branch office in + libburn.pykix.org so that all maintainers of our tools have one single place + to get the current (at least slightely) usable coordinated versions of + everything. + Project cdrskin will live forth independendly for a while but it is committed + to stay in sync with libburn.pykix.org (or some successor, if ever). + cdrskin is also committed to support icculus.org/burn if the pending fork + is made reality by content changes in that project. It will cease to maintain + a patched version of icculus.org/burn though. Precondition for a new + release of cdrskin on base of icculus.org/burn would be the pending + "whitelist patch" therefore. + I would rather prefer if both projects find consense and merge, or at least + cooperate. I have not given up hope totally, yet. + I, personally, will honor any approach. + +- 2nd September 2006 the decision is made to strive for a consolidation of + copyright and a commitment to GPL in a reasonable and open minded way. + This is to avoid long term problems with code of unknown origin and + with finding consense among the not so clearly defined group of copyright + claimers and -holders. + libisofs is already claimed sole copyright Mario Danic. + cdrskin and libburner are already claimed sole copyright Thomas Schmitt. + Rewrites of other components will follow and concluded by claiming full + copyright within the group of libburn.pykix.org-copyright holders. + +- 16th September 2006 feature freeze for release of libburn-0.2.2 . + +- 20th September 2006 release of libburn-0.2.2 . + +- 26th October 2006 feature freeze for cdrskin-0.2.4 based on libburn-0.2.3 . + This version of cdrskin is much more cdrecord compatible in repect + to drive addressing and audio features. + +- 30th October 2006 release of cdrskin-0.2.4 . + +- 13th November 2006 splitting releases of libburn+cdrskin from libisofs. + +- 24th November 2006 release of libburn-0.2.6 and cdrskin-0.2.6 . cdrskin has + become suitable for unaware frontends as long as they perform only the core + of cdrecord use cases (including open-ended input streams, audio, and + multi-session). + +- 28th November 2006 the umbrella project which encloses both, libisofs and + libburn, is now called libburnia. For the origin of this name, see + http://en.wikipedia.org/wiki/Liburnians . + +- 16th January 2007 release of libburn-0.3.0 and cdrskin-0.3.0 . Now the scope + is widened to a first class of DVD media: overwriteable single layer types + DVD-RAM, DVD+RW, DVD-RW. This is not a cdrecord emulation but rather inspired + by dvd+rw-tools' "poor man" writing facility for this class of media. + Taking a bow towards Andy Polyakov. + +- 11th February 2007 version 0.3.2 covers sequential DVD-RW and DVD-R with + multi-session and with DAO. + +- 12th March 2007 version 0.3.4 supports DVD+R and thus covers all single layer + DVD media. Code for double layer DVD+/-R is implemented but awaits a tester + yet. + +- 23th April 2007 version 0.3.6 follows the unanimous opinion of Linux kernel + people that one should not use /dev/sg on kernel 2.6. + +- 31st July 2007 version 0.3.8 marks the first anniversary of libburn revival. + We look back on improved stability, a substantially extended list of media + and write modes, and better protection against typical user mishaps. + +- 24th October 2007 version 0.4.0 is the foundation of new library libisoburn + and an upcomming integrated application for manipulating and writing + ISO 9660 + Rock Ridge images. cdrskin-0.4.0 got capabilities like growisofs + by these enhancements: growing of overwriteable media and disk files. + Taking again a bow towards Andy Polyakov. + +- 26th Januar 2008 version 0.4.2 rectifies the version numbering so that we + reliably release libburn.so.4 as should have been done since libburn-0.3.2. + cdrskin now is by default linked dynamically and does a runtime check + to ensure not to be started with a libburn which is older than itself. + + +------------------------------------------------------------------------------ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation. To be exact: version 2 of that License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +------------------------------------------------------------------------------ +Clarification in my name and in the name of Mario Danic, upcoming copyright +holders on toplevel of libburnia. To be fully in effect after the remaining +other copyrighted code has been replaced by ours and by copyright-free +contributions of our friends: +------------------------------------------------------------------------------ + +We, the copyright holders, agree on the interpretation that +dynamical linking of our libraries constitutes "use of" and +not "derivation from" our work in the sense of GPL, provided +those libraries are compiled from our unaltered code. + +Thus you may link our libraries dynamically with applications +which are not under GPL. You may distribute our libraries and +application tools in binary form, if you fulfill the usual +condition of GPL to offer a copy of the source code -altered +or unaltered- under GPL. + +We ask you politely to use our work in open source spirit +and with the due reference to the entire open source community. + +If there should really arise the case where above clarification +does not suffice to fulfill a clear and neat request in open source +spirit that would otherwise be declined for mere formal reasons, +only in that case we will duely consider to issue a special license +covering only that special case. +It is the open source idea of responsible freedom which will be +decisive and you will have to prove that you exhausted all own +means to qualify for GPL. + +For now we are firmly committed to maintain one single license: GPL. + +signed: Mario Danic, Thomas Schmitt + diff --git a/libisofs/branches/thomas/Roadmap b/libisofs/branches/thomas/Roadmap new file mode 100644 index 00000000..926ecef6 --- /dev/null +++ b/libisofs/branches/thomas/Roadmap @@ -0,0 +1,33 @@ + +>>>>>>>>>> RELEASE 0.6.1 (development) >>>>>>>>>>>>>>>>>>>>> + +- Review error severities +OK - Prepare API for stability and compatibility check +- Documentation + +>>>>>>>>>> RELEASE 0.6.2 (stable) >>>>>>>>>>>>>>>>>>>>>>>>>> + +- Intensive testing and bug fixing + +>>>>>>>>>> RELEASE 0.6.3 (development) >>>>>>>>>>>>>>>>>>>>> + +- Improves to public tree + -> Expose node extended info. Always compile it. + (little memory cost) + -> Review builder / tree / node relation + -> Optimize storage of children in node? + -> Inode object? +- Expose Builder and Streams +- Implement filters: compression, encryption... +- Consider some kind of plugin system for Builders, Filesystems and Filters. +- ECMA-119, Joliet, and ISO-9660:1999 writers can share most of the code. + Create a new writer as a generalization of these. +- Update Java bindings + +>>>>>>>>>>> ...... + +>>>>>>>>>>> RELEASE 1.0.0 (stable) >>>>>>>>>>>>>>>>>>>>>>>>>> + +- UDF +- HFS + diff --git a/libisofs/branches/thomas/TODO b/libisofs/branches/thomas/TODO new file mode 100644 index 00000000..bad313fc --- /dev/null +++ b/libisofs/branches/thomas/TODO @@ -0,0 +1,35 @@ +FEATURES +======== + + +TODO +==== + +#00001 (node.h) -> consider adding new timestamps to IsoTreeNode +#00004 (libisofs.h) -> Add a get_mime_type() function. +#00005 (node.c) -> optimize iso_dir_iter_take. +#00006 (libisofs.h) -> define more replace values when adding a node to a dir +#00007 (libisofs.h) -> expose iso_tree_add_new_file +#00008 (data_dource.c) -> guard against partial reads +#00009 (ecma119_tree.c/h) -> add true support for harlinks and inode numbers +#00010 (buffer.c) -> optimize ring buffer +#00011 (ecma119.c) -> guard against bad path table usage with more than 65535 dirs +#00012 (fs_image.c) -> support follow symlinks on imafe filesystem +#00013 (fs_image.c) -> check for unsupported flags when reading a dir record +#00014 (fs_image.c) -> more sanity checks to ensure dir record info is valid +#00015 (fs_image.c) -> take care of CD-ROM XA discs when reading SP entry +#00016 (fs_image.c) -> handle non RR ER entries +#00017 (fs_image.c) -> take advantage of other atts of PVD +#00018 (fs_image.c) -> check if there are more entries in the boot catalog +#00019 (fs_image.c) -> set IsoImage attribs from Joliet SVD? +#00020 (fs_image.c) -> handle RR info in Joliet tree +#00021 (fs_image.c) -> handle RR info in ISO 9660:1999 tree +#00022 (joliet.c) -> support relaxed constraints in joliet filenames +#00024 (libisofs.h) -> option to convert names to lower case for iso reading +#00025 (libisofs.h) -> support for merging old image files +#00026 (libisofs.h) -> add support for "hidden" bootable images. +#00027 (iso1999.h) -> Follow ISO 9660:1999 specs when sorting files + +FIXME +===== + diff --git a/libisofs/branches/thomas/acinclude.m4 b/libisofs/branches/thomas/acinclude.m4 new file mode 100644 index 00000000..861847bb --- /dev/null +++ b/libisofs/branches/thomas/acinclude.m4 @@ -0,0 +1,22 @@ +AC_DEFUN([TARGET_SHIZZLE], +[ + ARCH="" + + AC_MSG_CHECKING([target operating system]) + + case $target in + *-*-linux*) + ARCH=linux + LIBBURN_ARCH_LIBS= + ;; + *-*-freebsd*) + ARCH=freebsd + LIBBURN_ARCH_LIBS=-lcam + ;; + *) + AC_ERROR([You are attempting to compile for an unsupported platform]) + ;; + esac + + AC_MSG_RESULT([$ARCH]) +]) diff --git a/libisofs/branches/thomas/bootstrap b/libisofs/branches/thomas/bootstrap new file mode 100755 index 00000000..86709bfc --- /dev/null +++ b/libisofs/branches/thomas/bootstrap @@ -0,0 +1,10 @@ +#!/bin/sh -x + +aclocal +libtoolize --copy --force +autoconf + +# ts A61101 : libburn is not prepared for config.h +# autoheader + +automake --foreign --add-missing --copy --include-deps diff --git a/libisofs/branches/thomas/configure.ac b/libisofs/branches/thomas/configure.ac new file mode 100644 index 00000000..69b7dc5e --- /dev/null +++ b/libisofs/branches/thomas/configure.ac @@ -0,0 +1,155 @@ +AC_INIT([libisofs], [0.6.3], [http://libburnia-project.org]) +AC_PREREQ([2.50]) +dnl AC_CONFIG_HEADER([config.h]) + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([subdir-objects]) + +dnl A61101 This breaks Linux build (makes 32 bit off_t) +dnl http://sourceware.org/autobook/autobook/autobook_96.html says +dnl one must include some config.h and this was a pitfall. +dnl So why dig the pit at all ? +dnl AM_CONFIG_HEADER(config.h) + +dnl +dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match +dnl +dnl CURRENT and AGE describe the binary compatibility interval of a +dnl dynamic library. +dnl See also http://www.gnu.org/software/libtool/manual.html#Interfaces +dnl +dnl The name of the library will be libisofs.so.$CURRENT-$AGE.$AGE.$REV +dnl In the terminology of this file: +dnl CURRENT = LT_CURRENT +dnl REV = LT_REVISION +dnl AGE = LT_AGE +dnl +dnl LT_CURRENT, LT_REVISION and LT_AGE get set directly now. +dnl +dnl SONAME of the emerging library is LT_CURRENT - LT_AGE. +dnl The linker will do no finer checks. Especially no age range check for +dnl the cdrskin binary. If SONAME matches, then the couple starts. +dnl +dnl Therefore a run time check is provided by libisofs function +dnl iso_lib_version(). It returns the major, minor and micro revision of the +dnl library. This means LIBISOFS_*_VERSION kept its second job which does not +dnl comply to the usual ways of configure.ac . I.e. now *officially* this is +dnl the source code release version as announced to the public. It has no +dnl conection to SONAME or libtool version numbering. +dnl It rather feeds the API function iso_lib_version(). +dnl +dnl If LIBISOFS_*_VERSION changes, be sure to change AC_INIT above to match. +dnl +LIBISOFS_MAJOR_VERSION=0 +LIBISOFS_MINOR_VERSION=6 +LIBISOFS_MICRO_VERSION=3 +LIBISOFS_VERSION=$LIBISOFS_MAJOR_VERSION.$LIBISOFS_MINOR_VERSION.$LIBISOFS_MICRO_VERSION + +AC_SUBST(LIBISOFS_MAJOR_VERSION) +AC_SUBST(LIBISOFS_MINOR_VERSION) +AC_SUBST(LIBISOFS_MICRO_VERSION) +AC_SUBST(LIBISOFS_VERSION) + +dnl Libtool versioning +LT_RELEASE=$LIBISOFS_MAJOR_VERSION.$LIBISOFS_MINOR_VERSION +# SONAME = 6 - 0 = 6 . Library name = libisofs.6.0.0 +LT_CURRENT=6 +LT_REVISION=0 +LT_AGE=0 +LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE` + +AC_SUBST(LT_RELEASE) +AC_SUBST(LT_CURRENT) +AC_SUBST(LT_REVISION) +AC_SUBST(LT_AGE) +AC_SUBST(LT_CURRENT_MINUS_AGE) + +AC_PREFIX_DEFAULT([/usr/local]) +test "$prefix" = "NONE" && prefix=$ac_default_prefix + +AM_MAINTAINER_MODE + +AM_PROG_CC_C_O +AC_C_CONST +AC_C_INLINE +AC_C_BIGENDIAN + +dnl Large file support +AC_SYS_LARGEFILE +AC_FUNC_FSEEKO +AC_CHECK_FUNC([fseeko]) +if test ! $ac_cv_func_fseeko; then + AC_MSG_ERROR([Libisofs requires largefile support.]) +fi + +AC_PROG_LIBTOOL +AC_SUBST(LIBTOOL_DEPS) +LIBTOOL="$LIBTOOL --silent" + +AC_PROG_INSTALL + +AC_CHECK_HEADERS() + +dnl Use GNU extensions if available +AC_DEFINE(_GNU_SOURCE, 1) + +dnl Check for tm_gmtoff field in struct tm +AC_CHECK_MEMBER([struct tm.tm_gmtoff], + [AC_DEFINE(HAVE_TM_GMTOFF, 1, + [Define this if tm structure includes a tm_gmtoff entry.])], + , + [#include ]) + +dnl Check if non standard timegm() function is available +AC_CHECK_DECL([timegm], + [AC_DEFINE(HAVE_TIMEGM, 1, [Define this if timegm function is available])], + , + [#include ]) + +dnl Check if non standard eaccess() function is available +AC_CHECK_DECL([eaccess], + [AC_DEFINE(HAVE_EACCESS, 1, [Define this if eaccess function is available])], + , + [#include ]) + +THREAD_LIBS=-lpthread +AC_SUBST(THREAD_LIBS) + +TARGET_SHIZZLE +AC_SUBST(ARCH) +AC_SUBST(LIBBURN_ARCH_LIBS) + +dnl Add compiler-specific flags + +dnl See if the user wants aggressive optimizations of the code +AC_ARG_ENABLE(debug, +[ --enable-debug Disable aggressive optimizations [default=yes]], + , enable_debug=yes) +if test x$enable_debug != xyes; then + if test x$GCC = xyes; then + CFLAGS="$CFLAGS -O3" + CFLAGS="$CFLAGS -fexpensive-optimizations" + fi + CFLAGS="$CFLAGS -DNDEBUG" +else + if test x$GCC = xyes; then + CFLAGS="$CFLAGS -g -pedantic -Wall" + fi + CFLAGS="$CFLAGS -DDEBUG" +fi + +dnl Verbose debug to make libisofs issue more debug messages +AC_ARG_ENABLE(verbose-debug, +[ --enable-verbose-debug Enable verbose debug messages [default=no]], + AC_DEFINE(LIBISOFS_VERBOSE_DEBUG, 1)) + + +AC_CONFIG_FILES([ + Makefile + doc/doxygen.conf + version.h + libisofs-1.pc + ]) +AC_OUTPUT diff --git a/libisofs/branches/thomas/demo/cat.c b/libisofs/branches/thomas/demo/cat.c new file mode 100644 index 00000000..a95eebc3 --- /dev/null +++ b/libisofs/branches/thomas/demo/cat.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "libisofs.h" +#include "fsource.h" + +#include +#include + +/* + * Little test program to test filesystem implementations. + * Outputs file contents to stdout! + */ + +int main(int argc, char **argv) +{ + int res; + IsoFilesystem *fs; + IsoFileSource *file; + struct stat info; + + if (argc != 2) { + fprintf(stderr, "Usage: cat /path/to/file\n"); + return 1; + } + + /* create filesystem object */ + res = iso_local_filesystem_new(&fs); + if (res < 0) { + fprintf(stderr, "Can't get local fs object, err = %d\n", res); + return 1; + } + + res = fs->get_by_path(fs, argv[1], &file); + if (res < 0) { + fprintf(stderr, "Can't get file, err = %d\n", res); + return 1; + } + + res = iso_file_source_lstat(file, &info); + if (res < 0) { + fprintf(stderr, "Can't stat file, err = %d\n", res); + return 1; + } + + if (S_ISDIR(info.st_mode)) { + fprintf(stderr, "Path refers to a directory!!\n"); + return 1; + } else { + char buf[1024]; + res = iso_file_source_open(file); + if (res < 0) { + fprintf(stderr, "Can't open file, err = %d\n", res); + return 1; + } + while ((res = iso_file_source_read(file, buf, 1024)) > 0) { + fwrite(buf, 1, res, stdout); + } + if (res < 0) { + fprintf(stderr, "Error reading, err = %d\n", res); + return 1; + } + iso_file_source_close(file); + } + + iso_file_source_unref(file); + iso_filesystem_unref(fs); + return 0; +} diff --git a/libisofs/branches/thomas/demo/cat_buffer.c b/libisofs/branches/thomas/demo/cat_buffer.c new file mode 100644 index 00000000..74657ee2 --- /dev/null +++ b/libisofs/branches/thomas/demo/cat_buffer.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "libisofs.h" +#include "buffer.h" + +#include +#include +#include +#include +#include +#include +#include + +/* + * Little test program that reads a file and outputs it to stdout, using + * the libisofs ring buffer as intermediate memory + */ + +struct th_data +{ + IsoRingBuffer *rbuf; + char *path; +}; + +#define WRITE_CHUNK 2048 +#define READ_CHUNK 2048 + +static +void *write_function(void *arg) +{ + ssize_t bytes; + int res; + unsigned char tmp[WRITE_CHUNK]; + struct th_data *data = (struct th_data *) arg; + + int fd = open(data->path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Writer thread error: Can't open file"); + iso_ring_buffer_writer_close(data->rbuf, 1); + pthread_exit(NULL); + } + + res = 1; + while ( (bytes = read(fd, tmp, WRITE_CHUNK)) > 0) { + res = iso_ring_buffer_write(data->rbuf, tmp, bytes); + if (res <= 0) { + break; + } + /* To test premature reader exit >>>>>>>>>>> + iso_ring_buffer_writer_close(data->rbuf); + pthread_exit(NULL); + <<<<<<<<<<<<<<<<<<<<<<<<< */ + // if (rand() > 2000000000) { + // fprintf(stderr, "Writer sleeping\n"); + // sleep(1); + // } + } + fprintf(stderr, "Writer finish: %d\n", res); + + close(fd); + iso_ring_buffer_writer_close(data->rbuf, 0); + pthread_exit(NULL); +} + +static +void *read_function(void *arg) +{ + unsigned char tmp[READ_CHUNK]; + int res = 1; + struct th_data *data = (struct th_data *) arg; + + while ( (res = iso_ring_buffer_read(data->rbuf, tmp, READ_CHUNK)) > 0) { + write(1, tmp, READ_CHUNK); + /* To test premature reader exit >>>>>>>>>>> + iso_ring_buffer_reader_close(data->rbuf); + pthread_exit(NULL); + <<<<<<<<<<<<<<<<<<<<<<<<< */ + // if (rand() > 2000000000) { + // fprintf(stderr, "Reader sleeping\n"); + // sleep(1); + // } + } + fprintf(stderr, "Reader finish: %d\n", res); + + iso_ring_buffer_reader_close(data->rbuf, 0); + + pthread_exit(NULL); +} + +int main(int argc, char **argv) +{ + int res; + struct th_data data; + pthread_t reader; + pthread_t writer; + + if (argc != 2) { + fprintf(stderr, "Usage: catbuffer /path/to/file\n"); + return 1; + } + + res = iso_ring_buffer_new(1024, &data.rbuf); + if (res < 0) { + fprintf(stderr, "Can't create buffer\n"); + return 1; + } + data.path = argv[1]; + + res = pthread_create(&writer, NULL, write_function, (void *) &data); + res = pthread_create(&reader, NULL, read_function, (void *) &data); + + pthread_join(writer, NULL); + pthread_join(reader, NULL); + + fprintf(stderr, "Buffer was %d times full and %d times empty.\n", + iso_ring_buffer_get_times_full(data.rbuf), + iso_ring_buffer_get_times_empty(data.rbuf)); + + free(data.rbuf); + return 0; +} diff --git a/libisofs/branches/thomas/demo/ecma119_tree.c b/libisofs/branches/thomas/demo/ecma119_tree.c new file mode 100644 index 00000000..e1c4dbec --- /dev/null +++ b/libisofs/branches/thomas/demo/ecma119_tree.c @@ -0,0 +1,136 @@ +/* + * Little program that imports a directory to iso image, generates the + * ecma119 low level tree and prints it. + * Note that this is not an API example, but a little program for test + * purposes. + */ + +#include "libisofs.h" +#include "ecma119.h" +#include "ecma119_tree.h" +#include "util.h" +#include "filesrc.h" +#include "node.h" +#include +#include +#include +#include +#include +#include + +static void +print_permissions(mode_t mode) +{ + char perm[10]; + + //TODO suid, sticky... + + perm[9] = '\0'; + perm[8] = mode & S_IXOTH ? 'x' : '-'; + perm[7] = mode & S_IWOTH ? 'w' : '-'; + perm[6] = mode & S_IROTH ? 'r' : '-'; + perm[5] = mode & S_IXGRP ? 'x' : '-'; + perm[4] = mode & S_IWGRP ? 'w' : '-'; + perm[3] = mode & S_IRGRP ? 'r' : '-'; + perm[2] = mode & S_IXUSR ? 'x' : '-'; + perm[1] = mode & S_IWUSR ? 'w' : '-'; + perm[0] = mode & S_IRUSR ? 'r' : '-'; + printf("[%s]",perm); +} + +static void +print_dir(Ecma119Node *dir, int level) +{ + int i; + char *sp = alloca(level * 2 + 1); + + for (i = 0; i < level * 2; i += 2) { + sp[i] = '|'; + sp[i+1] = ' '; + } + + sp[level * 2-1] = '-'; + sp[level * 2] = '\0'; + + for (i = 0; i < dir->info.dir->nchildren; i++) { + Ecma119Node *child = dir->info.dir->children[i]; + + if (child->type == ECMA119_DIR) { + printf("%s+[D] ", sp); + print_permissions(iso_node_get_permissions(child->node)); + printf(" %s\n", child->iso_name); + print_dir(child, level+1); + } else if (child->type == ECMA119_FILE) { + printf("%s-[F] ", sp); + print_permissions(iso_node_get_permissions(child->node)); + printf(" %s {%p}\n", child->iso_name, (void*)child->info.file); + } else if (child->type == ECMA119_SYMLINK) { + printf("%s-[L] ", sp); + print_permissions(iso_node_get_permissions(child->node)); + printf(" %s -> %s\n", child->iso_name, + ((IsoSymlink*)child->node)->dest); + } else if (child->type == ECMA119_SPECIAL) { + printf("%s-[S] ", sp); + print_permissions(iso_node_get_permissions(child->node)); + printf(" %s\n", child->iso_name); + } else if (child->type == ECMA119_PLACEHOLDER) { + printf("%s-[RD] ", sp); + print_permissions(iso_node_get_permissions(child->node)); + printf(" %s\n", child->iso_name); + } else { + printf("%s-[????] ", sp); + } + } +} + +int main(int argc, char **argv) +{ + int result; + IsoImage *image; + Ecma119Image *ecma119; + + if (argc != 2) { + printf ("You need to specify a valid path\n"); + return 1; + } + + iso_init(); + iso_set_msgs_severities("NEVER", "ALL", ""); + result = iso_image_new("volume_id", &image); + if (result < 0) { + printf ("Error creating image\n"); + return 1; + } + + result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[1]); + if (result < 0) { + printf ("Error adding directory %d\n", result); + return 1; + } + + ecma119 = calloc(1, sizeof(Ecma119Image)); + iso_rbtree_new(iso_file_src_cmp, &(ecma119->files)); + ecma119->iso_level = 1; + ecma119->rockridge = 1; + ecma119->image = image; + ecma119->input_charset = strdup("UTF-8"); + + /* create low level tree */ + result = ecma119_tree_create(ecma119); + if (result < 0) { + printf ("Error creating ecma-119 tree: %d\n", result); + return 1; + } + + printf("================= ECMA-119 TREE =================\n"); + print_dir(ecma119->root, 0); + printf("\n\n"); + + ecma119_node_free(ecma119->root); + iso_rbtree_destroy(ecma119->files, iso_file_src_free); + free(ecma119->input_charset); + free(ecma119); + iso_image_unref(image); + iso_finish(); + return 0; +} diff --git a/libisofs/branches/thomas/demo/iso.c b/libisofs/branches/thomas/demo/iso.c new file mode 100644 index 00000000..f3783b08 --- /dev/null +++ b/libisofs/branches/thomas/demo/iso.c @@ -0,0 +1,176 @@ +/* + * Little program to show how to create an iso image from a local + * directory. + */ + +#include "libisofs.h" +#include "libburn/libburn.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char * const optstring = "JRIL:b:hV:"; +extern char *optarg; +extern int optind; + +void usage(char **argv) +{ + printf("%s [OPTIONS] DIRECTORY OUTPUT\n", argv[0]); +} + +void help() +{ + printf( + "Options:\n" + " -J Add Joliet support\n" + " -R Add Rock Ridge support\n" + " -I Add ISO 9660:1999 support\n" + " -V label Volume Label\n" + " -L Set the ISO level (1 or 2)\n" + " -b file Specifies a boot image to add to image\n" + " -h Print this message\n" + ); +} + +int callback(IsoFileSource *src) +{ + char *path = iso_file_source_get_path(src); + printf("CALLBACK: %s\n", path); + free(path); + return 1; +} + +int main(int argc, char **argv) +{ + int result; + int c; + IsoImage *image; + struct burn_source *burn_src; + unsigned char buf[2048]; + FILE *fd; + IsoWriteOpts *opts; + char *volid = "VOLID"; + char *boot_img = NULL; + int rr = 0, j = 0, iso1999 = 0, level = 1; + + while ((c = getopt(argc, argv, optstring)) != -1) { + switch(c) { + case 'h': + usage(argv); + help(); + exit(0); + break; + case 'J': + j = 1; + break; + case 'R': + rr = 1; + break; + case 'I': + iso1999 = 1; + break; + case 'L': + level = atoi(optarg); + break; + case 'b': + boot_img = optarg; + break; + case 'V': + volid = optarg; + break; + case '?': + usage(argv); + exit(1); + break; + } + } + + if (argc < 2) { + printf ("Please pass directory from which to build ISO\n"); + usage(argv); + return 1; + } + if (argc < 3) { + printf ("Please supply output file\n"); + usage(argv); + return 1; + } + + fd = fopen(argv[optind+1], "w"); + if (!fd) { + err(1, "error opening output file"); + } + + result = iso_init(); + if (result < 0) { + printf ("Can't initialize libisofs\n"); + return 1; + } + iso_set_msgs_severities("NEVER", "ALL", ""); + + result = iso_image_new(volid, &image); + if (result < 0) { + printf ("Error creating image\n"); + return 1; + } + iso_tree_set_follow_symlinks(image, 0); + iso_tree_set_ignore_hidden(image, 0); + iso_tree_set_ignore_special(image, 0); + iso_set_abort_severity("SORRY"); + /*iso_tree_set_report_callback(image, callback);*/ + + result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[optind]); + if (result < 0) { + printf ("Error adding directory %d\n", result); + return 1; + } + + if (boot_img) { + /* adds El-Torito boot info. Tunned for isolinux */ + ElToritoBootImage *bootimg; + result = iso_image_set_boot_image(image, boot_img, ELTORITO_NO_EMUL, + "/isolinux/boot.cat", &bootimg); + if (result < 0) { + printf ("Error adding boot image %d\n", result); + return 1; + } + el_torito_set_load_size(bootimg, 4); + el_torito_patch_isolinux_image(bootimg); + } + + result = iso_write_opts_new(&opts, 0); + if (result < 0) { + printf ("Cant create write opts, error %d\n", result); + return 1; + } + iso_write_opts_set_iso_level(opts, level); + iso_write_opts_set_rockridge(opts, rr); + iso_write_opts_set_joliet(opts, j); + iso_write_opts_set_iso1999(opts, iso1999); + + result = iso_image_create_burn_source(image, opts, &burn_src); + if (result < 0) { + printf ("Cant create image, error %d\n", result); + return 1; + } + + iso_write_opts_free(opts); + + while (burn_src->read_xt(burn_src, buf, 2048) == 2048) { + fwrite(buf, 1, 2048, fd); + } + fclose(fd); + burn_src->free_data(burn_src); + free(burn_src); + + iso_image_unref(image); + iso_finish(); + return 0; +} diff --git a/libisofs/branches/thomas/demo/iso_cat.c b/libisofs/branches/thomas/demo/iso_cat.c new file mode 100644 index 00000000..452d1b12 --- /dev/null +++ b/libisofs/branches/thomas/demo/iso_cat.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + + +#include +#include + +#include "libisofs.h" + +/* + * Little test program that extracts a file form a given ISO image. + * Outputs file contents to stdout! + */ + +int main(int argc, char **argv) +{ + int res; + IsoFilesystem *fs; + IsoFileSource *file; + struct stat info; + IsoDataSource *src; + IsoReadOpts *opts; + + if (argc != 3) { + fprintf(stderr, "Usage: isocat /path/to/image /path/to/file\n"); + return 1; + } + + res = iso_init(); + if (res < 0) { + fprintf(stderr, "Can't init libisofs\n"); + return 1; + } + + res = iso_data_source_new_from_file(argv[1], &src); + if (res < 0) { + fprintf(stderr, "Error creating data source\n"); + return 1; + } + + res = iso_read_opts_new(&opts, 0); + if (res < 0) { + fprintf(stderr, "Error creating read options\n"); + return 1; + } + res = iso_image_filesystem_new(src, opts, 1, &fs); + if (res < 0) { + fprintf(stderr, "Error creating filesystem\n"); + return 1; + } + iso_read_opts_free(opts); + + res = fs->get_by_path(fs, argv[2], &file); + if (res < 0) { + fprintf(stderr, "Can't get file, err = %d\n", res); + return 1; + } + + res = iso_file_source_lstat(file, &info); + if (res < 0) { + fprintf(stderr, "Can't stat file, err = %d\n", res); + return 1; + } + + if (S_ISDIR(info.st_mode)) { + fprintf(stderr, "Path refers to a directory!!\n"); + return 1; + } else { + char buf[1024]; + res = iso_file_source_open(file); + if (res < 0) { + fprintf(stderr, "Can't open file, err = %d\n", res); + return 1; + } + while ((res = iso_file_source_read(file, buf, 1024)) > 0) { + fwrite(buf, 1, res, stdout); + } + if (res < 0) { + fprintf(stderr, "Error reading, err = %d\n", res); + return 1; + } + iso_file_source_close(file); + } + + iso_file_source_unref(file); + iso_filesystem_unref(fs); + iso_data_source_unref(src); + iso_finish(); + return 0; +} diff --git a/libisofs/branches/thomas/demo/iso_grow.c b/libisofs/branches/thomas/demo/iso_grow.c new file mode 100644 index 00000000..951aec1b --- /dev/null +++ b/libisofs/branches/thomas/demo/iso_grow.c @@ -0,0 +1,257 @@ +/* + * Very simple program to show how to grow an iso image. + */ + +#include "libisofs.h" +#include "libburn/libburn.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static IsoDataSource *libburn_data_source_new(struct burn_drive *d); + +void usage(char **argv) +{ + printf("%s DISC DIRECTORY\n", argv[0]); +} + +int main(int argc, char **argv) +{ + int result; + IsoImage *image; + IsoDataSource *src; + struct burn_source *burn_src; + struct burn_drive_info *drives; + struct burn_drive *drive; + unsigned char buf[32 * 2048]; + IsoWriteOpts *opts; + int ret = 0; + IsoReadImageFeatures *features; + uint32_t ms_block; + IsoReadOpts *ropts; + + if (argc < 3) { + usage(argv); + return 1; + } + + iso_init(); + iso_set_msgs_severities("NEVER", "ALL", ""); + + /* create the image context */ + result = iso_image_new("volume_id", &image); + if (result < 0) { + printf ("Error creating image\n"); + return 1; + } + iso_tree_set_follow_symlinks(image, 0); + iso_tree_set_ignore_hidden(image, 0); + + if (!burn_initialize()) { + err(1, "Can't init libburn"); + } + burn_msgs_set_severities("NEVER", "SORRY", "libburner : "); + + if (burn_drive_scan_and_grab(&drives, argv[1], 0) != 1) { + err(1, "Can't open device. Are you sure it is a valid drive?\n"); + } + + drive = drives[0].drive; + +#ifdef ISO_GROW_CHECK_MEDIA + { + /* some check before going on */ + enum burn_disc_status state; + int pno; + char name[80]; + + state = burn_disc_get_status(drive); + burn_disc_get_profile(drive, &pno, name); + + /* + * my drives report BURN_DISC_BLANK on a DVD+RW with data. + * is that correct? + */ + if ( (pno != 0x1a) /*|| (state != BURN_DISC_FULL)*/ ) { + printf("You need to insert a DVD+RW with some data.\n"); + printf("Profile: %x, state: %d.\n", pno, state); + ret = 1; + goto exit_cleanup; + } + } +#endif + + /* create the data source to accesss previous image */ + src = libburn_data_source_new(drive); + if (src == NULL) { + printf("Can't create data source.\n"); + ret = 1; + goto exit_cleanup; + } + + /* import previous image */ + ret = iso_read_opts_new(&ropts, 0); + if (ret < 0) { + fprintf(stderr, "Error creating read options\n"); + return 1; + } + result = iso_image_import(image, src, ropts, &features); + iso_data_source_unref(src); + if (result < 0) { + printf ("Error importing previous session %d\n", result); + return 1; + } + iso_read_opts_free(ropts); + + iso_tree_set_replace_mode(image, ISO_REPLACE_IF_NEWER); + + /* add new dir */ + result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[2]); + if (result < 0) { + printf ("Error adding directory %d\n", result); + return 1; + } + + /* generate a multisession image with new contents */ + result = iso_write_opts_new(&opts, 1); + if (result < 0) { + printf("Cant create write opts, error %d\n", result); + return 1; + } + + /* round up to 32kb aligment = 16 block */ + ms_block = ((iso_read_image_features_get_size(features) + 15) / 16 ) * 16; + iso_write_opts_set_ms_block(opts, ms_block); + iso_write_opts_set_appendable(opts, 1); + iso_write_opts_set_overwrite_buf(opts, buf); + + iso_read_image_features_destroy(features); + + result = iso_image_create_burn_source(image, opts, &burn_src); + if (result < 0) { + printf("Cant create image, error %d\n", result); + return 1; + } + + iso_write_opts_free(opts); + + /* a. write the new image */ + printf("Adding new data...\n"); + { + struct burn_disc *target_disc; + struct burn_session *session; + struct burn_write_opts *burn_options; + struct burn_track *track; + struct burn_progress progress; + char reasons[BURN_REASONS_LEN]; + + target_disc = burn_disc_create(); + session = burn_session_create(); + burn_disc_add_session(target_disc, session, BURN_POS_END); + + track = burn_track_create(); + burn_track_set_source(track, burn_src); + burn_session_add_track(session, track, BURN_POS_END); + + burn_options = burn_write_opts_new(drive); + burn_drive_set_speed(drive, 0, 0); + burn_write_opts_set_underrun_proof(burn_options, 1); + + /* mmm, check for 32K alignment? */ + burn_write_opts_set_start_byte(burn_options, ms_block * 2048); + + if (burn_write_opts_auto_write_type(burn_options, target_disc, + reasons, 0) == BURN_WRITE_NONE) { + printf("Failed to find a suitable write mode:\n%s\n", reasons); + ret = 1; + goto exit_cleanup; + } + + /* ok, write the new track */ + burn_disc_write(burn_options, target_disc); + burn_write_opts_free(burn_options); + + while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) + usleep(1002); + + while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) { + printf("Writing: sector %d of %d\n", progress.sector, progress.sectors); + sleep(1); + } + + } + + /* b. write the new vol desc */ + printf("Writing the new vol desc...\n"); + ret = burn_random_access_write(drive, 0, (char*)buf, 32*2048, 0); + if (ret != 1) { + printf("Ups, new vol desc write failed\n"); + } + + iso_image_unref(image); + +exit_cleanup:; + burn_drive_release(drives[0].drive, 0); + burn_finish(); + iso_finish(); + + exit(ret); +} + +static int +libburn_ds_read_block(IsoDataSource *src, uint32_t lba, uint8_t *buffer) +{ + struct burn_drive *d; + off_t data_count; + + d = (struct burn_drive*)src->data; + + if ( burn_read_data(d, (off_t) lba * (off_t) 2048, (char*)buffer, + 2048, &data_count, 0) < 0 ) { + return -1; /* error */ + } + + return 1; +} + +static +int libburn_ds_open(IsoDataSource *src) +{ + /* nothing to do, device is always opened */ + return 1; +} + +static +int libburn_ds_close(IsoDataSource *src) +{ + /* nothing to do, device is always opened */ + return 1; +} + +static void +libburn_ds_free_data(IsoDataSource *src) +{ + /* nothing to do */ +} + +static IsoDataSource * +libburn_data_source_new(struct burn_drive *d) +{ + IsoDataSource *ret; + + ret = malloc(sizeof(IsoDataSource)); + ret->version = 0; + ret->refcount = 1; + ret->read_block = libburn_ds_read_block; + ret->open = libburn_ds_open; + ret->close = libburn_ds_close; + ret->free_data = libburn_ds_free_data; + ret->data = d; + return ret; +} diff --git a/libisofs/branches/thomas/demo/iso_modify.c b/libisofs/branches/thomas/demo/iso_modify.c new file mode 100644 index 00000000..85404ce4 --- /dev/null +++ b/libisofs/branches/thomas/demo/iso_modify.c @@ -0,0 +1,109 @@ +/* + * Little program to show how to modify an iso image. + */ + +#include "libisofs.h" +#include "libburn/libburn.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +void usage(char **argv) +{ + printf("%s [OPTIONS] IMAGE DIRECTORY OUTPUT\n", argv[0]); +} + +int main(int argc, char **argv) +{ + int result; + IsoImage *image; + IsoDataSource *src; + struct burn_source *burn_src; + unsigned char buf[2048]; + FILE *fd; + IsoWriteOpts *opts; + IsoReadOpts *ropts; + + if (argc < 4) { + usage(argv); + return 1; + } + + fd = fopen(argv[3], "w"); + if (!fd) { + err(1, "error opening output file"); + } + + iso_init(); + iso_set_msgs_severities("NEVER", "ALL", ""); + + /* create the data source to accesss previous image */ + result = iso_data_source_new_from_file(argv[1], &src); + if (result < 0) { + printf ("Error creating data source\n"); + return 1; + } + + /* create the image context */ + result = iso_image_new("volume_id", &image); + if (result < 0) { + printf ("Error creating image\n"); + return 1; + } + iso_tree_set_follow_symlinks(image, 0); + iso_tree_set_ignore_hidden(image, 0); + + /* import previous image */ + result = iso_read_opts_new(&ropts, 0); + if (result < 0) { + fprintf(stderr, "Error creating read options\n"); + return 1; + } + result = iso_image_import(image, src, ropts, NULL); + iso_read_opts_free(ropts); + iso_data_source_unref(src); + if (result < 0) { + printf ("Error importing previous session %d\n", result); + return 1; + } + + /* add new dir */ + result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[2]); + if (result < 0) { + printf ("Error adding directory %d\n", result); + return 1; + } + + /* generate a new image with both previous and added contents */ + result = iso_write_opts_new(&opts, 1); + if (result < 0) { + printf("Cant create write opts, error %d\n", result); + return 1; + } + /* for isolinux: iso_write_opts_set_allow_full_ascii(opts, 1); */ + + result = iso_image_create_burn_source(image, opts, &burn_src); + if (result < 0) { + printf ("Cant create image, error %d\n", result); + return 1; + } + + iso_write_opts_free(opts); + + while (burn_src->read_xt(burn_src, buf, 2048) == 2048) { + fwrite(buf, 1, 2048, fd); + } + fclose(fd); + burn_src->free_data(burn_src); + free(burn_src); + + iso_image_unref(image); + iso_finish(); + return 0; +} diff --git a/libisofs/branches/thomas/demo/iso_ms.c b/libisofs/branches/thomas/demo/iso_ms.c new file mode 100644 index 00000000..563c9d4d --- /dev/null +++ b/libisofs/branches/thomas/demo/iso_ms.c @@ -0,0 +1,114 @@ +/* + * Little program to show how to create a multisession iso image. + */ + +#include "libisofs.h" +#include "libburn/libburn.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +void usage(char **argv) +{ + printf("%s LSS NWA DISC DIRECTORY OUTPUT\n", argv[0]); +} + +int main(int argc, char **argv) +{ + int result; + IsoImage *image; + IsoDataSource *src; + struct burn_source *burn_src; + unsigned char buf[2048]; + FILE *fd; + IsoWriteOpts *opts; + IsoReadOpts *ropts; + uint32_t ms_block; + + if (argc < 6) { + usage(argv); + return 1; + } + + fd = fopen(argv[5], "w"); + if (!fd) { + err(1, "error opening output file"); + } + + iso_init(); + iso_set_msgs_severities("NEVER", "ALL", ""); + + /* create the data source to accesss previous image */ + result = iso_data_source_new_from_file(argv[3], &src); + if (result < 0) { + printf ("Error creating data source\n"); + return 1; + } + + /* create the image context */ + result = iso_image_new("volume_id", &image); + if (result < 0) { + printf ("Error creating image\n"); + return 1; + } + iso_tree_set_follow_symlinks(image, 0); + iso_tree_set_ignore_hidden(image, 0); + + /* import previous image */ + result = iso_read_opts_new(&ropts, 0); + if (result < 0) { + fprintf(stderr, "Error creating read options\n"); + return 1; + } + iso_read_opts_set_start_block(ropts, atoi(argv[1])); + result = iso_image_import(image, src, ropts, NULL); + iso_read_opts_free(ropts); + iso_data_source_unref(src); + if (result < 0) { + printf ("Error importing previous session %d\n", result); + return 1; + } + + /* add new dir */ + result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[4]); + if (result < 0) { + printf ("Error adding directory %d\n", result); + return 1; + } + + /* generate a multisession image with new contents */ + result = iso_write_opts_new(&opts, 1); + if (result < 0) { + printf("Cant create write opts, error %d\n", result); + return 1; + } + + /* round up to 32kb aligment = 16 block */ + ms_block = atoi(argv[2]); + iso_write_opts_set_ms_block(opts, ms_block); + iso_write_opts_set_appendable(opts, 1); + + result = iso_image_create_burn_source(image, opts, &burn_src); + if (result < 0) { + printf ("Cant create image, error %d\n", result); + return 1; + } + iso_write_opts_free(opts); + + while (burn_src->read_xt(burn_src, buf, 2048) == 2048) { + fwrite(buf, 1, 2048, fd); + } + fclose(fd); + burn_src->free_data(burn_src); + free(burn_src); + + iso_image_unref(image); + iso_finish(); + return 0; +} diff --git a/libisofs/branches/thomas/demo/iso_read.c b/libisofs/branches/thomas/demo/iso_read.c new file mode 100644 index 00000000..de30717a --- /dev/null +++ b/libisofs/branches/thomas/demo/iso_read.c @@ -0,0 +1,167 @@ +/* + * Little program to output the contents of an iso image. + */ + + +#include +#include +#include +#include + +#include "libisofs.h" + +static void +print_permissions(mode_t mode) +{ + char perm[10]; + + //TODO suid, sticky... + + perm[9] = '\0'; + perm[8] = mode & S_IXOTH ? 'x' : '-'; + perm[7] = mode & S_IWOTH ? 'w' : '-'; + perm[6] = mode & S_IROTH ? 'r' : '-'; + perm[5] = mode & S_IXGRP ? 'x' : '-'; + perm[4] = mode & S_IWGRP ? 'w' : '-'; + perm[3] = mode & S_IRGRP ? 'r' : '-'; + perm[2] = mode & S_IXUSR ? 'x' : '-'; + perm[1] = mode & S_IWUSR ? 'w' : '-'; + perm[0] = mode & S_IRUSR ? 'r' : '-'; + printf(" %s ",perm); +} + +static void +print_type(mode_t mode) +{ + switch(mode & S_IFMT) { + case S_IFSOCK: printf("[S] "); break; + case S_IFLNK: printf("[L] "); break; + case S_IFREG: printf("[R] "); break; + case S_IFBLK: printf("[B] "); break; + case S_IFDIR: printf("[D] "); break; + case S_IFIFO: printf("[F] "); break; + } +} + +static void +print_file_src(IsoFileSource *file) +{ + struct stat info; + char *name; + iso_file_source_lstat(file, &info); + print_type(info.st_mode); + print_permissions(info.st_mode); + //printf(" {%ld,%ld} ", (long)info.st_dev, (long)info.st_ino); + name = iso_file_source_get_name(file); + printf(" %s", name); + free(name); + if (S_ISLNK(info.st_mode)) { + char buf[PATH_MAX]; + iso_file_source_readlink(file, buf, PATH_MAX); + printf(" -> %s\n", buf); + } + printf("\n"); +} + +static void +print_dir(IsoFileSource *dir, int level) +{ + int ret, i; + IsoFileSource *file; + struct stat info; + char *sp = alloca(level * 2 + 1); + + for (i = 0; i < level * 2; i += 2) { + sp[i] = '|'; + sp[i+1] = ' '; + } + + sp[level * 2-1] = '-'; + sp[level * 2] = '\0'; + + ret = iso_file_source_open(dir); + if (ret < 0) { + printf ("Can't open dir %d\n", ret); + } + while ((ret = iso_file_source_readdir(dir, &file)) == 1) { + printf("%s", sp); + print_file_src(file); + ret = iso_file_source_lstat(file, &info); + if (ret < 0) { + break; + } + if (S_ISDIR(info.st_mode)) { + print_dir(file, level + 1); + } + iso_file_source_unref(file); + } + iso_file_source_close(dir); + if (ret < 0) { + printf ("Can't print dir\n"); + } +} + +int main(int argc, char **argv) +{ + int result; + IsoImageFilesystem *fs; + IsoDataSource *src; + IsoFileSource *root; + IsoReadOpts *ropts; + + if (argc != 2) { + printf ("You need to specify a valid path\n"); + return 1; + } + + iso_init(); + iso_set_msgs_severities("NEVER", "ALL", ""); + + result = iso_data_source_new_from_file(argv[1], &src); + if (result < 0) { + printf ("Error creating data source\n"); + return 1; + } + + result = iso_read_opts_new(&ropts, 0); + if (result < 0) { + fprintf(stderr, "Error creating read options\n"); + return 1; + } + result = iso_image_filesystem_new(src, ropts, 1, &fs); + iso_read_opts_free(ropts); + if (result < 0) { + printf ("Error creating filesystem\n"); + return 1; + } + + printf("\nVOLUME INFORMATION\n"); + printf("==================\n\n"); + + printf("Vol. id: %s\n", iso_image_fs_get_volume_id(fs)); + printf("Publisher: %s\n", iso_image_fs_get_publisher_id(fs)); + printf("Data preparer: %s\n", iso_image_fs_get_data_preparer_id(fs)); + printf("System: %s\n", iso_image_fs_get_system_id(fs)); + printf("Application: %s\n", iso_image_fs_get_application_id(fs)); + printf("Copyright: %s\n", iso_image_fs_get_copyright_file_id(fs)); + printf("Abstract: %s\n", iso_image_fs_get_abstract_file_id(fs)); + printf("Biblio: %s\n", iso_image_fs_get_biblio_file_id(fs)); + + printf("\nDIRECTORY TREE\n"); + printf("==============\n"); + + result = fs->get_root(fs, &root); + if (result < 0) { + printf ("Can't get root %d\n", result); + return 1; + } + //print_file_src(root); + print_dir(root, 0); + iso_file_source_unref(root); + + fs->close(fs); + iso_filesystem_unref((IsoFilesystem*)fs); + iso_data_source_unref(src); + iso_finish(); + return 0; +} diff --git a/libisofs/branches/thomas/demo/lsl.c b/libisofs/branches/thomas/demo/lsl.c new file mode 100644 index 00000000..17659f75 --- /dev/null +++ b/libisofs/branches/thomas/demo/lsl.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "libisofs.h" +#include "fsource.h" + +#include +#include +#include + +/* + * Little test program to test filesystem implementations. + * + */ + +static void +print_permissions(mode_t mode) +{ + char perm[10]; + + //TODO suid, sticky... + + perm[9] = '\0'; + perm[8] = mode & S_IXOTH ? 'x' : '-'; + perm[7] = mode & S_IWOTH ? 'w' : '-'; + perm[6] = mode & S_IROTH ? 'r' : '-'; + perm[5] = mode & S_IXGRP ? 'x' : '-'; + perm[4] = mode & S_IWGRP ? 'w' : '-'; + perm[3] = mode & S_IRGRP ? 'r' : '-'; + perm[2] = mode & S_IXUSR ? 'x' : '-'; + perm[1] = mode & S_IWUSR ? 'w' : '-'; + perm[0] = mode & S_IRUSR ? 'r' : '-'; + printf(" %s ",perm); +} + +static void +print_type(mode_t mode) +{ + switch(mode & S_IFMT) { + case S_IFSOCK: printf("[S] "); break; + case S_IFLNK: printf("[L] "); break; + case S_IFREG: printf("[R] "); break; + case S_IFBLK: printf("[B] "); break; + case S_IFDIR: printf("[D] "); break; + case S_IFIFO: printf("[F] "); break; + } +} + +static void +print_file_src(IsoFileSource *file) +{ + struct stat info; + char *name; + iso_file_source_lstat(file, &info); + print_type(info.st_mode); + print_permissions(info.st_mode); + printf(" {%ld,%ld} ", (long)info.st_dev, (long)info.st_ino); + name = iso_file_source_get_name(file); + printf(" %s", name); + free(name); + if (S_ISLNK(info.st_mode)) { + char buf[PATH_MAX]; + iso_file_source_readlink(file, buf, PATH_MAX); + printf(" -> %s\n", buf); + } + printf("\n"); +} + +int main(int argc, char **argv) +{ + int res; + IsoFilesystem *fs; + IsoFileSource *dir; + IsoFileSource *file; + struct stat info; + + if (argc != 2) { + fprintf(stderr, "Usage: lsl /path/to/file\n"); + return 1; + } + + /* create filesystem object */ + res = iso_local_filesystem_new(&fs); + if (res < 0) { + fprintf(stderr, "Can't get local fs object, err = %d\n", res); + return 1; + } + + res = fs->get_by_path(fs, argv[1], &dir); + if (res < 0) { + fprintf(stderr, "Can't get file, err = %d\n", res); + return 1; + } + + res = iso_file_source_lstat(dir, &info); + if (res < 0) { + fprintf(stderr, "Can't stat file, err = %d\n", res); + return 1; + } + + if (S_ISDIR(info.st_mode)) { + res = iso_file_source_open(dir); + if (res < 0) { + fprintf(stderr, "Can't open file, err = %d\n", res); + return 1; + } + + while (iso_file_source_readdir(dir, &file) == 1) { + print_file_src(file); + iso_file_source_unref(file); + } + + res = iso_file_source_close(dir); + if (res < 0) { + fprintf(stderr, "Can't close file, err = %d\n", res); + return 1; + } + } else { + print_file_src(dir); + } + + iso_file_source_unref(dir); + iso_filesystem_unref(fs); + return 0; +} diff --git a/libisofs/branches/thomas/demo/tree.c b/libisofs/branches/thomas/demo/tree.c new file mode 100644 index 00000000..6dddbb99 --- /dev/null +++ b/libisofs/branches/thomas/demo/tree.c @@ -0,0 +1,107 @@ +/* + * Little program that import a directory and prints the resulting iso tree. + */ + +#include "libisofs.h" +#include +#include +#include +#include +#include +#include + +static void +print_permissions(mode_t mode) +{ + char perm[10]; + + //TODO suid, sticky... + + perm[9] = '\0'; + perm[8] = mode & S_IXOTH ? 'x' : '-'; + perm[7] = mode & S_IWOTH ? 'w' : '-'; + perm[6] = mode & S_IROTH ? 'r' : '-'; + perm[5] = mode & S_IXGRP ? 'x' : '-'; + perm[4] = mode & S_IWGRP ? 'w' : '-'; + perm[3] = mode & S_IRGRP ? 'r' : '-'; + perm[2] = mode & S_IXUSR ? 'x' : '-'; + perm[1] = mode & S_IWUSR ? 'w' : '-'; + perm[0] = mode & S_IRUSR ? 'r' : '-'; + printf("[%s]",perm); +} + +static void +print_dir(IsoDir *dir, int level) +{ + int i; + IsoDirIter *iter; + IsoNode *node; + char *sp = alloca(level * 2 + 1); + + for (i = 0; i < level * 2; i += 2) { + sp[i] = '|'; + sp[i+1] = ' '; + } + + sp[level * 2-1] = '-'; + sp[level * 2] = '\0'; + + iso_dir_get_children(dir, &iter); + while (iso_dir_iter_next(iter, &node) == 1) { + + if (ISO_NODE_IS_DIR(node)) { + printf("%s+[D] ", sp); + print_permissions(iso_node_get_permissions(node)); + printf(" %s\n", iso_node_get_name(node)); + print_dir(ISO_DIR(node), level+1); + } else if (ISO_NODE_IS_FILE(node)) { + printf("%s-[F] ", sp); + print_permissions(iso_node_get_permissions(node)); + printf(" %s\n", iso_node_get_name(node) ); + } else if (ISO_NODE_IS_SYMLINK(node)) { + printf("%s-[L] ", sp); + print_permissions(iso_node_get_permissions(node)); + printf(" %s -> %s \n", iso_node_get_name(node), + iso_symlink_get_dest(ISO_SYMLINK(node)) ); + } else { + printf("%s-[C] ", sp); + print_permissions(iso_node_get_permissions(node)); + printf(" %s\n", iso_node_get_name(node) ); + } + } + iso_dir_iter_free(iter); +} + +int main(int argc, char **argv) +{ + int result; + IsoImage *image; + + if (argc != 2) { + printf ("You need to specify a valid path\n"); + return 1; + } + + iso_init(); + iso_set_msgs_severities("NEVER", "ALL", ""); + + result = iso_image_new("volume_id", &image); + if (result < 0) { + printf ("Error creating image\n"); + return 1; + } + + result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[1]); + if (result < 0) { + printf ("Error adding directory %d\n", result); + return 1; + } + + printf("================= IMAGE =================\n"); + print_dir(iso_image_get_root(image), 0); + printf("\n\n"); + + iso_image_unref(image); + iso_finish(); + return 0; +} diff --git a/libisofs/branches/thomas/doc/Tutorial b/libisofs/branches/thomas/doc/Tutorial new file mode 100755 index 00000000..6de705bd --- /dev/null +++ b/libisofs/branches/thomas/doc/Tutorial @@ -0,0 +1,506 @@ +=============================================================================== + LIBISOFS DEVELOPMENT TUTORIAL +=============================================================================== + +Creation date: 2008-Jan-27 +Author: Vreixo Formoso +_______________________________________________________________________________ + +This is a little tutorial of how to use libisofs library for application +development. + +Contents: +--------- + +1. Introduction + 1.1 Library initialization + 1.2 Image context + 1.3 Error reporting +2. Creating an image + 2.1 Image tree manipulation + 2.2 Set the write options + 2.3 Obtaining a burn_source +3. Image growing and modification + 3.1 Growing vs Modification + 3.2 Image import + 3.3 Generating a new image +4. Bootable images +5. Advanced features + + +------------------------------------------------------------------------------- +1. Introduction +------------------------------------------------------------------------------- + +[TODO some lines about refcounts] + +------------------------------------------------------------------------------- +1.1. Library initialization + +Before any usage of the library, you have to call + + iso_init() + +in the same way, when you have finished using the library, you should call + + iso_finish() + +to free all resources reserved by the library. + +------------------------------------------------------------------------------- +1.2. Image context + +Libisofs is image-oriented, the core of libisofs usage is the IsoImage object. +Thus, the first you need to do is to get your own IsoImage object: + + IsoImage *my_image; + iso_image_new("NEW DISC", &my_image); + +An IsoImage is a context for image creation. It holds the files that will be +added to image, other related information and several options to customize +the behavior of libisofs when working with such Image. i.e., an IsoImage is +a context for libisofs operations. As such, you can work with several image +contexts at a time. + +------------------------------------------------------------------------------- +1.3. Error reporting + +In libisofs error reporting is done in two ways: with the return value of +the functions and with the message queue. + +Error codes are negative numbers, defined in "libisofs.h" header. An +error code is associated with a given severity, either "DEBUG", "UPDATE", +"NOTE", "HINT", "WARNING", "SORRY", "FAILURE" and "FATAL". For the meaning +of each severity take a look at private header "libiso_msgs.h". Errors +reported by function return value are always "FAILURE" or "FATAL". Other kind +of errors are only reported with the message queue. You can get the severity +of any error message with iso_error_get_severity() function. + +First of all, most libisofs functions return an integer. If such integer is +a negative number, it means the function has returned an error. The error code +and its severity is encoded in the return value (take a look at error codes in +libisofs.h header). + +Additionally, libisofs reports most of its errors in a message queue. Error +messages on that queue can be printed directly to stderr or programmatically +retrieved. First of all, you should set the severity threshold over which an +error is printed or enqueued, with function: + + iso_set_msgs_severities() + +Errors enqueued can be retrieved with function: + + iso_obtain_msgs() + +Together with the code error, a text message and its severity, this function +also returns the image id. This is an identifier that uniquely identifies a +given image context. You can get the identifier of each IsoImage with the + + iso_image_get_msg_id() + +and that way distinguish what image has issued the message. + + +------------------------------------------------------------------------------- +2. Creating an Image +------------------------------------------------------------------------------- + +An image is built from a set of files that you want to store together in an +ISO-9660 volume. We call the "iso tree" to the file hierarchy that will be +written to image. The image context, IsoImage, holds that tree, together with +configuration options and other properties of the image, that provide info +about the volume (such as the identifier, author, etc...). + +All configuration options and volume properties are set by its corresponding +setters (iso_image_set_volset_id(), iso_image_set_publisher_id()...) + +To create an image, you have to follow the following steps: + +* Obtain the image context. + See "1.2 Image context" for details of how to obtain the IsoImage. +* Set the desired properties +* Prepare the iso tree with the files you want to add to image. + See "2.1 Image tree manipulation" for details +* Select the options for image generation. + See "2.2 Set the write options" +* Get the burn_source used to actually write the image. + + +------------------------------------------------------------------------------- +2.1 Image tree manipulation + +libisofs maintains in memory a file tree (usually called the iso tree), that +represents the files and directories that will be written later to image. You +are allowed to make whatever changes you want to that tree, just like you do +to any "real" filesystem, before actually write it to image. + +Unlike other ISO-9660 mastering tools, you have full control over the file +hierarchy that will be written to image, via the libisofs API. You can add +new files, create any file in image, change its name, attributes, etc The iso +tree behaves just like any other POSIX filesystem. + +The root of the iso tree is created automatically when the IsoImage is +allocated, and you can't replace it. To get a reference to it you can use the +function: + + iso_image_get_root() + +* Iso tree objects + +Each file in the image or iso tree is represented by an IsoNode instance. In +the same way a POSIX filesystem has several file types (regular files, +directories, symlinks...), the IsoNode has several subtypes: + + IsoNode + | + --------------------------------- + | | | | + IsoDir IsoFile IsoSymlink IsoSpecial + +where + + - IsoDir represents a directory + - IsoFile represents a regular file + - IsoSymlink represents a symbolic linke + - IsoSpecial represents any other POSIX file, i.e. block and character + devices, FIFOs, sockets. + +You can obtain the concrete type of an IsoNode with the iso_node_get_type() +function. + +Many libisofs functions take or return an IsoNode. Many others, however, +require an specific type. You can safety cast any subtype to an IsoNode +object. In the same way, after ensuring you are dealing with the correct +subtype, you can downcast a given IsoNode to the specific subtype. + + IsoDir *dir; + IsoNode *node; + + node = (IsoNode*) dir; + + if (iso_node_get_type(node) == LIBISO_DIR) { + dir = (IsoDir*) node; + ... + } + +or with the provided macros: + + IsoDir *dir; + IsoNode *node; + + node = ISO_NODE(dir); + + if (ISO_NODE_IS_DIR(node)) { + dir = ISO_DIR(node); + ... + } + +* Adding files to the image + +Files can be added to the image or iso tree either as new files or as files +from the filesystem. + +In the first case, files are created directly on the image. They do not +correspond to any file in the filesystem. Provided functions are: + + - iso_tree_add_new_dir() + - iso_tree_add_new_symlink() + - iso_tree_add_new_special() + +On the other side, you can add local files to the image, either with the + + iso_tree_add_node() + +or with + + iso_tree_add_dir_rec(). + +The first is intended to add a single file, while the last can be used to add, +recursively, a full directory (see below for details). + +It is important to note that libisofs doesn't store any kind of link between +the IsoNode and the filesystem file it was created from. The above functions +just initialize a newly created IsoNode with the attributes of a given file in +the filesystem. After that, you can move the original file, change its +attributes or even delete it. The IsoNode in the image tree remains with the +original attributes. One exception to this rule are the contents of a regular +file. Libisofs does not make any copy of those contents until they're actually +written to image. Thus, you shouldn't modify, move or delete regular files +after adding them to the IsoImage. + + +* Recursive directory addition. + +One common use case is to add a local directory to the image. While this can +be done with iso_tree_add_node(), handling the addition of directory children +in the application, libisofs provides a function suitable for this case: + + iso_tree_add_dir_rec() + +that takes care of adding all files inside a directory, recursing on directory +children. By default, this function adds all children. However, it is usual +that you don't want really this. For example, you may want to exclude some +kind of files (backup files, application sockets,...). Libisofs provides +several functions to customize the behavior of that function: + + - iso_tree_set_follow_symlinks() + - iso_tree_set_ignore_hidden() + - iso_tree_set_ignore_special() + - iso_tree_add_exclude() + +* Operations on iso tree + +[TODO briefly explain how to add node, change attributes, ...] + +* Replace mode + +[TODO] + +------------------------------------------------------------------------------- +2.2 Set the write options + +Once you have prepared the iso tree, it is time to select the options for the +image writing. + +These options affect the characteristics of the filesystem to create in the +image, but also can control how libisofs generates the image. + +First of all you have to get an instance of IsoWriteOpts, with the function + + iso_write_opts_new() + +The several options available can be classified in: + +- Extensions to add to the ISO-9660 image: + + iso_write_opts_set_rockridge() + iso_write_opts_set_joliet() + iso_write_opts_set_iso1999() + +RockRidge is highly recommended, in fact you should use it in all image. Joliet +is needed if you want to use your images in Windows system. Nowadays, +ISO-9660:1999 is no much useful, so in most cases you don't want such +extension. + +- ISO-9660 options: + + iso_write_opts_set_iso_level() + iso_write_opts_set_omit_version_numbers() + iso_write_opts_set_allow_deep_paths() + iso_write_opts_set_allow_longer_paths() + iso_write_opts_set_max_37_char_filenames() + iso_write_opts_set_no_force_dots() + iso_write_opts_set_allow_lowercase() + iso_write_opts_set_allow_full_ascii() + +These control the options for the ISO-9660 filesystem. In most cases you won't +care about them, as it is the RockRidge or Joliet extensions what determine the +properties of the files once the image is mounted. + +- File attributes options + + iso_write_opts_set_replace_mode() + iso_write_opts_set_default_dir_mode() + iso_write_opts_set_default_file_mode() + iso_write_opts_set_default_uid() + iso_write_opts_set_default_gid() + iso_write_opts_set_replace_timestamps() + iso_write_opts_set_default_timestamp() + iso_write_opts_set_always_gmt() + +They allow to set default attributes for files in image, despite of the real +attributes of the file on the local filesystem. + +------------------------------------------------------------------------------- +2.3 Obtaining a burn_source + +Finally, you get the burn_source used to write the image with the function: + + iso_image_create_burn_source() + +The returned burn_source is suitable for using with libburn, to directly burn +the image to a disc. Alternatively, you can use burn_source read() to get +the image contents (for example, to write them to a file, pipe...). + +Before creating the burn_source, libisofs computes the size of the image, so +the get_size() function of the burn_source always returns the final image +size. It also starts a writing thread. All the operations needed to generate +the image are done by this thread, including read the original files contents. +The image is writing to a FIFO buffer, from which the burn_source will read. +The size of the buffer can be set in advanced with a property of the +IsoWriteOpts struct: + + iso_write_opts_set_fifo_size() + +You can get the state of the buffer in any moment, with the function: + + iso_ring_buffer_get_status() + +You can also cancel the writer thread at any time, with the cancel() function +of the burn_source. + + +------------------------------------------------------------------------------- +3. Image growing and modification +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +3.1 Growing vs Modification + +Libisofs is not restricted only to create new images. It can also be used to +modify existing images. It supports two kind of image modifications, that we +have called image growing and image modification: + +Image modification consists in generating a new image, based on the contents +of an existing image. In this mode, libisofs takes an image, the users modifies +its contents (adding new files, removing files, changing their names...), and +finally libisofs generates a completely new image. + +On the other side, image growing is similar, with the difference that the new +image is dependent on the other, i.e., it refers to files of the other image. +Thus, it can't be mounted without the old image. The purpose of this kind of +images is to increment or add files to a multisession disc. The new image only +contains the new files. Old files are just references to the old image blocks. + +The advantage of the growing approach is that the generated image is smaller, +as only the new files are written. This mode is suitable when you just want to +add some files to a very big image, or when dealing with write-once media, such +as CD-R. Both the time and space needed for the modification is much less than +with normal image modify. + +The main problem of growing is that the new image needs to be recorded together +with the old image, in order to be mountable. The total size of the image +(old + new) is bigger (even much bigger) than a completely new image. So, if +you plan to distribute an image on Internet, or burn it to a disc, generate a +completely new image is usually a better alternative. + +To be able to mount a grown image, the OS needs to now you have appended new +data to the original image. In multisession media (such as CD-R), the new data +is appended as a new session, so the OS can identify this and mount the image +propertly. However, when dealing with non-multisession media (such as DVD+RW) +or plain .iso files, the new data is just appended at the end of the old image, +and the OS has no way to know that the appended data is in fact a "new +session". The method introduced by Andy Polyakov in growisofs can be used in +those cases. It consists in overwrite the volume descriptors of the old image +with a new ones that refer to the newly appended contents. + +------------------------------------------------------------------------------- +3.2 Image import + +The first thing you need to do in order to modify or grow an image is to import +it, with the function: + + iso_image_import() + +It takes several arguments. + +First, the image context, an IsoImage previously obtained with iso_image_new(). +In most cases you will want to use an empty image. However, if you have already +added files to the image, they will be removed and replaced with the contents +of the image being imported. + +The second parameter is an IsoDataSource instance. It abstracts the original +image, and it is used by libisofs to access its contents. You are free to +implement your own data source to access image contents. However, libisofs has +a simple implementation suitable for reading images on the local filesystem, +that can be used for import both .iso files and inserted media, via the block +device and POSIX functions. You can get it with + + iso_data_source_new_from_file() + +The third parameter of iso_image_import() is a pointer to an IsoReadOpts +struct. It holds the options for image reading. You get it with: + + iso_read_opts_new() + +and after calling iso_image_import() you should free it with + + iso_read_opts_free() + +Some options are related to select what extensions to read. Default options +are suitable for most users. + + iso_read_opts_set_no_rockridge() + iso_read_opts_set_no_joliet() + iso_read_opts_set_no_iso1999() + iso_read_opts_set_preferjoliet() + +If RockRidge extensions are not present, many files attributes can't be +obtained. In those cases libisofs uses default values. You have options to +configure what default values to use. + + iso_read_opts_set_default_uid() + iso_read_opts_set_default_gid() + iso_read_opts_set_default_permissions() + +If the original image has been created in another system with a different +charset, you may want to use: + + iso_read_opts_set_input_charset() + +to specify the encoding of the file names on image. + +Finally, to import multisession images, you should tell libisofs that it must +read the last session. For that, you must set the block where the last session +starts: + + iso_read_opts_set_start_block() + +The last parameter for iso_image_import(), optional, is a pointer that will +be filled with a library-allocated IsoReadImageFeatures, that lets you access +some information about the image: size, extensions used,... + +[TODO: explain that iso_image_import uses dir rec options] + +------------------------------------------------------------------------------- +3.3 Generating a new image + +After importing the image, the old image tree gets loaded. You are free to +make any changes to it: add new files, remove files, change names or +attributes... Refer to "2.1 Image tree manipulation" for details. + +When it is ready, you can now create the new image. The process is the same as +explained in "2.2 Set the write options" and "2.3 Obtaining a burn_source". +However, there are some write options that should be taken into account. + +First of all, you must select whether you want to grow or modify the image +(read "3.1 Growing vs Modification" for details). You must call + + iso_write_opts_set_appendable() + +An appendable image leads to image growing, and a non-appendable image leads +to a completelly new image (modification). An appendable image will be appended +after the old image (in a new session, for example). Thus, in those cases, the +first block of the image is not 0. You should set the correct lba of the first +block with: + + iso_write_opts_set_ms_block() + +That is usually the "Next Writable Address" on a multisession media, and a +value slightly greater than the old image size on .iso files or DVD+RW media. +You can obtain the old image size with the iso_read_image_features_get_size() +function. + +In this last case (i.e., on a non multisession media), you will need to +overwrite the volume descriptors of the old image with the new ones. To do +this you need: + +- Allocate a buffer of at least 64 KiBs. +- Initialize it with the first 64 KiBs of the original image. +- Pass the buffer to libisofs with the iso_write_opts_set_overwrite_buf() + option. +- After appending the new image, you have to overwrite the first 64 KiBs of + the original image with the new content of the buffer. + + +------------------------------------------------------------------------------- +4. Bootable images +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +5. Advanced features +------------------------------------------------------------------------------- + + diff --git a/libisofs/branches/thomas/doc/Wiki b/libisofs/branches/thomas/doc/Wiki new file mode 100755 index 00000000..c9f6f8e1 --- /dev/null +++ b/libisofs/branches/thomas/doc/Wiki @@ -0,0 +1,32 @@ += libisofs = + +libisofs is a library to create an ISO-9660 filesystem, and supports extensions like RockRidge and Joliet. It is also a full featured ISO-9660 editor, allowing you to modify an ISO image or multisession disc, including file addition/removal, change of file names and attributes, and similar. + +The old libisofs.so.5 has been declarated deprecated and frozen, leaving it unmaintained. A full refactoring of the design has been done during the last months, and the next generation libisofs.so.6 of the library will be released in the following days. + +== Source Code == + +The code is maintained in a [http://bazaar-vcs.org/ Bazaar] repository at Launchpad (https://launchpad.net/libisofs/). You can download it with: + +{{{ +$ bzr branch lp:libisofs +}}} + + +To report any bug or suggest enchantments, [http://libburnia-project.org/register register] yourself and submit a new ticket. Bug and enchantments reports for nglibisofs can be found at http://libburnia-project.org/report/9. + +== Usage tutorial == + +Coming soon... For now check [http://codebrowse.launchpad.net/~mario-danic/libisofs/mainline/annotate/metalpain2002%40yahoo.es-20080201154704-xqyzc57vki97iv3y?file_id=tutorial-20080127170757-cwmomu7oz9eh7fcz-1 "doc/Tutorial"] in the source tree. + + +=== Applications === + +Comming soon: + +[http://libburnia-project.org/browser/libisoburn/trunk libisoburn]: + emulates ISO 9660 multi-session on overwriteable media, coordinates libisofs and libburn. + +[http://libburnia-project.org/browser/libisoburn/trunk/xorriso/xorriso_eng.html?format=raw xorriso]: + creates, loads, manipulates and writes ISO 9660 filesystem images with Rock Ridge extensions. + diff --git a/libisofs/branches/thomas/doc/devel/1. Overview b/libisofs/branches/thomas/doc/devel/1. Overview new file mode 100644 index 00000000..e69de29b diff --git a/libisofs/branches/thomas/doc/devel/2. Features b/libisofs/branches/thomas/doc/devel/2. Features new file mode 100644 index 00000000..89501e38 --- /dev/null +++ b/libisofs/branches/thomas/doc/devel/2. Features @@ -0,0 +1,193 @@ +FEATURES +======== + +Contents: + +2.0 Operations on image tree +2.1 ECMA-119 +2.2 Rock Ridge +2.3 Joliet +2.4 El-Torito +2.5 UDF +2.6 HFS/HFS+ +2.7 Others + + +=============================================================================== + +2.0 Operations on image tree +----------------------------- + +Basic: + - We HAVE TO Support addition of directories + - From filesystem + - From filesystem recursively + - New on image + - We HAVE TO support addition of files + - From local filesystem + - From previous/ms images + - We HAVE TO support addition of other POSIX file types + - From filesystem + - New on image + - Types: symlinks, block/char devices, fifos, sockets... + - We HAVE TO support modification of file names on image + - We HAVE TO support modification of POSIX attributes: + - Uid/Gid + - Permissions (we DON'T HAVE TO support full mode modification, + as we don't want a dir to be changed to a reg file!!) + - Timestamps + - We HAVE TO support deletion of nodes. + - We HAVE TO support iteration of directory tree. + - We WANT TO support direct getting (without iteration) of the number of + nodes in a directory. + +Extras: + - We WANT TO support on-the-fly modification of file contents, to + allow things like compression and encryption. + +Notes: many operations will need RR extensions to be actually reflected on +the image. + +=============================================================================== + +2.1 ECMA-119 +------------ + +Support for ECMA-119 (ISO-9660) specification. + +2.1.1 Creation +-------------- + +We HAVE TO support creation of new images. + + General: + - We HAVE TO support single volume images + - We DON'T NEED TO support multiple volume images. + It seems multiple volume images are not used. + - We HAVE TO support bootable volumes (see 2.4 in this doc) + Conformance: + - We HAVE TO support Level 1 restrictions (ECMA-119 10.1) + - We HAVE TO support Level 2 restrictions (ECMA-119 10.2) + Single Section files have a theoric size limit of 4GB (data length + is a 32-bit number, see ECMA-119 9.1.4). However I think I have + read that only files up to 2GB are supported. + - We MAY support full Level 3 (ECMA-119 10.3) + Multiple file sections are useful to support files higher than + level 2 limit. However, it seems it's a feature not supported in + most O.S. nowadays, so it's not very useful. + - We DON'T WANT TO support files recording in interleaved mode + (ECMA-119 6.4.3) + It seems a feature that it's not used. + - We DON'T WANT TO support associated files (ECMA-119 6.5.4) + What is that? Is it used? + - We DON'T WANT TO support Volume Partitions (ECMA-119 8.6) + What is that? Is it used? + - We DON'T WANT TO support extended attribute records (ECMA-119 9.5) + It seems an unused feature. RR is a better alternative. + - We DON'T NEED TO support file versions other than 1. + Restrictions: + - We HAVE TO provide a way to relax iso restrictions related to + filenames, allowing: + - Higher filename length, up to 37 chars (ECMA-119 7.5.1/7.6.3) + - Omit version number (ECMA-119 7.5.1) + - Directory hierarchy deeper than 8 levels / 255 path length + (ECMA-119 6.8.2.1) + - More characters in filenames, not only d-characters + + +2.2.2 Reading +------------- + + General + - We HAVE TO support the reading of iso images + - We DON'T NEED TO support reading of features we don't support in + creation (see 2.2.1 above) + - We HAVE TO support reading arbitray file contents inside image + +2.2.3 Modification/growing +-------------------------- + + General + - We HAVE TO support creation of new images from the contents of + an existing image + - We HAVE TO support multissession images + - We HAVE TO support growing of images + +=============================================================================== + +2.2 Rock Ridge +-------------- + +- We HAVE TO support ALL Rock Ridge features, with these exceptions: + - We DON'T NEED TO support SF System User Entry (RRIP 4.1.7), used to + encode sparse files. + - We MIGHT support BACKUP timestamp (RRIP 4.1.6) +- We HAVE TO support any charset in RR filenames, and not only POSIX portable + filename character set (RRIP 3.4.1). Namely, UTF-8 SHOULD BE the default for + RR filenames. +- We MIGHT support Linux specific ZF extension, to allow transparent + compression. + +=============================================================================== + +2.3 Joliet +---------- + +- We HAVE TO support ALL Joliet features, with these exceptions: + - We DON'T KNOW what to do with UCS-2 conformance level 1 and 2 (scape + sequences '%\@' and '%\C'). What's this??????? + - We DON'T KNOW what to do with CD-XA extensions. + + +=============================================================================== + +2.4 El-Torito +------------- + +- We HAVE TO El-Torito standard with a single boot image. +- We MAY support multiple boot images and boot entry selection. + - El Torito standard is not very clear about how to do that. +- We HAVE TO support both emulation and not emulation mode. +- We HAVE TO support 80x86 platform. We MAY support Power PC and Mac platforms. +- We HAVE TO provide improved support for isolinux boot images, namely patching + features. +- We HAVE TO support El-Torito in ms images. + + +=============================================================================== + +2.5 UDF +------- + + + +=============================================================================== + +2.6 HFS/HFS+ +------------ + + + + + +=============================================================================== + +2.7 Others +---------- + +- We HAVE TO support sorting of file contents on image +- We HAVE TO support inode caching to prevent the same file to be written + several times into the image +- We DON'T NEED TO support TRANS.TBL files +- We DON'T NEED TO support padding of images + - Padding should be part of the burning process + + + + + + + + + + diff --git a/libisofs/branches/thomas/doc/devel/3. Use Cases b/libisofs/branches/thomas/doc/devel/3. Use Cases new file mode 100644 index 00000000..e6e17f0f --- /dev/null +++ b/libisofs/branches/thomas/doc/devel/3. Use Cases @@ -0,0 +1,193 @@ +USE CASES FOR NG LIBISOFS +========================= + +3.1 General Operations +====================== + +3.1.1 Creation of a new image +----------------------------- + +Desc: Creation of a new ISO image from files on the local filesystem +Phases: + - User creates a new image context + - User get the root (empty) of the image + - User adds files to the image root (see 3.2.) + - User sets the options for the the new image (extension to use...) + - User gets a burn_source to write the image. + - The burn_source can be used by libburn to write the new image. + +3.1.2 Image growing (multisession) +---------------------------------- + +Desc: An existing image can be grown with new files. New content is added + incrementally. Suitable for multisession. Growing support for + overwritteable media. +Phases: + - Uses reads an existing image to get the image context. + - User get the root of the image + - User modifies the image tree (see 3.2.) + - User sets the options for the the new image (extension to use...) + A required option will be the nwa for the image. + Optionally it can pass a pointer to a 64K buffer, that will be filled + with suitable volume descriptors to be used with overwrieable media. + - User gets a burn_source to write the image. + - The burn_source can be used by libburn to write an image that should be + appended to the previous image. + + +3.1.3 Image modification +------------------------ + +Desc: Creation of a new image from the contents of a previous image. +Phases: + - Uses reads an existing image to get the image context. + - User get the root of the image + - User modifies the image tree (see 3.2.) + - User sets the options for the the new image (extension to use...) + - User gets a burn_source to write the image. + - The burn_source can be used by libburn to write the image to another + device or file. + +3.2 Tree operations +=================== + +3.2.1 Addition of contents +-------------------------- + + All addition operations take a parent dir. The functions check that the + node name is unique among all children. Image context options determine + what to do if a file with such name already exist. + + 3.2.1.1 Directories + -------------------- + - Creation of new directories in image, given a parent dir. and a name. + Attributes are initialized to default values + - Addition of a dir from the filesystem, given a parent. + Dir contents are not added. Name and other attributes taken from + the dir on filesystem + - Recursive addition of a dir, given a parent. Directory contents are + recursivelly added to image. + + 3.2.1.2 Regular files + ---------------------- + - Addition of a local filesystem file. Name, attributes and contents to + be written taken from the filesystem file. + - Addition of files from the previous image. Files are automatically + added to the tree when the image is read. Name and attrbs taken from + previous image. When the image has no RR extensions, unavailable atts + are initialized to default values. The contents are only written to + img if we choose image modification. + - Addition of filtered files. Name and atts taken from the local + filesystem. A filter (see 3.3) is applied to the file contents before + written to image. + - Addition of splitted files. Like local filesystem files, but the file + is splitted in several files on a given size. Suitable for big (> 2GB) + files. Name of the splitted files automatically generated. + + 3.2.1.3 Symbolic links + ---------------------- + + Simbolic links are only written to image if RR extensions are enabled. + + - Addition of a simbolic link from local filesystem. Name, atts and + dest of a path are taken from the simbolic link. + - Addition of new link on image to a path. Name and dest specified, + the destination is specified as a path. Attributes initialized to + default values. + - Addition of new link on image to another node. Name and dest + specified, the dest is set to a node previously added to image. + When written, the destination path is computed as the relative path + from the link to the destination node. Attributes initialized to + default values. + + 3.2.1.4 Special files (block devices, fifos...) + ----------------------------------------------- + + Special files are only written to image if RR extensions are enabled. + + - Addition of special files from filesystem. + - Creation of new special files on image. + + +3.2.2 Modification of contents +------------------------------ + + 3.2.2.1 Deletion of nodes + ------------------------- + + - Any node can be deleted. When a dir is remove, all its contents + are also removed. + + 3.2.2.2 Move + ------------ + + - Any node can be move to another dir.. + + 3.2.2.3 Rename + -------------- + + - You can change the name of any node + + 3.2.2.4 Change of POSIX attributes + ---------------------------------- + + - Following POSIX atts can be changed: owner (uid/gid), permissions, + timestamps. + +3.2.3 Bootable information +-------------------------- + + - Addition of a boot image to a volume. + - In most cases, the catalog and the boot image itself is added to the + iso tree. + - Alternatively, user can select to add a hidden images, i.e., images + that don't appear in the iso tree. + - Modification of boot image attributes: + - bootable flag + - load segment + - load size + - Automatic patching of isolinux images. User needs to set whether to apply + this. + - Reading of El-Torito info from multisession images. Modification of its + attributes. + - Removing of El-Torito images + + +3.2.4 Other operations +---------------------- + + 3.2.4.1 Set file sort weight + ----------------------------- + + - Any file can have a sort weight, that will determine the order in + which the files are written to image + + 3.2.4.2 Hidding of nodes + ------------------------ + + - Files can be hidden in the RR or Joliet tree + + +3.3 Filters +=========== + + [TODO] + + Support for: + - compression filter + - encryption filter + - external process filter + + + + + + + + + + + + + + diff --git a/libisofs/branches/thomas/doc/devel/4. Design b/libisofs/branches/thomas/doc/devel/4. Design new file mode 100644 index 00000000..e69de29b diff --git a/libisofs/branches/thomas/doc/devel/5. Implementation b/libisofs/branches/thomas/doc/devel/5. Implementation new file mode 100644 index 00000000..e69de29b diff --git a/libisofs/branches/thomas/doc/devel/README b/libisofs/branches/thomas/doc/devel/README new file mode 100644 index 00000000..75face44 --- /dev/null +++ b/libisofs/branches/thomas/doc/devel/README @@ -0,0 +1,7 @@ +Index +===== + +1. Overview +2. Features +3. Design +4. Implementation diff --git a/libisofs/branches/thomas/doc/devel/UML/BuilderSec.png b/libisofs/branches/thomas/doc/devel/UML/BuilderSec.png new file mode 100644 index 0000000000000000000000000000000000000000..a89203ee903b9abd36879bfe0d099c7f7d5f48eb GIT binary patch literal 25202 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 Wyv4nr=4OT5*VQsOUwGE>*8c&|P(C&Q literal 0 HcmV?d00001 diff --git a/libisofs/branches/thomas/doc/devel/UML/BuilderSec.violet b/libisofs/branches/thomas/doc/devel/UML/BuilderSec.violet new file mode 100644 index 00000000..c7e7fbf5 --- /dev/null +++ b/libisofs/branches/thomas/doc/devel/UML/BuilderSec.violet @@ -0,0 +1,821 @@ + + + + + + + + fs:Filesystem + + + + + + 160.0 + 73.0 + + + + + + + + + + file:FileSource + + + + + + + + + + + 192.0 + 209.0 + + + + + + + + 274.0 + 202.0 + + + + + + + + User + + + + + + 34.86475730998367 + 0.0 + + + + + + + + + + + + + b:TNBuilder + + + + + + + + + + + + + + + + + + + + + + + ftn:FileTN + + + + + + + + + + + + + + + + fs:FileStream + + + + + + + + + + + + + + + + + + + + + d:DirTreeNode + + + + + + + + + + + + + + + + + + + + 66.86475730998367 + 80.0 + + + + + + + + 539.756828460011 + 126.0 + + + + + + + + 651.0 + 0.0 + + + + + + + + 683.0 + 305.0 + + + + + + + + 571.756828460011 + 328.0 + + + + + + + + 306.0 + 351.0 + + + + + + + + 331.97135964975513 + 374.0 + + + + + + + + 363.97135964975513 + 457.0 + + + + + + + + 418.8259109283281 + 480.0 + + + + + + + + 363.97135964975513 + 563.0 + + + + + + + + 1. User wants to add a file to a dir in the iso node + + + + + + 143.89406091532933 + 16.868736840587744 + + + + + + + + 2. It creates the source filesystem and the + custom builder + + + + + + 317.51829970572646 + 74.92004824517142 + + + + + + + + 570.819415201306 + 142.7048538003265 + 0.0 + 0.0 + + + + + + + + + 570.819415201306 + 142.7048538003265 + + + + + + + + 218.81410916050066 + 114.16388304026121 + 0.0 + 0.0 + + + + + + + + + 218.81410916050066 + 114.16388304026121 + + + + + + + + 3. It gets the file from the filesystem +and add it to parent dir + + + + + + 379.1320632384976 + 217.4323774110454 + + + + + + + + 327.03195662574825 + 218.46075295682857 + 0.0 + 0.0 + + + + + + + + + 327.03195662574825 + 218.46075295682857 + + + + + + + + 4. The dir delegates in the builder. +5. The builder stat's the source file. In + this example it's a reg. file + + + + + + 767.038589176755 + 206.92203801047344 + + + + + + + + 694.4969551615891 + 312.7614712457156 + 0.0 + 0.0 + + + + + + + + + 694.4969551615891 + 312.7614712457156 + + + + + + + + 314.9148790283507 + 359.23720542189034 + 0.0 + 0.0 + + + + + + + + + 314.9148790283507 + 359.23720542189034 + + + + + + + + 6. The conversion is not needed, so +the builder just creates a FileTreeNode + + + + + + 762.2817607167442 + 335.3564064307673 + + + + + + + + 522.2869299335649 + 399.9594286575042 + 0.0 + 0.0 + + + + + + + + + 522.2869299335649 + 399.9594286575042 + + + + + + + + 7. Sets the attributes from source + + + + + + 774.1738318667714 + 413.8440760209469 + + + + + + + + 8 ...and a FileStream to read contents + from the FileSource + + + + + + 762.2817607167442 + 478.0612602310938 + + + + + + + + 534.9181953038541 + 453.1845675071054 + 0.0 + 0.0 + + + + + + + + + 534.9181953038541 + 453.1845675071054 + + + + + + + + 482.368075796364 + 524.8261757327898 + 0.0 + 0.0 + + + + + + + + + 482.368075796364 + 524.8261757327898 + + + + + + + + 9. Finally, the FileTreeNode is added to + the parent dir, and returned to the user + + + + + + 757.5249322567332 + 556.5489298212734 + + + + + + + + 689.7401267015781 + 614.8200784564067 + 0.0 + 0.0 + + + + + + + + + 689.7401267015781 + 614.8200784564067 + + + + + + + + 363.97135964975513 + 656.0 + + + + + + + + 10. The user can change any attribute + on the FileTreeNode + + + + + + 735.3910524340093 + 659.0235200658623 + + + + + + + + 373.3523804664971 + 666.0945878777277 + 0.0 + 0.0 + + + + + + + + + 373.3523804664971 + 666.0945878777277 + + + + + + + «create» + + + + + + + + + «create» + + + + + + + + + + + + file + + + + + + + + + add_file(file,b) + + + + + + + + + create_file(file) + + + + + + + + + lstat() + + + + + + + + + S_IFREG + + + + + + + + + «create» + + + + + + + + + set attributes + + + + + + + + + + + + + + + + + ftn + + + + + + + + + «create» + + + + + + + + + set stream (fs) + + + + + + + + + + + + + + + + + ftn + + + + + + + + + «create» + + + + + + + + + get(path) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + set_permission() + + + + + + + + + + + + + + + + + diff --git a/libisofs/branches/thomas/doc/devel/UML/builder.violet b/libisofs/branches/thomas/doc/devel/UML/builder.violet new file mode 100644 index 00000000..3dba303d --- /dev/null +++ b/libisofs/branches/thomas/doc/devel/UML/builder.violet @@ -0,0 +1,884 @@ + + + + + + + + get_root() +get_from_path(char *) + + + + + «interface» +Filesystem + + + + + + 159.04005306497305 + 489.4913761627291 + + + + + + + + MountedFilesytem + + + + + + 56.38849919058573 + 630.9884605487425 + + + + + + + + IsoImage + + + + + + 258.8562868808994 + 766.3563832139356 + + + + + + + + lstat() +read() +close() +open() +readdir() + + + + + «interface» +SourceFile + + + + + + 481.55979910778467 + 464.84194569982117 + + + + + + + + TarFile + + + + + + 176.58261638364775 + 701.0593878047844 + + + + + + + + read() +size() +open() +close() + + + + + «interface» +Stream + + + + + + 779.894860994415 + 340.36024540554786 + + + + + + + + FdStream + + + + + + 907.9433913981195 + 505.6600343909271 + + + + + + + + FileStream + + + + + + 646.2536512193697 + 514.5953286599063 + + + + + + + + TransformStream + + + + + + 774.6238447615127 + 513.9203093177954 + + + + + + + + create_file() +create_symlink() +create_dir() + + + + + «interface» +TreeNodeBuilder + + + + + + 469.51180397870456 + 119.92057094444797 + + + + + + + + TreeNode + + + + + + 777.5164467644091 + 137.7586776694888 + + + + + + + + File + + + + + + 776.3272396494064 + 235.11044131455145 + + + + + + + + Dir + + + + + + 899.7797731623193 + 242.40651557378732 + + + + + + + + Symlink + + + + + + 658.5957352641371 + 237.4888555445569 + + + + + + + + «interface» +FileBuilder + + + + + + 68.74900622278733 + 236.29964842955417 + + + + + + + + «interface» +DirBuilder + + + + + + 190.04813195306485 + 236.2996484295542 + + + + + + + + «interface» +SymlinkBuilder + + + + + + 304.21201499332614 + 236.29964842955417 + + + + + + + + POSIX inspired interface to files on different filesystems. +open/close act as a opendir/closedir if the file is a dir, +I think we don't need different function to open a dir. + + + + + + 154.8805850420814 + 333.9382491299707 + + + + + + + + "Sources" for file contents + + + + + + 587.0127806828101 + 358.755499461917 + + + + + + + + CutOutStream + + + + + + 845.6997102991108 + 605.2834046956852 + + + + + + + + FilterStream + + + + + + 721.2489168102784 + 605.2834046956852 + + + + + + + + «interface» +Filter + + + + + + 715.5920625607861 + 705.6925676241749 + + + + + + + + Used for arbitray streams, not related to +filesystem high-level idea. Also used for +files like fifos, that can't be added directly as +regulat files via de Builder, because its size is +unknown. The need to be added as new_files +on image + + + + + + 906.5108934811542 + 328.0975464705584 + + + + + + + + Create the user-specified TreeNode from the +user-specified source. If the source type differs the +TreeNode type the use wants to create, it makes +the needed conversion, if possible. Each builder +implementation can do different conversions. + + + + + + 654.7808793787427 + 20.610173055266337 + + + + + + + + Together with the SourceFile encapsulates the +access to a given filesystem and abstracts it to +a POSIX interface. + + + + + + 20.610173055266422 + 403.050865276332 + + + + + + + + The TreeNodeBuilder can be created with +the combination of different interfaces for +each factory method + + + + + + 149.90663761154804 + 57.982756057296896 + + + + + + + + MountedSrc + + + + + + 373.3523804664971 + 634.9818895055197 + + + + + + + + TarSrc + + + + + + 479.4183976444791 + 695.7930726875627 + + + + + + + + IsoSrc + + + + + + 578.4133470105959 + 773.574818618083 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{create}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libisofs/branches/thomas/doc/devel/UML/builder.violet.png b/libisofs/branches/thomas/doc/devel/UML/builder.violet.png new file mode 100644 index 0000000000000000000000000000000000000000..6884fe9ab24b1af2531453fae6ce2c58e52402a5 GIT binary patch literal 34098 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
oOVz;_72ai1A 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 + + + + + + + + + «interface» +BurnSource + + + + + + libburn + + + + + 600.0 + 50.0 + + + + + + + + 612.4370214406964 + 81.3237865499184 + + + + + + + + Ecma119Source + + + + + + 604.1172144213822 + 242.59825146055505 + + + + + + + + WriterState + + + + + + 861.1034292438676 + 244.31119796826698 + + + + + + + + FilesWriterSt + + + + + + 984.2531292100068 + 359.95094883087904 + + + + + + + + VolDescWriterSt + + + + + + 717.2457054234224 + 357.4185959653686 + + + + + + + + DirInfoWriterSt + + + + + + 854.6043620021998 + 355.85097462036043 + + + + + + + + Ecma119Image + + + + + + 392.3294860655768 + 240.39714472372754 + + + + + + + + The context data for image burn sources, contains +references to the tree, creation options... + + + + + + 261.45257180386454 + 85.80450046553075 + + + + + + + + Ecma119Node + + + + + + 291.8219414851778 + 612.806815288254 + + + + + + + + init() +write_voldesc() +write_dir_info() + + + + + «interface» +ImageWriter + + + + + + 401.9520048709197 + 344.8700633507891 + + + + + + + + JolietNode + + + + + + 409.0872475609359 + 614.8200784564067 + + + + + + + + FileRegistry + + + + + + 718.2810974616434 + 459.0339463910502 + + + + + + + + Ecma119Writer + + + + + + 273.51763645062584 + 489.95333138112096 + + + + + + + + JolietWriter + + + + + + 404.3304191009253 + 485.1965029211101 + + + + + + + + ElToritoWriter + + + + + + 512.5482665661723 + 485.19650292111 + + + + + + + + size : off_t +block : uint32_t + + + + + IsoFile + + + + + + 720.659511691649 + 568.4410009713001 + + + + + + + + «interface» +Stream + + + + + + 909.7434429770816 + 580.3330721213274 + + + + + + + + ImageWriter goal is to encapsulate the output +of image blocks related to one "specification", +i.e. we will have an implementation for ECMA-119/RR, +other for Joliet... + +Note that having different implementations for things that +need to be written in the same block has no utility, i.e. RR +and ECMA-119 must share its Writer implementation. + +Note also that while this provides considerable encapsulation +the provided abstraction is really partial: In the relation +with WriterState the encapsulation is quite good, each +concrete state know what method to call here (notice that +this interface is also quite coupled with state). However, +with respect to Ecma119Image the abstration only refers +to implementation, as the Ecma119Image needs to know +about the ImageWriter implementations. This can't be +avoided, as Ecma119Image has to be responsible of the +instantation in the correct order and following the user +needs. + + + + + + + + 2.3784142300054896 + 160.54296052536733 + + + + + + + + The files are registered into the file registry, that will take care +about the written of content. + + + + + + 286.59891471565567 + 708.7674405416217 + + + + + + + + Each state will invoque property method in each of +the ImageWriters. Some writers can return without +outputting anything. It is possible that when dealing +with UDF or other specification we would need new +states and methods in ImageWriter + + + + + + 765.8493820617523 + 132.001989765302 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libisofs/branches/thomas/doc/devel/UML/burn_source.png b/libisofs/branches/thomas/doc/devel/UML/burn_source.png new file mode 100644 index 0000000000000000000000000000000000000000..5786ab29cc075491d6ee6f819d4624c1dede6ca2 GIT binary patch literal 35652 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 VL!|wOR^VH}q$T9UbDrwG{XaLw+CcyS literal 0 HcmV?d00001 diff --git a/libisofs/branches/thomas/doc/devel/UML/eltorito.violet b/libisofs/branches/thomas/doc/devel/UML/eltorito.violet new file mode 100644 index 00000000..b8a7d01c --- /dev/null +++ b/libisofs/branches/thomas/doc/devel/UML/eltorito.violet @@ -0,0 +1,552 @@ + + + + + + + + Volume + + + + + + 479.2699858975891 + 226.94112549695433 + + + + + + + + block : uint32_t + + + + + ElToritoCatalog + + + + + + 472.58578643762684 + 344.73506473629425 + + + + + + + + bootable : bool +type : enum +partition_type : enum +load_seg : uint16 +load_size : uint16 +patch_isolinux : bool +block: uint32_t + + + + + BootImage + + + + + + 470.4142135623731 + 487.3919189857866 + + + + + + + + In a future we can support several boot +images + + + + + + 251.63542622468316 + 429.69343417595167 + + + + + + + + img : boolean + + + + + BootNode + + + + + + 193.07106781186545 + 334.49242404917493 + + + + + + + + + + TreeNode + + + + + + iso_tree + + + + + 180.0 + 40.0 + + + + + + + + 193.0 + 69.0 + + + + + + + + The img field is an implementation detail, used +to distinguish between the catalog node and the image +node. This is needed when the image is written. + + + + + + 57.81118318204312 + 584.0458146424488 + + + + + + + + The support for growing or modify El-Torito images +is really hard to implement. The reason: when the +image is hidden, we don't know its size, so the best we +can do is just refer to the old image. When modify, all +we can do may be wrong. + + + + + + 748.978906441031 + 574.8973495522459 + + + + + + + + The block in both Catalog and BootImage is needed +for multissession images + + + + + + 629.3242465083424 + 441.1316647878586 + + + + + + + + File + + + + + + 188.09040379562163 + 172.5340546095176 + + + + + + + + CatalogStream + + + + + + 851.105100475371 + 283.5127233261827 + + + + + + + + FileStream + + + + + + 743.4055403867466 + 284.4253525880894 + + + + + + + + TransformStream + + + + + + 958.5987801403015 + 279.8322618091961 + + + + + + + + «interface» +Stream + + + + + + 847.6728065449973 + 157.05765855361264 + + + + + + + + IsoLinuxPatch + + + + + + 968.73629022557 + 384.6660889654818 + + + + + + + + Generates the content of the catalog on-the-fly + + + + + + 517.6021638285529 + 107.48023074035522 + + + + + + + + To apply the needed patch to isolinux +images + + + + + + 923.4814562296309 + 509.1168824543143 + + + + + + + + + + + + + 1 image + + + + + + + + + + + + + + + + + + + + + + + 0..1 boot_cat + + + + + + + + + + + + + + + 0..1 node + + + + + + + + + + + + + + + + + + + + 0..1 node + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libisofs/branches/thomas/doc/devel/UML/eltorito.violet.png b/libisofs/branches/thomas/doc/devel/UML/eltorito.violet.png new file mode 100644 index 0000000000000000000000000000000000000000..adc195c25ed2dab2a308a7c84e068f0b5ba26ee5 GIT binary patch literal 24861 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 literal 0 HcmV?d00001 diff --git a/libisofs/branches/thomas/doc/devel/UML/iso_tree.violet b/libisofs/branches/thomas/doc/devel/UML/iso_tree.violet new file mode 100644 index 00000000..a3fa2268 --- /dev/null +++ b/libisofs/branches/thomas/doc/devel/UML/iso_tree.violet @@ -0,0 +1,748 @@ + + + + + + + + volume_id : char* +publisher_id : char* +data_preparer_id : char* +system_id : char* +application_id : char* +copyright_file_id : char* +abstract_file_id : char* +biblio_file_id : char* + + + + + Volume + + + + + + 1160.4799402311673 + 240.649943764645 + + + + + + + + sort_weight : int +block : uint32_t + + + + + File + + + + + + 687.5479565719912 + 269.2931470368318 + + + + + + + + name : char * +attribs : struct stat +hidden : enum + + + + + TreeNode + + + + + + 706.83671056434 + 108.4726745515399 + + + + + + + + add(XXX) +remove(Node) +children() + + + + + Directory + + + + + + 986.1687535943008 + 267.29314703683184 + + + + + + + + dest : char* + + + + + Symlink + + + + + + 571.9364350336367 + 273.31078127658077 + + + + + + + + Special + + + + + + 813.0651280884073 + 272.20749521231266 + + + + + + + + name : char* + + + + + <<static>>new(id) +<<static>>read(src, opts) +create() +grow() + + + + + Image + + + + + + 1149.1980515339465 + 455.5218613006981 + + + + + + + + In addition to the dest as a path, it could +be a good idea to have a ref to tree node. +That way we can compute the dest on creation +time, and thus links to files on image are also valid +after moving or renaming those files + + + + + + 322.02220861890066 + 362.2044136147912 + + + + + + + + Image is a context for the creation of images. Its "static" +methods, new() and read() are used to create a new +image context, either from scratch or from an existing +image (for example, a ms disc). The methods create() and +grow() return an BurnSource suitable for libburn. +create() writes a full image, grow() only add to the image +the new files, thus it is suitable for a new session + + + + + + + 1212.7956394939486 + 697.0920982847697 + + + + + + + + Ecma119Source + + + + + + 1423.5617211564486 + 483.61244144432396 + + + + + + + + + + «interface» +BurnSource + + + + + + Libburn + + + + + 1420.0 + 280.0 + + + + + + + + 1431.4906533445824 + 311.35760744838467 + + + + + + + + Class diagram for the public tree. Note that getters and setters are not shown, +to improve readability. Note also that not all the attributes will have public getters +or/and setters. +El-Torito related information is shown in another diagram. +We don't show the several functions in Dir to manage the tree. + + + + + + 290.59037712396525 + 9.859316379054544 + + + + + + + + «interface» +DataSource + + + + + + 1192.781692587207 + 608.8954677283948 + + + + + + + + + + «interface» +Filters + + + + + + filters + + + + + 260.0 + 710.0 + + + + + + + + 265.45434264405947 + 743.9994422711634 + + + + + + + + TransformStream + + + + + + 486.9335577265969 + 640.636302316303 + + + + + + + + CutOutStream + + + + + + 555.9916340674516 + 750.220757440409 + + + + + + + + get_size() +read() +open() +close() +is_repeatable() + + + + + «interface» +Stream + + + + + + 688.5487814157467 + 437.25152600545294 + + + + + + + + FdStream + + + + + + 680.6673668471356 + 637.245696021424 + + + + + + + + FileStream + + + + + + 828.9404615480411 + 642.40096597045 + + + + + + + + FilteredStream + + + + + + 428.449880813367 + 747.5389646099015 + + + + + + + + «interface» +SourceFile + + + + + + 1000.6667341519202 + 639.0812755928229 + + + + + + + + For files, we need to know whethe they come +from a previous session. That's the purpose of +the block field + + + + + + 818.829652614022 + 414.36457377531684 + + + + + + + + + + + + + 1 volume + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {create} + + + + + + + + + + + + 0..1 + + + + + + + + + + + + + + + + + + * children + + + + + + + + + + + + + + + + + + 1 root + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 parent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 src + + + + + + + + + + + + diff --git a/libisofs/branches/thomas/doc/devel/UML/iso_tree.violet.png b/libisofs/branches/thomas/doc/devel/UML/iso_tree.violet.png new file mode 100644 index 0000000000000000000000000000000000000000..d445b4fe0232237f34f353ecb3015f3e60866161 GIT binary patch literal 38189 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 literal 0 HcmV?d00001 diff --git a/libisofs/branches/thomas/doc/devel/UML/nglibisofs.violet b/libisofs/branches/thomas/doc/devel/UML/nglibisofs.violet new file mode 100644 index 00000000..fd6a247f --- /dev/null +++ b/libisofs/branches/thomas/doc/devel/UML/nglibisofs.violet @@ -0,0 +1,1059 @@ + + + + + + + + addFilter(Filter) + + + + + Image + + + + + + 1110.8240579870107 + 412.38305701571016 + + + + + + + + Volume + + + + + + 746.3823937606093 + 414.32909259072375 + + + + + + + + TreeNode + + + + + + 625.8940609153292 + 195.78347904495376 + + + + + + + + File + + + + + + 497.8940609153292 + 296.78347904495376 + + + + + + + + dest + + + + + Symlink + + + + + + 620.8940609153292 + 296.78347904495376 + + + + + + + + Directory + + + + + + 754.8940609153292 + 301.78347904495376 + + + + + + + + «interface» + +FileSource + + + + + + 567.7128002861876 + 674.7337520453865 + + + + + + + + FilterFileSource + + + + + + 404.71280028618764 + 774.7337520453866 + + + + + + + + «interface» +Filter + + + + + + 254.78439895092293 + 770.0234642237775 + + + + + + + + LocalFileSource + + + + + + 556.36965453568 + 774.2484706711477 + + + + + + + + PreviousImageSource + + + + + + 709.0702627569951 + 767.6450499937722 + + + + + + + + ExternalAppFilter + + + + + + 110.5229299160843 + 923.5005293319075 + + + + + + + + EncryptionFilter + + + + + + 239.49694258624206 + 916.3652866418913 + + + + + + + + CompressionFilter + + + + + + 371.4989323515441 + 917.5544937568939 + + + + + + + + SplittedFile + + + + + + 287.78812183065844 + 413.84407602094683 + + + + + + + + Special + + + + + + 386.0027189390628 + 302.28361365806154 + + + + + + + + DataSource + + + + + + 731.4111571155465 + 901.4839567978983 + + + + + + + + «interface» +BurnSource + + + + + + 1335.6119487863941 + 398.7730655710078 + + + + + + + + ... + + + + + + 1477.8641028983907 + 783.298732945339 + + + + + + + + PVDWriter + + + + + + 1198.964607961779 + 781.9555871948312 + + + + + + + + WriterState + + + + + + 1336.8762962072105 + 661.712946507712 + + + + + + + + + + Libburn + + + + + 1330.0 + 370.0 + + + + + + + + Ecma119Image + + + + + + 1095.4640655004926 + 540.6103494032706 + + + + + + + + Ecma119Source + + + + + + 1326.9200188086058 + 535.6052987693873 + + + + + + + + Ecma119Node + + + + + + 881.981844994835 + 577.6636135370297 + + + + + + + + DirectoryInfoWriter + + + + + + 1316.10674358551 + 783.9555871948312 + + + + + + + + LocalFile + + + + + + 396.0059692959062 + 407.89804044593336 + + + + + + + + FilteredFile + + + + + + 505.41302387615644 + 412.6548689059442 + + + + + + + + PrevImgFile + + + + + + 617.1984926864122 + 410.2764546759387 + + + + + + + + Ecma119File + + + + + + 743.3186217548513 + 674.795410727119 + + + + + + + + Ecma119Symlink + + + + + + 874.2922359743702 + 669.9419255760395 + + + + + + + + Ecma119Dir + + + + + + 1037.9535936976897 + 672.3203398060449 + + + + + + + + FileSourceRegistry + + + + + + 550.129075763134 + 551.543289325507 + + + + + + + + Base object for all interaction with user. +Represents a context for image creation +and manipulation. + + + + + + 1042.275395468971 + 308.29855659733465 + + + + + + + + Registry to ensure the same file is only +written once to the image + + + + + + 271.5290039756343 + 521.8448045156721 + + + + + + + + The context data for image burn sources, contains +references to the tree, creation options... + + + + + + 770.7463914933367 + 497.80317395532944 + + + + + + + + A filter to be applied to file contents. A single filter +can be used to several files. + + + + + + 151.32085117392114 + 668.9230150024738 + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * children + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 root + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [create] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libisofs/branches/thomas/doc/devel/UML/stream.violet b/libisofs/branches/thomas/doc/devel/UML/stream.violet new file mode 100644 index 00000000..d488940d --- /dev/null +++ b/libisofs/branches/thomas/doc/devel/UML/stream.violet @@ -0,0 +1,492 @@ + + + + + + + + TransformStream + + + + + + 374.71280028618764 + 246.7337520453866 + + + + + + + + get_size() +read() +open() +close() +is_repeatable() + + + + + «interface» +Stream + + + + + + 576.3280239753375 + 43.34897573453627 + + + + + + + + FileStream + + + + + + 741.9465965652432 + 246.8166228690261 + + + + + + + + + + CompressionFilter + + + + + + + + + EncryptionFilter + + + + + + + + + ExtAppFilter + + + + + + + + + filter(in, out) + + + + + «interface» +Filter + + + + + + Filters + + + + + 270.0 + 480.0 + + + + + + + + A Stream to read data from an abstract +file represented by a SourceFile + + + + + + 781.6101730552666 + 137.2161620284267 + + + + + + + + A stream to get data from an arbitrary file +descritor. size must be know in advance. + + + + + + 580.8730162779191 + 392.3137084989848 + + + + + + + + fd : int +size : off_t + + + + + FdStream + + + + + + 565.61818228198 + 253.24264068711926 + + + + + + + + 281.2426406871193 + 620.6274169979695 + + + + + + + + 429.51546936508925 + 624.9910026589843 + + + + + + + + 568.2426406871186 + 624.6274169979695 + + + + + + + + A Filter do a tranformation on a stream of data. +The main difference with TransformSources is that +a Filter can be applied to several sources. +NOTES: +- filter() method still to define +- A filter_changes_size() method can be useful + + + + + + + 724.6274169979696 + 510.3015151901651 + + + + + + + + FilteredStream + + + + + + 439.0 + 357.0 + + + + + + + + size : off_t +lba: off_t + + + + + CutOutStream + + + + + + 321.0 + 358.0 + + + + + + + + This can be implemented as a Filter, but +it has no sense to have the same cut out +filter to several sources, so this is a better +place. + + + + + + 67.0 + 276.0 + + + + + + + + A stream that applies some transformation +to the contents of another stream. + + + + + + 122.0 + 183.0 + + + + + + + + 437.57046683437824 + 509.23933115391503 + + + + + + + + «interface» +SourceFile + + + + + + 920.6530291048848 + 248.90158697766475 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libisofs/branches/thomas/doc/devel/UML/stream.violet.png b/libisofs/branches/thomas/doc/devel/UML/stream.violet.png new file mode 100644 index 0000000000000000000000000000000000000000..f5215a50ae42b31bb7da329d10df18921a868c7c GIT binary patch literal 21694 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libisofs/branches/thomas/doc/devel/cookbook/ISO 9660-1999 b/libisofs/branches/thomas/doc/devel/cookbook/ISO 9660-1999 new file mode 100644 index 00000000..4b813b7b --- /dev/null +++ b/libisofs/branches/thomas/doc/devel/cookbook/ISO 9660-1999 @@ -0,0 +1,119 @@ +=============================================================================== + ISO/IEC 9660:1999 Cookbook +=============================================================================== + +Creation date: 2008-Jan-14 +Author: Vreixo Formoso +_______________________________________________________________________________ + +Contents: +--------- + +1. References +2. General +3. Features +4. Implementation +5. Known implementation bugs and specification ambiguities/problems + + +------------------------------------------------------------------------------- +1. References: + +ISO/IEC DIS 9660:1999(E) "Information processing. Volume and file structure of + CD­-ROM for Information Interchange" + + +------------------------------------------------------------------------------- +2. General + +ISO 9660:1999, also known as ISO-9660 version 2 is an update of the old +ISO 9660:1988 standard for writing data images for CD. + +In the same way Joliet does, it is based on a Secondary Volume Descriptor (that +is called Enhanced Volume Descriptor), that provides a second tree where the +new file information is recorded. + +------------------------------------------------------------------------------- +3. Features + +It makes some improvements with respect to ECMA-119, mainly related to relax +the constraints imposed by its predecessor. + +- It removes the limit to the deep of the directory hierarchy (6.8.2.1). +However, it still keep a limit to the path length, of 255 characters as in +ECMA-119. + +- File names don't need the version number (;1) anymore, and the "." and ";", +used as SEPARATORS for extension and version number, have no special meaning +now. + +- The file name max length is incremented to 207 bytes. + +- The file name is not restricted to d-characters. + +------------------------------------------------------------------------------- +4. Implementation + +ISO 9660:1999 is very similar to old ISO 9660:1988 (ECMA-119). It needs two +tree hierarchies: one, identified by the Primary Volume Descriptor, is recorded +in the same way that an ECMA-119 structure. + +The second structure is identified by a Enhanced Volume Descriptor (8.5). The +structure is exactly like defined in ECMA-119, with the exceptions named above. + +Thus, to write an ISO 9660:1999: + +- First 16 blocks are set to 0. +- Block 16 identifies a PVD (8.4), associated with a directory structure +written following ECMA-119. +- It is needed a Enhanced Volume descriptor to describe the additional +structure. It is much like a SVD, with version number set to 2 to identify +this new version. +- We can also write boot records (El-Torito) and additional SVD (Joliet). +- We write a Volume Descriptor Set Terminator (8.3) +- We write directory structure and path tables (L and M) for both ECMA-119 +tree and enhanced tree. Path table record and directory record format is +the same in both structures. However, ECMA-119 is constrained by the usual +restrictions. +- And write the contents of the files. + +Interchange levels 1, 2 and 3 are also defined. For PVD tree, they have the +same meaning as in ECMA-119. For EVD tree, in levels 1 and 2 files are +restricted to one file section (i.e., 4 GB filesize limit). In level 3 we can +have more than one section per file. Level 1 does not impose other +restrictions than that in the EVD tree. + +It seems that both El-Torito and Joliet can coexist in a ISO 9660:1999 image. +However, Joliet has no utility at all in this kind of images, as it has no +benefit over ISO 9660:1999, and it is more restrictive in filename length. + +------------------------------------------------------------------------------- +5. Known implementation bugs and specification ambiguities/problems + +- While the specification clearly states that the tree speficied by the Primary +Volume Descriptor should remain compatible with ISO-9660 (ECMA-119), i.e., it +should be constrained by ECMA-119 restrictions, some image generation +applications out there just make both Primary and Enhanced Volume Descriptors +to point to the same directory structure. That is a specification violation, as +for a) the directory hierarchy specified in the Primary Volume Descriptor +doesn't follow the restrictions specified in the specs, and b) the same +directories are part of two different hiearchies (6.8.3 "A directory shall not +be a part of more than one Directory Hierarchy."). +Thus, we should keep two trees as we do with Joliet. Or are there strong +reasons against this? + +- It's not very clear what characters are allowed for files and dir names. For +the tree identified in the Enhanced Volume Descriptor, it seems that a +"sequence of characters rather than d-characters or d1-characters" is allowed. +It also seems that the charset is determined by the escape sequence in the +EVD. Anyway, leaving escape sequence to 0 and use any user-specified sequence +(such as UTF-8) seems a good solution and is what many other applications do. +Linux correctly mounts the images in this case. + +- It is not clear if RR extensions are allowed in the tree identified by the +Enhanced Volume Descriptor. However, it seems not a good idea. With 207 bytes +filenames and XA extensions, there is no place for RR entries in the directory +records of the enhanced tree. In my opinion, RR extension should be attached to +the ECMA-119 tree that must also be written to image. + + diff --git a/libisofs/branches/thomas/doc/doxygen.conf.in b/libisofs/branches/thomas/doc/doxygen.conf.in new file mode 100644 index 00000000..fe1864fe --- /dev/null +++ b/libisofs/branches/thomas/doc/doxygen.conf.in @@ -0,0 +1,1298 @@ +# Doxyfile 1.5.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file that +# follow. The default is UTF-8 which is also the encoding used for all text before +# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into +# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list +# of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = @PACKAGE_NAME@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = @top_srcdir@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = YES + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be extracted +# and appear in the documentation as a namespace called 'anonymous_namespace{file}', +# where file will be replaced with the base name of the file that contains the anonymous +# namespace. By default anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text " + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = libisofs \ + doc + +# This tag can be used to specify the character encoding of the source files that +# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default +# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. +# See http://www.gnu.org/software/libiconv for the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = libisofs.h \ + comments + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the output. +# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, +# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = test + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH +# then you must also enable this option. If you don't then doxygen will produce +# a warning and turn it on anyway + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = OB \ + OTK \ + _ + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = doc/html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 200 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = letter + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DOXYGEN + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to +# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to +# specify the directory where the mscgen tool resides. If left empty the tool is assumed to +# be found in the default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the number +# of direct children of the root node in a graph is already larger than +# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/libisofs/branches/thomas/libisofs-1.pc.in b/libisofs/branches/thomas/libisofs-1.pc.in new file mode 100644 index 00000000..4cf18c17 --- /dev/null +++ b/libisofs/branches/thomas/libisofs-1.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libisofs +Description: ISO9660 filesystem creation library +Version: @VERSION@ +Requires: +Libs: -L${libdir} -lisofs +Cflags: -I${includedir}/libisofs diff --git a/libisofs/branches/thomas/libisofs/buffer.c b/libisofs/branches/thomas/libisofs/buffer.c new file mode 100644 index 00000000..c59bad13 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/buffer.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/* + * Synchronized ring buffer, works with a writer thread and a read thread. + * + * TODO #00010 : optimize ring buffer + * - write/read at the end of buffer requires a second mutex_lock, even if + * there's enought space/data at the beginning + * - pre-buffer for writes < BLOCK_SIZE + * + */ + +#include "buffer.h" +#include "libburn/libburn.h" +#include "ecma119.h" + +#include +#include + +#ifndef MIN +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +struct iso_ring_buffer +{ + uint8_t *buf; + + /* + * Max number of bytes in buffer + */ + size_t cap; + + /* + * Number of bytes available. + */ + size_t size; + + /* position for reading and writing, offset from buf */ + size_t rpos; + size_t wpos; + + /* + * flags to report if read or writer threads ends execution + * 0 not finished, 1 finished ok, 2 finish with error + */ + unsigned int rend :2; + unsigned int wend :2; + + /* just for statistical purposes */ + unsigned int times_full; + unsigned int times_empty; + + pthread_mutex_t mutex; + pthread_cond_t empty; + pthread_cond_t full; +}; + +/** + * Create a new buffer. + * + * The created buffer should be freed with iso_ring_buffer_free() + * + * @param size + * Number of blocks in buffer. You should supply a number >= 32, otherwise + * size will be ignored and 32 will be used by default, which leads to a + * 64 KiB buffer. + * @return + * 1 success, < 0 error + */ +int iso_ring_buffer_new(size_t size, IsoRingBuffer **rbuf) +{ + IsoRingBuffer *buffer; + + if (rbuf == NULL) { + return ISO_NULL_POINTER; + } + + buffer = malloc(sizeof(IsoRingBuffer)); + if (buffer == NULL) { + return ISO_OUT_OF_MEM; + } + + buffer->cap = (size > 32 ? size : 32) * BLOCK_SIZE; + buffer->buf = malloc(buffer->cap); + if (buffer->buf == NULL) { + free(buffer); + return ISO_OUT_OF_MEM; + } + + buffer->size = 0; + buffer->wpos = 0; + buffer->rpos = 0; + + buffer->times_full = 0; + buffer->times_empty = 0; + + buffer->rend = buffer->wend = 0; + + /* init mutex and waiting queues */ + pthread_mutex_init(&buffer->mutex, NULL); + pthread_cond_init(&buffer->empty, NULL); + pthread_cond_init(&buffer->full, NULL); + + *rbuf = buffer; + return ISO_SUCCESS; +} + +void iso_ring_buffer_free(IsoRingBuffer *buf) +{ + if (buf == NULL) { + return; + } + free(buf->buf); + pthread_mutex_destroy(&buf->mutex); + pthread_cond_destroy(&buf->empty); + pthread_cond_destroy(&buf->full); + free(buf); +} + +/** + * Write count bytes into buffer. It blocks until all bytes where written or + * reader close the buffer. + * + * @param buf + * the buffer + * @param data + * pointer to a memory region of at least coun bytes, from which data + * will be read. + * @param + * Number of bytes to write + * @return + * 1 succes, 0 read finished, < 0 error + */ +int iso_ring_buffer_write(IsoRingBuffer *buf, uint8_t *data, size_t count) +{ + size_t len; + int bytes_write = 0; + + if (buf == NULL || data == NULL) { + return ISO_NULL_POINTER; + } + + while (bytes_write < count) { + + pthread_mutex_lock(&buf->mutex); + + while (buf->size == buf->cap) { + + /* + * Note. There's only a writer, so we have no race conditions. + * Thus, the while(buf->size == buf->cap) is used here + * only to propertly detect the reader has been cancelled + */ + + if (buf->rend) { + /* the read procces has been finished */ + pthread_mutex_unlock(&buf->mutex); + return 0; + } + buf->times_full++; + /* wait until space available */ + pthread_cond_wait(&buf->full, &buf->mutex); + } + + len = MIN(count - bytes_write, buf->cap - buf->size); + if (buf->wpos + len > buf->cap) { + len = buf->cap - buf->wpos; + } + memcpy(buf->buf + buf->wpos, data + bytes_write, len); + buf->wpos = (buf->wpos + len) % (buf->cap); + bytes_write += len; + buf->size += len; + + /* wake up reader */ + pthread_cond_signal(&buf->empty); + pthread_mutex_unlock(&buf->mutex); + } + return ISO_SUCCESS; +} + +/** + * Read count bytes from the buffer into dest. It blocks until the desired + * bytes has been read. If the writer finishes before outputting enought + * bytes, 0 (EOF) is returned, the number of bytes already read remains + * unknown. + * + * @return + * 1 success, 0 EOF, < 0 error + */ +int iso_ring_buffer_read(IsoRingBuffer *buf, uint8_t *dest, size_t count) +{ + size_t len; + int bytes_read = 0; + + if (buf == NULL || dest == NULL) { + return ISO_NULL_POINTER; + } + + while (bytes_read < count) { + pthread_mutex_lock(&buf->mutex); + + while (buf->size == 0) { + /* + * Note. There's only a reader, so we have no race conditions. + * Thus, the while(buf->size == 0) is used here just to ensure + * a reader detects the EOF propertly if the writer has been + * canceled while the reader was waiting + */ + + if (buf->wend) { + /* the writer procces has been finished */ + pthread_mutex_unlock(&buf->mutex); + return 0; /* EOF */ + } + buf->times_empty++; + /* wait until data available */ + pthread_cond_wait(&buf->empty, &buf->mutex); + } + + len = MIN(count - bytes_read, buf->size); + if (buf->rpos + len > buf->cap) { + len = buf->cap - buf->rpos; + } + memcpy(dest + bytes_read, buf->buf + buf->rpos, len); + buf->rpos = (buf->rpos + len) % (buf->cap); + bytes_read += len; + buf->size -= len; + + /* wake up the writer */ + pthread_cond_signal(&buf->full); + pthread_mutex_unlock(&buf->mutex); + } + return ISO_SUCCESS; +} + +void iso_ring_buffer_writer_close(IsoRingBuffer *buf, int error) +{ + pthread_mutex_lock(&buf->mutex); + buf->wend = error ? 2 : 1; + + /* ensure no reader is waiting */ + pthread_cond_signal(&buf->empty); + pthread_mutex_unlock(&buf->mutex); +} + +void iso_ring_buffer_reader_close(IsoRingBuffer *buf, int error) +{ + pthread_mutex_lock(&buf->mutex); + + if (buf->rend) { + /* reader already closed */ + pthread_mutex_unlock(&buf->mutex); + return; + } + + buf->rend = error ? 2 : 1; + + /* ensure no writer is waiting */ + pthread_cond_signal(&buf->full); + pthread_mutex_unlock(&buf->mutex); +} + +/** + * Get the times the buffer was full. + */ +unsigned int iso_ring_buffer_get_times_full(IsoRingBuffer *buf) +{ + return buf->times_full; +} + +/** + * Get the times the buffer was empty. + */ +unsigned int iso_ring_buffer_get_times_empty(IsoRingBuffer *buf) +{ + return buf->times_empty; +} + + +/** + * Get the status of the buffer used by a burn_source. + * + * @param b + * A burn_source previously obtained with + * iso_image_create_burn_source(). + * @param size + * Will be filled with the total size of the buffer, in bytes + * @param free_bytes + * Will be filled with the bytes currently available in buffer + * @return + * < 0 error, > 0 state: + * 1="active" : input and consumption are active + * 2="ending" : input has ended without error + * 3="failing" : input had error and ended, + * 5="abandoned" : consumption has ended prematurely + * 6="ended" : consumption has ended without input error + * 7="aborted" : consumption has ended after input error + */ +int iso_ring_buffer_get_status(struct burn_source *b, size_t *size, + size_t *free_bytes) +{ + int ret; + IsoRingBuffer *buf; + if (b == NULL) { + return ISO_NULL_POINTER; + } + buf = ((Ecma119Image*)(b->data))->buffer; + + /* get mutex */ + pthread_mutex_lock(&buf->mutex); + if (size) { + *size = buf->cap; + } + if (free_bytes) { + *free_bytes = buf->cap - buf->size; + } + + ret = (buf->rend ? 4 : 0) + (buf->wend + 1); + + pthread_mutex_unlock(&buf->mutex); + return ret; +} diff --git a/libisofs/branches/thomas/libisofs/buffer.h b/libisofs/branches/thomas/libisofs/buffer.h new file mode 100644 index 00000000..c98e06cb --- /dev/null +++ b/libisofs/branches/thomas/libisofs/buffer.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#ifndef LIBISO_BUFFER_H_ +#define LIBISO_BUFFER_H_ + +#include +#include + +#define BLOCK_SIZE 2048 + +typedef struct iso_ring_buffer IsoRingBuffer; + +/** + * Create a new buffer. + * + * The created buffer should be freed with iso_ring_buffer_free() + * + * @param size + * Number of blocks in buffer. You should supply a number >= 32, otherwise + * size will be ignored and 32 will be used by default, which leads to a + * 64 KiB buffer. + * @return + * 1 success, < 0 error + */ +int iso_ring_buffer_new(size_t size, IsoRingBuffer **rbuf); + +/** + * Free a given buffer + */ +void iso_ring_buffer_free(IsoRingBuffer *buf); + +/** + * Write count bytes into buffer. It blocks until all bytes where written or + * reader close the buffer. + * + * @param buf + * the buffer + * @param data + * pointer to a memory region of at least coun bytes, from which data + * will be read. + * @param + * Number of bytes to write + * @return + * 1 succes, 0 read finished, < 0 error + */ +int iso_ring_buffer_write(IsoRingBuffer *buf, uint8_t *data, size_t count); + +/** + * Read count bytes from the buffer into dest. It blocks until the desired + * bytes has been read. If the writer finishes before outputting enought + * bytes, 0 (EOF) is returned, the number of bytes already read remains + * unknown. + * + * @return + * 1 success, 0 EOF, < 0 error + */ +int iso_ring_buffer_read(IsoRingBuffer *buf, uint8_t *dest, size_t count); + +/** + * Close the buffer (to be called by the writer). + * You have to explicity close the buffer when you don't have more data to + * write, otherwise reader will be waiting forever. + * + * @param error + * Writer has finished prematurely due to an error + */ +void iso_ring_buffer_writer_close(IsoRingBuffer *buf, int error); + +/** + * Close the buffer (to be called by the reader). + * If for any reason you don't want to read more data, you need to call this + * to let the writer thread finish. + * + * @param error + * Reader has finished prematurely due to an error + */ +void iso_ring_buffer_reader_close(IsoRingBuffer *buf, int error); + +/** + * Get the times the buffer was full. + */ +unsigned int iso_ring_buffer_get_times_full(IsoRingBuffer *buf); + +/** + * Get the times the buffer was empty. + */ +unsigned int iso_ring_buffer_get_times_empty(IsoRingBuffer *buf); + +#endif /*LIBISO_BUFFER_H_*/ diff --git a/libisofs/branches/thomas/libisofs/builder.c b/libisofs/branches/thomas/libisofs/builder.c new file mode 100644 index 00000000..9dbea875 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/builder.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "builder.h" +#include "node.h" +#include "fsource.h" + +#include +#include +#include + +void iso_node_builder_ref(IsoNodeBuilder *builder) +{ + ++builder->refcount; +} + +void iso_node_builder_unref(IsoNodeBuilder *builder) +{ + if (--builder->refcount == 0) { + /* free private data */ + builder->free(builder); + free(builder); + } +} + +static +int default_create_file(IsoNodeBuilder *builder, IsoImage *image, + IsoFileSource *src, IsoFile **file) +{ + int ret; + struct stat info; + IsoStream *stream; + IsoFile *node; + char *name; + + if (builder == NULL || src == NULL || file == NULL) { + return ISO_NULL_POINTER; + } + + ret = iso_file_source_stat(src, &info); + if (ret < 0) { + return ret; + } + + /* this will fail if src is a dir, is not accessible... */ + ret = iso_file_source_stream_new(src, &stream); + if (ret < 0) { + return ret; + } + + name = iso_file_source_get_name(src); + ret = iso_node_new_file(name, stream, &node); + if (ret < 0) { + /* the stream has taken our ref to src, so we need to add one */ + iso_file_source_ref(src); + iso_stream_unref(stream); + free(name); + return ret; + } + + /* fill node fields */ + iso_node_set_permissions((IsoNode*)node, info.st_mode); + iso_node_set_uid((IsoNode*)node, info.st_uid); + iso_node_set_gid((IsoNode*)node, info.st_gid); + iso_node_set_atime((IsoNode*)node, info.st_atime); + iso_node_set_mtime((IsoNode*)node, info.st_mtime); + iso_node_set_ctime((IsoNode*)node, info.st_ctime); + iso_node_set_uid((IsoNode*)node, info.st_uid); + + *file = node; + return ISO_SUCCESS; +} + +static +int default_create_node(IsoNodeBuilder *builder, IsoImage *image, + IsoFileSource *src, IsoNode **node) +{ + int ret; + struct stat info; + IsoNode *new; + char *name; + + if (builder == NULL || src == NULL || node == NULL) { + return ISO_NULL_POINTER; + } + + /* get info about source */ + if (iso_tree_get_follow_symlinks(image)) { + ret = iso_file_source_stat(src, &info); + } else { + ret = iso_file_source_lstat(src, &info); + } + if (ret < 0) { + return ret; + } + + name = iso_file_source_get_name(src); + new = NULL; + + switch (info.st_mode & S_IFMT) { + case S_IFREG: + { + /* source is a regular file */ + IsoStream *stream; + IsoFile *file; + ret = iso_file_source_stream_new(src, &stream); + if (ret < 0) { + break; + } + /* take a ref to the src, as stream has taken our ref */ + iso_file_source_ref(src); + + /* create the file */ + ret = iso_node_new_file(name, stream, &file); + if (ret < 0) { + iso_stream_unref(stream); + } + new = (IsoNode*) file; + } + break; + case S_IFDIR: + { + /* source is a directory */ + IsoDir *dir; + ret = iso_node_new_dir(name, &dir); + new = (IsoNode*)dir; + } + break; + case S_IFLNK: + { + /* source is a symbolic link */ + char dest[PATH_MAX]; + IsoSymlink *link; + + ret = iso_file_source_readlink(src, dest, PATH_MAX); + if (ret < 0) { + break; + } + ret = iso_node_new_symlink(name, strdup(dest), &link); + new = (IsoNode*) link; + } + break; + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + { + /* source is an special file */ + IsoSpecial *special; + ret = iso_node_new_special(name, info.st_mode, info.st_rdev, + &special); + new = (IsoNode*) special; + } + break; + } + + if (ret < 0) { + free(name); + return ret; + } + + /* fill fields */ + iso_node_set_permissions(new, info.st_mode); + iso_node_set_uid(new, info.st_uid); + iso_node_set_gid(new, info.st_gid); + iso_node_set_atime(new, info.st_atime); + iso_node_set_mtime(new, info.st_mtime); + iso_node_set_ctime(new, info.st_ctime); + iso_node_set_uid(new, info.st_uid); + + *node = new; + return ISO_SUCCESS; +} + +static +void default_free(IsoNodeBuilder *builder) +{ + return; +} + +int iso_node_basic_builder_new(IsoNodeBuilder **builder) +{ + IsoNodeBuilder *b; + + if (builder == NULL) { + return ISO_NULL_POINTER; + } + + b = malloc(sizeof(IsoNodeBuilder)); + if (b == NULL) { + return ISO_OUT_OF_MEM; + } + + b->refcount = 1; + b->create_file_data = NULL; + b->create_node_data = NULL; + b->create_file = default_create_file; + b->create_node = default_create_node; + b->free = default_free; + + *builder = b; + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/builder.h b/libisofs/branches/thomas/libisofs/builder.h new file mode 100644 index 00000000..e0d3ff10 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/builder.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#ifndef LIBISO_BUILDER_H_ +#define LIBISO_BUILDER_H_ + +/* + * Definitions for IsoNode builders. + */ + +/* + * Some functions here will be moved to libisofs.h when we expose + * Builder. + */ + +#include "libisofs.h" +#include "fsource.h" + +typedef struct Iso_Node_Builder IsoNodeBuilder; + +struct Iso_Node_Builder +{ + + /** + * Create a new IsoFile from an IsoFileSource. Name, permissions + * and other attributes are taken from src, but a regular file will + * always be created, even if src is another kind of file. + * + * In that case, if the implementation can't do the conversion, it + * should fail propertly. + * + * On sucess, the ref. to src will be owned by file, so you musn't + * unref it. + * + * @return + * 1 on success, < 0 on error + */ + int (*create_file)(IsoNodeBuilder *builder, IsoImage *image, + IsoFileSource *src, IsoFile **file); + + /** + * Create a new IsoNode from a IsoFileSource. The type of the node to be + * created is determined from the type of the file source. Name, + * permissions and other attributes are taken from source file. + * + * Note that the src is never unref, so you need to free it. + * + * @return + * 1 on success, < 0 on error + */ + int (*create_node)(IsoNodeBuilder *builder, IsoImage *image, + IsoFileSource *src, IsoNode **node); + + /** + * Free implementation specific data. Should never be called by user. + * Use iso_node_builder_unref() instead. + */ + void (*free)(IsoNodeBuilder *builder); + + int refcount; + void *create_file_data; + void *create_node_data; +}; + +void iso_node_builder_ref(IsoNodeBuilder *builder); +void iso_node_builder_unref(IsoNodeBuilder *builder); + +/** + * Create a new basic builder ... + * + * @return + * 1 success, < 0 error + */ +int iso_node_basic_builder_new(IsoNodeBuilder **builder); + +#endif /*LIBISO_BUILDER_H_*/ diff --git a/libisofs/branches/thomas/libisofs/data_source.c b/libisofs/branches/thomas/libisofs/data_source.c new file mode 100644 index 00000000..98515c6c --- /dev/null +++ b/libisofs/branches/thomas/libisofs/data_source.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "libisofs.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include + +/** + * Private data for File IsoDataSource + */ +struct file_data_src +{ + char *path; + int fd; +}; + +/** + * Increments the reference counting of the given IsoDataSource. + */ +void iso_data_source_ref(IsoDataSource *src) +{ + src->refcount++; +} + +/** + * Decrements the reference counting of the given IsoDataSource, freeing it + * if refcount reach 0. + */ +void iso_data_source_unref(IsoDataSource *src) +{ + if (--src->refcount == 0) { + src->free_data(src); + free(src); + } +} + +static +int ds_open(IsoDataSource *src) +{ + int fd; + struct file_data_src *data; + + if (src == NULL || src->data == NULL) { + return ISO_NULL_POINTER; + } + + data = (struct file_data_src*) src->data; + if (data->fd != -1) { + return ISO_FILE_ALREADY_OPENNED; + } + + fd = open(data->path, O_RDONLY); + if (fd == -1) { + return ISO_FILE_ERROR; + } + + data->fd = fd; + return ISO_SUCCESS; +} + +static +int ds_close(IsoDataSource *src) +{ + int ret; + struct file_data_src *data; + + if (src == NULL || src->data == NULL) { + return ISO_NULL_POINTER; + } + + data = (struct file_data_src*) src->data; + if (data->fd == -1) { + return ISO_FILE_NOT_OPENNED; + } + + /* close can fail if fd is not valid, but that should never happen */ + ret = close(data->fd); + + /* in any case we mark file as closed */ + data->fd = -1; + return ret == 0 ? ISO_SUCCESS : ISO_FILE_ERROR; +} + +static int ds_read_block(IsoDataSource *src, uint32_t lba, uint8_t *buffer) +{ + struct file_data_src *data; + + if (src == NULL || src->data == NULL || buffer == NULL) { + return ISO_NULL_POINTER; + } + + data = (struct file_data_src*) src->data; + if (data->fd == -1) { + return ISO_FILE_NOT_OPENNED; + } + + /* goes to requested block */ + if (lseek(data->fd, (off_t)lba * (off_t)2048, SEEK_SET) == (off_t) -1) { + return ISO_FILE_SEEK_ERROR; + } + + /* TODO #00008 : guard against partial reads. */ + if (read(data->fd, buffer, 2048) != 2048) { + return ISO_FILE_READ_ERROR; + } + + return ISO_SUCCESS; +} + +static +void ds_free_data(IsoDataSource *src) +{ + struct file_data_src *data; + + data = (struct file_data_src*)src->data; + + /* close the file if needed */ + if (data->fd != -1) { + close(data->fd); + } + free(data->path); + free(data); +} + +/** + * Create a new IsoDataSource from a local file. This is suitable for + * accessing regular .iso images, or to acces drives via its block device + * and standard POSIX I/O calls. + * + * @param path + * The path of the file + * @param src + * Will be filled with the pointer to the newly created data source. + * @return + * 1 on success, < 0 on error. + */ +int iso_data_source_new_from_file(const char *path, IsoDataSource **src) +{ + int ret; + struct file_data_src *data; + IsoDataSource *ds; + + if (path == NULL || src == NULL) { + return ISO_NULL_POINTER; + } + + /* ensure we have read access to the file */ + ret = iso_eaccess(path); + if (ret < 0) { + return ret; + } + + data = malloc(sizeof(struct file_data_src)); + if (data == NULL) { + return ISO_OUT_OF_MEM; + } + + ds = malloc(sizeof(IsoDataSource)); + if (ds == NULL) { + free(data); + return ISO_OUT_OF_MEM; + } + + /* fill data fields */ + data->path = strdup(path); + if (data->path == NULL) { + free(data); + free(ds); + return ISO_OUT_OF_MEM; + } + + data->fd = -1; + ds->version = 0; + ds->refcount = 1; + ds->data = data; + + ds->open = ds_open; + ds->close = ds_close; + ds->read_block = ds_read_block; + ds->free_data = ds_free_data; + + *src = ds; + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/ecma119.c b/libisofs/branches/thomas/libisofs/ecma119.c new file mode 100644 index 00000000..e49ad8c0 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/ecma119.c @@ -0,0 +1,1579 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * Copyright (c) 2007 Mario Danic + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "libisofs.h" +#include "ecma119.h" +#include "joliet.h" +#include "iso1999.h" +#include "eltorito.h" +#include "ecma119_tree.h" +#include "filesrc.h" +#include "image.h" +#include "writer.h" +#include "messages.h" +#include "rockridge.h" +#include "util.h" + +#include "libburn/libburn.h" + +#include +#include +#include +#include +#include + +/* + * TODO #00011 : guard against bad path table usage with more than 65535 dirs + * image with more than 65535 directories have path_table related problems + * due to 16 bits parent id. Note that this problem only affects to folders + * that are parent of another folder. + */ + +static +void ecma119_image_free(Ecma119Image *t) +{ + size_t i; + + ecma119_node_free(t->root); + iso_image_unref(t->image); + iso_rbtree_destroy(t->files, iso_file_src_free); + iso_ring_buffer_free(t->buffer); + + for (i = 0; i < t->nwriters; ++i) { + IsoImageWriter *writer = t->writers[i]; + writer->free_data(writer); + free(writer); + } + free(t->input_charset); + free(t->output_charset); + free(t->writers); + free(t); +} + +/** + * Check if we should add version number ";" to the given node name. + */ +static +int need_version_number(Ecma119Image *t, Ecma119Node *n) +{ + if (t->omit_version_numbers) { + return 0; + } + if (n->type == ECMA119_DIR || n->type == ECMA119_PLACEHOLDER) { + return 0; + } else { + return 1; + } +} + +/** + * Compute the size of a directory entry for a single node + */ +static +size_t calc_dirent_len(Ecma119Image *t, Ecma119Node *n) +{ + int ret = n->iso_name ? strlen(n->iso_name) + 33 : 34; + if (need_version_number(t, n)) { + ret += 2; /* take into account version numbers */ + } + if (ret % 2) + ret++; + return ret; +} + +/** + * Computes the total size of all directory entries of a single dir, + * acording to ECMA-119 6.8.1.1 + * + * This also take into account the size needed for RR entries and + * SUSP continuation areas (SUSP, 5.1). + * + * @param ce + * Will be filled with the size needed for Continuation Areas + * @return + * The size needed for all dir entries of the given dir, without + * taking into account the continuation areas. + */ +static +size_t calc_dir_size(Ecma119Image *t, Ecma119Node *dir, size_t *ce) +{ + size_t i, len; + size_t ce_len = 0; + + /* size of "." and ".." entries */ + len = 34 + 34; + if (t->rockridge) { + len += rrip_calc_len(t, dir, 1, 255 - 34, &ce_len); + *ce += ce_len; + len += rrip_calc_len(t, dir, 2, 255 - 34, &ce_len); + *ce += ce_len; + } + + for (i = 0; i < dir->info.dir->nchildren; ++i) { + size_t remaining; + Ecma119Node *child = dir->info.dir->children[i]; + size_t dirent_len = calc_dirent_len(t, child); + if (t->rockridge) { + dirent_len += rrip_calc_len(t, child, 0, 255 - dirent_len, &ce_len); + *ce += ce_len; + } + remaining = BLOCK_SIZE - (len % BLOCK_SIZE); + if (dirent_len > remaining) { + /* child directory entry doesn't fit on block */ + len += remaining + dirent_len; + } else { + len += dirent_len; + } + } + + /* + * The size of a dir is always a multiple of block size, as we must add + * the size of the unused space after the last directory record + * (ECMA-119, 6.8.1.3) + */ + len = ROUND_UP(len, BLOCK_SIZE); + + /* cache the len */ + dir->info.dir->len = len; + return len; +} + +static +void calc_dir_pos(Ecma119Image *t, Ecma119Node *dir) +{ + size_t i, len; + size_t ce_len = 0; + + t->ndirs++; + dir->info.dir->block = t->curblock; + len = calc_dir_size(t, dir, &ce_len); + t->curblock += DIV_UP(len, BLOCK_SIZE); + if (t->rockridge) { + t->curblock += DIV_UP(ce_len, BLOCK_SIZE); + } + for (i = 0; i < dir->info.dir->nchildren; i++) { + Ecma119Node *child = dir->info.dir->children[i]; + if (child->type == ECMA119_DIR) { + calc_dir_pos(t, child); + } + } +} + +/** + * Compute the length of the path table, in bytes. + */ +static +uint32_t calc_path_table_size(Ecma119Node *dir) +{ + uint32_t size; + size_t i; + + /* size of path table for this entry */ + size = 8; + size += dir->iso_name ? strlen(dir->iso_name) : 1; + size += (size % 2); + + /* and recurse */ + for (i = 0; i < dir->info.dir->nchildren; i++) { + Ecma119Node *child = dir->info.dir->children[i]; + if (child->type == ECMA119_DIR) { + size += calc_path_table_size(child); + } + } + return size; +} + +static +int ecma119_writer_compute_data_blocks(IsoImageWriter *writer) +{ + Ecma119Image *target; + uint32_t path_table_size; + + if (writer == NULL) { + return ISO_ASSERT_FAILURE; + } + + target = writer->target; + + /* compute position of directories */ + iso_msg_debug(target->image->id, "Computing position of dir structure"); + target->ndirs = 0; + calc_dir_pos(target, target->root); + + /* compute length of pathlist */ + iso_msg_debug(target->image->id, "Computing length of pathlist"); + path_table_size = calc_path_table_size(target->root); + + /* compute location for path tables */ + target->l_path_table_pos = target->curblock; + target->curblock += DIV_UP(path_table_size, BLOCK_SIZE); + target->m_path_table_pos = target->curblock; + target->curblock += DIV_UP(path_table_size, BLOCK_SIZE); + target->path_table_size = path_table_size; + + return ISO_SUCCESS; +} + +/** + * Write a single directory record (ECMA-119, 9.1) + * + * @param file_id + * if >= 0, we use it instead of the filename (for "." and ".." entries). + * @param len_fi + * Computed length of the file identifier. Total size of the directory + * entry will be len + 33 + padding if needed (ECMA-119, 9.1.12) + * @param info + * SUSP entries for the given directory record. It will be NULL for the + * root directory record in the PVD (ECMA-119, 8.4.18) (in order to + * distinguish it from the "." entry in the root directory) + */ +static +void write_one_dir_record(Ecma119Image *t, Ecma119Node *node, int file_id, + uint8_t *buf, size_t len_fi, struct susp_info *info) +{ + uint32_t len; + uint32_t block; + uint8_t len_dr; /*< size of dir entry without SUSP fields */ + uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id + : (uint8_t*)node->iso_name; + + struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; + + len_dr = 33 + len_fi + (len_fi % 2 ? 0 : 1); + + memcpy(rec->file_id, name, len_fi); + + if (need_version_number(t, node)) { + len_dr += 2; + rec->file_id[len_fi++] = ';'; + rec->file_id[len_fi++] = '1'; + } + + if (node->type == ECMA119_DIR) { + /* use the cached length */ + len = node->info.dir->len; + block = node->info.dir->block; + } else if (node->type == ECMA119_FILE) { + len = iso_file_src_get_size(node->info.file); + block = node->info.file->block; + } else { + /* + * for nodes other than files and dirs, we set both + * len and block to 0 + */ + len = 0; + block = 0; + } + + /* + * For ".." entry we need to write the parent info! + */ + if (file_id == 1 && node->parent) + node = node->parent; + + rec->len_dr[0] = len_dr + (info != NULL ? info->suf_len : 0); + iso_bb(rec->block, block, 4); + iso_bb(rec->length, len, 4); + iso_datetime_7(rec->recording_time, t->now, t->always_gmt); + rec->flags[0] = (node->type == ECMA119_DIR) ? 2 : 0; + iso_bb(rec->vol_seq_number, 1, 2); + rec->len_fi[0] = len_fi; + + /* and finally write the SUSP fields */ + if (info != NULL) { + rrip_write_susp_fields(t, info, buf + len_dr); + } +} + +static +char *get_relaxed_vol_id(Ecma119Image *t, const char *name) +{ + int ret; + if (name == NULL) { + return NULL; + } + if (strcmp(t->input_charset, t->output_charset)) { + /* charset conversion needed */ + char *str; + ret = strconv(name, t->input_charset, t->output_charset, &str); + if (ret == ISO_SUCCESS) { + return str; + } + iso_msg_submit(t->image->id, ISO_FILENAME_WRONG_CHARSET, ret, + "Charset conversion error. Can't convert %s from %s to %s", + name, t->input_charset, t->output_charset); + } + return strdup(name); +} + +/** + * Write the Primary Volume Descriptor (ECMA-119, 8.4) + */ +static +int ecma119_writer_write_vol_desc(IsoImageWriter *writer) +{ + IsoImage *image; + Ecma119Image *t; + struct ecma119_pri_vol_desc vol; + + char *vol_id, *pub_id, *data_id, *volset_id; + char *system_id, *application_id, *copyright_file_id; + char *abstract_file_id, *biblio_file_id; + + if (writer == NULL) { + return ISO_ASSERT_FAILURE; + } + + t = writer->target; + image = t->image; + + iso_msg_debug(image->id, "Write Primary Volume Descriptor"); + + memset(&vol, 0, sizeof(struct ecma119_pri_vol_desc)); + + if (t->relaxed_vol_atts) { + vol_id = get_relaxed_vol_id(t, image->volume_id); + volset_id = get_relaxed_vol_id(t, image->volset_id); + } else { + str2d_char(t->input_charset, image->volume_id, &vol_id); + str2d_char(t->input_charset, image->volset_id, &volset_id); + } + str2a_char(t->input_charset, image->publisher_id, &pub_id); + str2a_char(t->input_charset, image->data_preparer_id, &data_id); + str2a_char(t->input_charset, image->system_id, &system_id); + str2a_char(t->input_charset, image->application_id, &application_id); + str2d_char(t->input_charset, image->copyright_file_id, ©right_file_id); + str2d_char(t->input_charset, image->abstract_file_id, &abstract_file_id); + str2d_char(t->input_charset, image->biblio_file_id, &biblio_file_id); + + vol.vol_desc_type[0] = 1; + memcpy(vol.std_identifier, "CD001", 5); + vol.vol_desc_version[0] = 1; + strncpy_pad((char*)vol.system_id, system_id, 32); + strncpy_pad((char*)vol.volume_id, vol_id, 32); + iso_bb(vol.vol_space_size, t->vol_space_size, 4); + iso_bb(vol.vol_set_size, 1, 2); + iso_bb(vol.vol_seq_number, 1, 2); + iso_bb(vol.block_size, BLOCK_SIZE, 2); + iso_bb(vol.path_table_size, t->path_table_size, 4); + iso_lsb(vol.l_path_table_pos, t->l_path_table_pos, 4); + iso_msb(vol.m_path_table_pos, t->m_path_table_pos, 4); + + write_one_dir_record(t, t->root, 0, vol.root_dir_record, 1, NULL); + + strncpy_pad((char*)vol.vol_set_id, volset_id, 128); + strncpy_pad((char*)vol.publisher_id, pub_id, 128); + strncpy_pad((char*)vol.data_prep_id, data_id, 128); + + strncpy_pad((char*)vol.application_id, application_id, 128); + strncpy_pad((char*)vol.copyright_file_id, copyright_file_id, 37); + strncpy_pad((char*)vol.abstract_file_id, abstract_file_id, 37); + strncpy_pad((char*)vol.bibliographic_file_id, biblio_file_id, 37); + + iso_datetime_17(vol.vol_creation_time, t->now, t->always_gmt); + iso_datetime_17(vol.vol_modification_time, t->now, t->always_gmt); + iso_datetime_17(vol.vol_effective_time, t->now, t->always_gmt); + vol.file_structure_version[0] = 1; + + free(vol_id); + free(volset_id); + free(pub_id); + free(data_id); + free(system_id); + free(application_id); + free(copyright_file_id); + free(abstract_file_id); + free(biblio_file_id); + + /* Finally write the Volume Descriptor */ + return iso_write(t, &vol, sizeof(struct ecma119_pri_vol_desc)); +} + +static +int write_one_dir(Ecma119Image *t, Ecma119Node *dir) +{ + int ret; + uint8_t buffer[BLOCK_SIZE]; + size_t i; + size_t fi_len, len; + struct susp_info info; + + /* buf will point to current write position on buffer */ + uint8_t *buf = buffer; + + /* initialize buffer with 0s */ + memset(buffer, 0, BLOCK_SIZE); + + /* + * set susp_info to 0's, this way code for both plain ECMA-119 and + * RR is very similar + */ + memset(&info, 0, sizeof(struct susp_info)); + if (t->rockridge) { + /* initialize the ce_block, it might be needed */ + info.ce_block = dir->info.dir->block + DIV_UP(dir->info.dir->len, + BLOCK_SIZE); + } + + /* write the "." and ".." entries first */ + if (t->rockridge) { + ret = rrip_get_susp_fields(t, dir, 1, 255 - 32, &info); + if (ret < 0) { + return ret; + } + } + len = 34 + info.suf_len; + write_one_dir_record(t, dir, 0, buf, 1, &info); + buf += len; + + if (t->rockridge) { + ret = rrip_get_susp_fields(t, dir, 2, 255 - 32, &info); + if (ret < 0) { + return ret; + } + } + len = 34 + info.suf_len; + write_one_dir_record(t, dir, 1, buf, 1, &info); + buf += len; + + for (i = 0; i < dir->info.dir->nchildren; i++) { + Ecma119Node *child = dir->info.dir->children[i]; + + /* compute len of directory entry */ + fi_len = strlen(child->iso_name); + len = fi_len + 33 + (fi_len % 2 ? 0 : 1); + if (need_version_number(t, child)) { + len += 2; + } + + /* get the SUSP fields if rockridge is enabled */ + if (t->rockridge) { + ret = rrip_get_susp_fields(t, child, 0, 255 - len, &info); + if (ret < 0) { + return ret; + } + len += info.suf_len; + } + + if ( (buf + len - buffer) > BLOCK_SIZE) { + /* dir doesn't fit in current block */ + ret = iso_write(t, buffer, BLOCK_SIZE); + if (ret < 0) { + return ret; + } + memset(buffer, 0, BLOCK_SIZE); + buf = buffer; + } + /* write the directory entry in any case */ + write_one_dir_record(t, child, -1, buf, fi_len, &info); + buf += len; + } + + /* write the last block */ + ret = iso_write(t, buffer, BLOCK_SIZE); + if (ret < 0) { + return ret; + } + + /* write the Continuation Area if needed */ + if (info.ce_len > 0) { + ret = rrip_write_ce_fields(t, &info); + } + + return ret; +} + +static +int write_dirs(Ecma119Image *t, Ecma119Node *root) +{ + int ret; + size_t i; + + /* write all directory entries for this dir */ + ret = write_one_dir(t, root); + if (ret < 0) { + return ret; + } + + /* recurse */ + for (i = 0; i < root->info.dir->nchildren; i++) { + Ecma119Node *child = root->info.dir->children[i]; + if (child->type == ECMA119_DIR) { + ret = write_dirs(t, child); + if (ret < 0) { + return ret; + } + } + } + return ISO_SUCCESS; +} + +static +int write_path_table(Ecma119Image *t, Ecma119Node **pathlist, int l_type) +{ + size_t i, len; + uint8_t buf[64]; /* 64 is just a convenient size larger enought */ + struct ecma119_path_table_record *rec; + void (*write_int)(uint8_t*, uint32_t, int); + Ecma119Node *dir; + uint32_t path_table_size; + int parent = 0; + int ret= ISO_SUCCESS; + + path_table_size = 0; + write_int = l_type ? iso_lsb : iso_msb; + + for (i = 0; i < t->ndirs; i++) { + dir = pathlist[i]; + + /* find the index of the parent in the table */ + while ((i) && pathlist[parent] != dir->parent) { + parent++; + } + + /* write the Path Table Record (ECMA-119, 9.4) */ + memset(buf, 0, 64); + rec = (struct ecma119_path_table_record*) buf; + rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->iso_name) : 1; + rec->len_xa[0] = 0; + write_int(rec->block, dir->info.dir->block, 4); + write_int(rec->parent, parent + 1, 2); + if (dir->parent) { + memcpy(rec->dir_id, dir->iso_name, rec->len_di[0]); + } + len = 8 + rec->len_di[0] + (rec->len_di[0] % 2); + ret = iso_write(t, buf, len); + if (ret < 0) { + /* error */ + return ret; + } + path_table_size += len; + } + + /* we need to fill the last block with zeros */ + path_table_size %= BLOCK_SIZE; + if (path_table_size) { + uint8_t zeros[BLOCK_SIZE]; + len = BLOCK_SIZE - path_table_size; + memset(zeros, 0, len); + ret = iso_write(t, zeros, len); + } + return ret; +} + +static +int write_path_tables(Ecma119Image *t) +{ + int ret; + size_t i, j, cur; + Ecma119Node **pathlist; + + iso_msg_debug(t->image->id, "Writing ISO Path tables"); + + /* allocate temporal pathlist */ + pathlist = malloc(sizeof(void*) * t->ndirs); + if (pathlist == NULL) { + return ISO_OUT_OF_MEM; + } + pathlist[0] = t->root; + cur = 1; + + for (i = 0; i < t->ndirs; i++) { + Ecma119Node *dir = pathlist[i]; + for (j = 0; j < dir->info.dir->nchildren; j++) { + Ecma119Node *child = dir->info.dir->children[j]; + if (child->type == ECMA119_DIR) { + pathlist[cur++] = child; + } + } + } + + /* Write L Path Table */ + ret = write_path_table(t, pathlist, 1); + if (ret < 0) { + goto write_path_tables_exit; + } + + /* Write L Path Table */ + ret = write_path_table(t, pathlist, 0); + + write_path_tables_exit: ; + free(pathlist); + return ret; +} + +/** + * Write both the directory structure (ECMA-119, 6.8) and the L and M + * Path Tables (ECMA-119, 6.9). + */ +static +int ecma119_writer_write_data(IsoImageWriter *writer) +{ + int ret; + Ecma119Image *t; + + if (writer == NULL) { + return ISO_ASSERT_FAILURE; + } + t = writer->target; + + /* first of all, we write the directory structure */ + ret = write_dirs(t, t->root); + if (ret < 0) { + return ret; + } + + /* and write the path tables */ + ret = write_path_tables(t); + + return ret; +} + +static +int ecma119_writer_free_data(IsoImageWriter *writer) +{ + /* nothing to do */ + return ISO_SUCCESS; +} + +int ecma119_writer_create(Ecma119Image *target) +{ + int ret; + IsoImageWriter *writer; + + writer = malloc(sizeof(IsoImageWriter)); + if (writer == NULL) { + return ISO_OUT_OF_MEM; + } + + writer->compute_data_blocks = ecma119_writer_compute_data_blocks; + writer->write_vol_desc = ecma119_writer_write_vol_desc; + writer->write_data = ecma119_writer_write_data; + writer->free_data = ecma119_writer_free_data; + writer->data = NULL; + writer->target = target; + + /* add this writer to image */ + target->writers[target->nwriters++] = writer; + + iso_msg_debug(target->image->id, "Creating low level ECMA-119 tree..."); + ret = ecma119_tree_create(target); + if (ret < 0) { + return ret; + } + + /* we need the volume descriptor */ + target->curblock++; + return ISO_SUCCESS; +} + +/** compute how many padding bytes are needed */ +static +int pad_writer_compute_data_blocks(IsoImageWriter *writer) +{ + Ecma119Image *target; + + if (writer == NULL) { + return ISO_ASSERT_FAILURE; + } + + target = writer->target; + if (target->curblock < 32) { + target->pad_blocks = 32 - target->curblock; + target->curblock = 32; + } + return ISO_SUCCESS; +} + +static +int pad_writer_write_vol_desc(IsoImageWriter *writer) +{ + /* nothing to do */ + return ISO_SUCCESS; +} +static +int pad_writer_write_data(IsoImageWriter *writer) +{ + int ret; + Ecma119Image *t; + uint32_t pad[BLOCK_SIZE]; + size_t i; + + if (writer == NULL) { + return ISO_ASSERT_FAILURE; + } + t = writer->target; + + if (t->pad_blocks == 0) { + return ISO_SUCCESS; + } + + memset(pad, 0, BLOCK_SIZE); + for (i = 0; i < t->pad_blocks; ++i) { + ret = iso_write(t, pad, BLOCK_SIZE); + if (ret < 0) { + return ret; + } + } + + return ISO_SUCCESS; +} + +static +int pad_writer_free_data(IsoImageWriter *writer) +{ + /* nothing to do */ + return ISO_SUCCESS; +} + +static +int pad_writer_create(Ecma119Image *target) +{ + IsoImageWriter *writer; + + writer = malloc(sizeof(IsoImageWriter)); + if (writer == NULL) { + return ISO_OUT_OF_MEM; + } + + writer->compute_data_blocks = pad_writer_compute_data_blocks; + writer->write_vol_desc = pad_writer_write_vol_desc; + writer->write_data = pad_writer_write_data; + writer->free_data = pad_writer_free_data; + writer->data = NULL; + writer->target = target; + + /* add this writer to image */ + target->writers[target->nwriters++] = writer; + return ISO_SUCCESS; +} + +static +void *write_function(void *arg) +{ + int res; + size_t i; + uint8_t buf[BLOCK_SIZE]; + IsoImageWriter *writer; + + Ecma119Image *target = (Ecma119Image*)arg; + iso_msg_debug(target->image->id, "Starting image writing..."); + + target->bytes_written = (off_t) 0; + target->percent_written = 0; + + /* Write System Area, 16 blocks of zeros (ECMA-119, 6.2.1) */ + memset(buf, 0, BLOCK_SIZE); + for (i = 0; i < 16; ++i) { + res = iso_write(target, buf, BLOCK_SIZE); + if (res < 0) { + goto write_error; + } + } + + /* write volume descriptors, one per writer */ + iso_msg_debug(target->image->id, "Write volume descriptors"); + for (i = 0; i < target->nwriters; ++i) { + writer = target->writers[i]; + res = writer->write_vol_desc(writer); + if (res < 0) { + goto write_error; + } + } + + /* write Volume Descriptor Set Terminator (ECMA-119, 8.3) */ + { + struct ecma119_vol_desc_terminator *vol; + vol = (struct ecma119_vol_desc_terminator *) buf; + + vol->vol_desc_type[0] = 255; + memcpy(vol->std_identifier, "CD001", 5); + vol->vol_desc_version[0] = 1; + + res = iso_write(target, buf, BLOCK_SIZE); + if (res < 0) { + goto write_error; + } + } + + /* write data for each writer */ + for (i = 0; i < target->nwriters; ++i) { + writer = target->writers[i]; + res = writer->write_data(writer); + if (res < 0) { + goto write_error; + } + } + + iso_ring_buffer_writer_close(target->buffer, 0); + pthread_exit(NULL); + + write_error: ; + if (res == ISO_CANCELED) { + /* canceled */ + iso_msg_submit(target->image->id, ISO_IMAGE_WRITE_CANCELED, 0, NULL); + } else { + /* image write error */ + iso_msg_submit(target->image->id, ISO_WRITE_ERROR, res, + "Image write error"); + } + iso_ring_buffer_writer_close(target->buffer, 1); + pthread_exit(NULL); +} + +static +int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img) +{ + int ret, i, voldesc_size, nwriters; + Ecma119Image *target; + + /* 1. Allocate target and copy opts there */ + target = calloc(1, sizeof(Ecma119Image)); + if (target == NULL) { + return ISO_OUT_OF_MEM; + } + + /* create the tree for file caching */ + ret = iso_rbtree_new(iso_file_src_cmp, &(target->files)); + if (ret < 0) { + free(target); + return ret; + } + + target->image = src; + iso_image_ref(src); + + target->iso_level = opts->level; + target->rockridge = opts->rockridge; + target->joliet = opts->joliet; + target->iso1999 = opts->iso1999; + target->always_gmt = opts->always_gmt; + target->ino = 0; + target->omit_version_numbers = opts->omit_version_numbers + | opts->max_37_char_filenames; + target->allow_deep_paths = opts->allow_deep_paths; + target->allow_longer_paths = opts->allow_longer_paths; + target->max_37_char_filenames = opts->max_37_char_filenames; + target->no_force_dots = opts->no_force_dots; + target->allow_lowercase = opts->allow_lowercase; + target->allow_full_ascii = opts->allow_full_ascii; + target->relaxed_vol_atts = opts->relaxed_vol_atts; + target->joliet_longer_paths = opts->joliet_longer_paths; + target->sort_files = opts->sort_files; + + target->replace_uid = opts->replace_uid ? 1 : 0; + target->replace_gid = opts->replace_gid ? 1 : 0; + target->replace_dir_mode = opts->replace_dir_mode ? 1 : 0; + target->replace_file_mode = opts->replace_file_mode ? 1 : 0; + + target->uid = opts->replace_uid == 2 ? opts->uid : 0; + target->gid = opts->replace_gid == 2 ? opts->gid : 0; + target->dir_mode = opts->replace_dir_mode == 2 ? opts->dir_mode : 0555; + target->file_mode = opts->replace_file_mode == 2 ? opts->file_mode : 0444; + + target->now = time(NULL); + target->ms_block = opts->ms_block; + target->appendable = opts->appendable; + + target->replace_timestamps = opts->replace_timestamps ? 1 : 0; + target->timestamp = opts->replace_timestamps == 2 ? + opts->timestamp : target->now; + + /* el-torito? */ + target->eltorito = (src->bootcat == NULL ? 0 : 1); + target->catalog = src->bootcat; + + /* default to locale charset */ + setlocale(LC_CTYPE, ""); + target->input_charset = strdup(nl_langinfo(CODESET)); + if (target->input_charset == NULL) { + iso_image_unref(src); + free(target); + return ISO_OUT_OF_MEM; + } + + if (opts->output_charset != NULL) { + target->output_charset = strdup(opts->output_charset); + } else { + target->output_charset = strdup(target->input_charset); + } + if (target->output_charset == NULL) { + iso_image_unref(src); + free(target); + return ISO_OUT_OF_MEM; + } + + /* + * 2. Based on those options, create needed writers: iso, joliet... + * Each writer inits its structures and stores needed info into + * target. + * If the writer needs an volume descriptor, it increments image + * current block. + * Finally, create Writer for files. + */ + target->curblock = target->ms_block + 16; + + /* the number of writers is dependent of the extensions */ + nwriters = 1 + 1 + 1; /* ECMA-119 + padding + files */ + + if (target->eltorito) { + nwriters++; + } + if (target->joliet) { + nwriters++; + } + if (target->iso1999) { + nwriters++; + } + + target->writers = malloc(nwriters * sizeof(void*)); + if (target->writers == NULL) { + iso_image_unref(src); + free(target); + return ISO_OUT_OF_MEM; + } + + /* create writer for ECMA-119 structure */ + ret = ecma119_writer_create(target); + if (ret < 0) { + goto target_cleanup; + } + + /* create writer for El-Torito */ + if (target->eltorito) { + ret = eltorito_writer_create(target); + if (ret < 0) { + goto target_cleanup; + } + } + + /* create writer for Joliet structure */ + if (target->joliet) { + ret = joliet_writer_create(target); + if (ret < 0) { + goto target_cleanup; + } + } + + /* create writer for ISO 9660:1999 structure */ + if (target->iso1999) { + ret = iso1999_writer_create(target); + if (ret < 0) { + goto target_cleanup; + } + } + + voldesc_size = target->curblock - target->ms_block - 16; + + /* Volume Descriptor Set Terminator */ + target->curblock++; + + /* + * Create the writer for possible padding to ensure that in case of image + * growing we can safety overwrite the first 64 KiB of image. + */ + ret = pad_writer_create(target); + if (ret < 0) { + goto target_cleanup; + } + + /* create writer for file contents */ + ret = iso_file_src_writer_create(target); + if (ret < 0) { + goto target_cleanup; + } + + /* + * 3. + * Call compute_data_blocks() in each Writer. + * That function computes the size needed by its structures and + * increments image current block propertly. + */ + for (i = 0; i < target->nwriters; ++i) { + IsoImageWriter *writer = target->writers[i]; + ret = writer->compute_data_blocks(writer); + if (ret < 0) { + goto target_cleanup; + } + } + + /* create the ring buffer */ + ret = iso_ring_buffer_new(opts->fifo_size, &target->buffer); + if (ret < 0) { + goto target_cleanup; + } + + /* check if we need to provide a copy of volume descriptors */ + if (opts->overwrite) { + + /* + * Get a copy of the volume descriptors to be written in a DVD+RW + * disc + */ + + uint8_t *buf; + struct ecma119_vol_desc_terminator *vol; + IsoImageWriter *writer; + + /* + * In the PVM to be written in the 16th sector of the disc, we + * need to specify the full size. + */ + target->vol_space_size = target->curblock; + + /* write volume descriptor */ + for (i = 0; i < target->nwriters; ++i) { + writer = target->writers[i]; + ret = writer->write_vol_desc(writer); + if (ret < 0) { + iso_msg_debug(target->image->id, + "Error writing overwrite volume descriptors"); + goto target_cleanup; + } + } + + /* skip the first 16 blocks (system area) */ + buf = opts->overwrite + 16 * BLOCK_SIZE; + voldesc_size *= BLOCK_SIZE; + + /* copy the volume descriptors to the overwrite buffer... */ + ret = iso_ring_buffer_read(target->buffer, buf, voldesc_size); + if (ret < 0) { + iso_msg_debug(target->image->id, + "Error reading overwrite volume descriptors"); + goto target_cleanup; + } + + /* ...including the vol desc terminator */ + memset(buf + voldesc_size, 0, BLOCK_SIZE); + vol = (struct ecma119_vol_desc_terminator*) (buf + voldesc_size); + vol->vol_desc_type[0] = 255; + memcpy(vol->std_identifier, "CD001", 5); + vol->vol_desc_version[0] = 1; + } + + /* + * The volume space size is just the size of the last session, in + * case of ms images. + */ + target->vol_space_size = target->curblock - target->ms_block; + target->total_size = (off_t) target->vol_space_size * BLOCK_SIZE; + + /* 4. Create and start writting thread */ + + /* ensure the thread is created joinable */ + pthread_attr_init(&(target->th_attr)); + pthread_attr_setdetachstate(&(target->th_attr), PTHREAD_CREATE_JOINABLE); + + ret = pthread_create(&(target->wthread), &(target->th_attr), + write_function, (void *) target); + if (ret != 0) { + iso_msg_submit(target->image->id, ISO_THREAD_ERROR, 0, + "Cannot create writer thread"); + ret = ISO_THREAD_ERROR; + goto target_cleanup; + } + + /* + * Notice that once we reach this point, target belongs to the writer + * thread and should not be modified until the writer thread finished. + * There're however, specific fields in target that can be accessed, or + * even modified by the read thread (look inside bs_* functions) + */ + + *img = target; + return ISO_SUCCESS; + + target_cleanup: ; + ecma119_image_free(target); + return ret; +} + +static int bs_read(struct burn_source *bs, unsigned char *buf, int size) +{ + int ret; + Ecma119Image *t = (Ecma119Image*)bs->data; + + ret = iso_ring_buffer_read(t->buffer, buf, size); + if (ret == ISO_SUCCESS) { + return size; + } else if (ret < 0) { + /* error */ + iso_msg_submit(t->image->id, ISO_BUF_READ_ERROR, ret, NULL); + return -1; + } else { + /* EOF */ + return 0; + } +} + +static off_t bs_get_size(struct burn_source *bs) +{ + Ecma119Image *target = (Ecma119Image*)bs->data; + return target->total_size; +} + +static void bs_free_data(struct burn_source *bs) +{ + int st; + Ecma119Image *target = (Ecma119Image*)bs->data; + + st = iso_ring_buffer_get_status(bs, NULL, NULL); + + /* was read already finished (i.e, canceled)? */ + if (st < 4) { + /* forces writer to stop if it is still running */ + iso_ring_buffer_reader_close(target->buffer, 0); + + /* wait until writer thread finishes */ + pthread_join(target->wthread, NULL); + iso_msg_debug(target->image->id, "Writer thread joined"); + } + + iso_msg_debug(target->image->id, + "Ring buffer was %d times full and %d times empty", + iso_ring_buffer_get_times_full(target->buffer), + iso_ring_buffer_get_times_empty(target->buffer)); + + /* now we can safety free target */ + ecma119_image_free(target); +} + +static +int bs_cancel(struct burn_source *bs) +{ + int st; + size_t cap, free; + Ecma119Image *target = (Ecma119Image*)bs->data; + + st = iso_ring_buffer_get_status(bs, &cap, &free); + + if (free == cap && (st == 2 || st == 3)) { + /* image was already consumed */ + iso_ring_buffer_reader_close(target->buffer, 0); + } else { + iso_msg_debug(target->image->id, "Reader thread being cancelled"); + + /* forces writer to stop if it is still running */ + iso_ring_buffer_reader_close(target->buffer, ISO_CANCELED); + } + + /* wait until writer thread finishes */ + pthread_join(target->wthread, NULL); + + iso_msg_debug(target->image->id, "Writer thread joined"); + return ISO_SUCCESS; +} + +static +int bs_set_size(struct burn_source *bs, off_t size) +{ + Ecma119Image *target = (Ecma119Image*)bs->data; + + /* + * just set the value to be returned by get_size. This is not used at + * all by libisofs, it is here just for helping libburn to correctly pad + * the image if needed. + */ + target->total_size = size; + return 1; +} + +int iso_image_create_burn_source(IsoImage *image, IsoWriteOpts *opts, + struct burn_source **burn_src) +{ + int ret; + struct burn_source *source; + Ecma119Image *target= NULL; + + if (image == NULL || opts == NULL || burn_src == NULL) { + return ISO_NULL_POINTER; + } + + source = calloc(1, sizeof(struct burn_source)); + if (source == NULL) { + return ISO_OUT_OF_MEM; + } + + ret = ecma119_image_new(image, opts, &target); + if (ret < 0) { + free(source); + return ret; + } + + source->refcount = 1; + source->version = 1; + source->read = NULL; + source->get_size = bs_get_size; + source->set_size = bs_set_size; + source->free_data = bs_free_data; + source->read_xt = bs_read; + source->cancel = bs_cancel; + source->data = target; + + *burn_src = source; + return ISO_SUCCESS; +} + +int iso_write(Ecma119Image *target, void *buf, size_t count) +{ + int ret; + + ret = iso_ring_buffer_write(target->buffer, buf, count); + if (ret == 0) { + /* reader cancelled */ + return ISO_CANCELED; + } + + /* total size is 0 when writing the overwrite buffer */ + if (ret > 0 && (target->total_size != (off_t) 0)){ + unsigned int kbw, kbt; + int percent; + + target->bytes_written += (off_t) count; + kbw = (unsigned int) (target->bytes_written >> 10); + kbt = (unsigned int) (target->total_size >> 10); + percent = (kbw * 100) / kbt; + + /* only report in 5% chunks */ + if (percent >= target->percent_written + 5) { + iso_msg_debug(target->image->id, "Processed %u of %u KB (%d %%)", + kbw, kbt, percent); + target->percent_written = percent; + } + } + + return ret; +} + +int iso_write_opts_new(IsoWriteOpts **opts, int profile) +{ + IsoWriteOpts *wopts; + + if (opts == NULL) { + return ISO_NULL_POINTER; + } + if (profile < 0 || profile > 2) { + return ISO_WRONG_ARG_VALUE; + } + + wopts = calloc(1, sizeof(IsoWriteOpts)); + if (wopts == NULL) { + return ISO_OUT_OF_MEM; + } + + switch (profile) { + case 0: + wopts->level = 1; + break; + case 1: + wopts->level = 2; + wopts->rockridge = 1; + break; + case 2: + wopts->level = 2; + wopts->rockridge = 1; + wopts->joliet = 1; + wopts->replace_dir_mode = 1; + wopts->replace_file_mode = 1; + wopts->replace_uid = 1; + wopts->replace_gid = 1; + wopts->replace_timestamps = 1; + wopts->always_gmt = 1; + break; + default: + /* should never happen */ + free(wopts); + return ISO_ASSERT_FAILURE; + break; + } + wopts->fifo_size = 1024; /* 2 MB buffer */ + wopts->sort_files = 1; /* file sorting is always good */ + + *opts = wopts; + return ISO_SUCCESS; +} + +void iso_write_opts_free(IsoWriteOpts *opts) +{ + if (opts == NULL) { + return; + } + + free(opts->output_charset); + free(opts); +} + +int iso_write_opts_set_iso_level(IsoWriteOpts *opts, int level) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + if (level != 1 && level != 2) { + return ISO_WRONG_ARG_VALUE; + } + opts->level = level; + return ISO_SUCCESS; +} + +int iso_write_opts_set_rockridge(IsoWriteOpts *opts, int enable) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->rockridge = enable ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_joliet(IsoWriteOpts *opts, int enable) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->joliet = enable ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_iso1999(IsoWriteOpts *opts, int enable) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->iso1999 = enable ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_omit_version_numbers(IsoWriteOpts *opts, int omit) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->omit_version_numbers = omit ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_allow_deep_paths(IsoWriteOpts *opts, int allow) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->allow_deep_paths = allow ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_allow_longer_paths(IsoWriteOpts *opts, int allow) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->allow_longer_paths = allow ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_max_37_char_filenames(IsoWriteOpts *opts, int allow) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->max_37_char_filenames = allow ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_no_force_dots(IsoWriteOpts *opts, int no) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->no_force_dots = no ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_allow_lowercase(IsoWriteOpts *opts, int allow) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->allow_lowercase = allow ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_allow_full_ascii(IsoWriteOpts *opts, int allow) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->allow_full_ascii = allow ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_relaxed_vol_atts(IsoWriteOpts *opts, int allow) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->relaxed_vol_atts = allow ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_joliet_longer_paths(IsoWriteOpts *opts, int allow) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->joliet_longer_paths = allow ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_sort_files(IsoWriteOpts *opts, int sort) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->sort_files = sort ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_replace_mode(IsoWriteOpts *opts, int dir_mode, + int file_mode, int uid, int gid) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + if (dir_mode < 0 || dir_mode > 2) { + return ISO_WRONG_ARG_VALUE; + } + if (file_mode < 0 || file_mode > 2) { + return ISO_WRONG_ARG_VALUE; + } + if (uid < 0 || uid > 2) { + return ISO_WRONG_ARG_VALUE; + } + if (gid < 0 || gid > 2) { + return ISO_WRONG_ARG_VALUE; + } + opts->replace_dir_mode = dir_mode; + opts->replace_file_mode = file_mode; + opts->replace_uid = uid; + opts->replace_gid = gid; + return ISO_SUCCESS; +} + +int iso_write_opts_set_default_dir_mode(IsoWriteOpts *opts, mode_t dir_mode) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->dir_mode = dir_mode; + return ISO_SUCCESS; +} + +int iso_write_opts_set_default_file_mode(IsoWriteOpts *opts, mode_t file_mode) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->file_mode = file_mode; + return ISO_SUCCESS; +} + +int iso_write_opts_set_default_uid(IsoWriteOpts *opts, uid_t uid) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->uid = uid; + return ISO_SUCCESS; +} + +int iso_write_opts_set_default_gid(IsoWriteOpts *opts, gid_t gid) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->gid = gid; + return ISO_SUCCESS; +} + +int iso_write_opts_set_replace_timestamps(IsoWriteOpts *opts, int replace) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + if (replace < 0 || replace > 2) { + return ISO_WRONG_ARG_VALUE; + } + opts->replace_timestamps = replace; + return ISO_SUCCESS; +} + +int iso_write_opts_set_default_timestamp(IsoWriteOpts *opts, time_t timestamp) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->timestamp = timestamp; + return ISO_SUCCESS; +} + +int iso_write_opts_set_always_gmt(IsoWriteOpts *opts, int gmt) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->always_gmt = gmt ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_output_charset(IsoWriteOpts *opts, const char *charset) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->output_charset = charset ? strdup(charset) : NULL; + return ISO_SUCCESS; +} + +int iso_write_opts_set_appendable(IsoWriteOpts *opts, int appendable) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->appendable = appendable ? 1 : 0; + return ISO_SUCCESS; +} + +int iso_write_opts_set_ms_block(IsoWriteOpts *opts, uint32_t ms_block) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->ms_block = ms_block; + return ISO_SUCCESS; +} + +int iso_write_opts_set_overwrite_buf(IsoWriteOpts *opts, uint8_t *overwrite) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->overwrite = overwrite; + return ISO_SUCCESS; +} + +int iso_write_opts_set_fifo_size(IsoWriteOpts *opts, size_t fifo_size) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + if (fifo_size < 32) { + return ISO_WRONG_ARG_VALUE; + } + opts->fifo_size = fifo_size; + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/ecma119.h b/libisofs/branches/thomas/libisofs/ecma119.h new file mode 100644 index 00000000..ffb16116 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/ecma119.h @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#ifndef LIBISO_ECMA119_H_ +#define LIBISO_ECMA119_H_ + +#include "libisofs.h" +#include "util.h" +#include "buffer.h" + +#include +#include + +#define BLOCK_SIZE 2048 + +/** + * Holds the options for the image generation. + */ +struct iso_write_opts { + + int level; /**< ISO level to write at. (ECMA-119, 10) */ + + /** Which extensions to support. */ + unsigned int rockridge :1; + unsigned int joliet :1; + unsigned int iso1999 :1; + + /* allways write timestamps in GMT */ + unsigned int always_gmt :1; + + /* + * Relaxed constraints. Setting any of these to 1 break the specifications, + * but it is supposed to work on most moderns systems. Use with caution. + */ + + /** + * Omit the version number (";1") at the end of the ISO-9660 identifiers. + * Version numbers are usually not used. + */ + unsigned int omit_version_numbers :1; + + /** + * Allow ISO-9660 directory hierarchy to be deeper than 8 levels. + */ + unsigned int allow_deep_paths :1; + + /** + * Allow path in the ISO-9660 tree to have more than 255 characters. + */ + unsigned int allow_longer_paths :1; + + /** + * Allow a single file or directory hierarchy to have up to 37 characters. + * This is larger than the 31 characters allowed by ISO level 2, and the + * extra space is taken from the version number, so this also forces + * omit_version_numbers. + */ + unsigned int max_37_char_filenames :1; + + /** + * ISO-9660 forces filenames to have a ".", that separates file name from + * extension. libisofs adds it if original filename doesn't has one. Set + * this to 1 to prevent this behavior + */ + unsigned int no_force_dots :1; + + /** + * Allow lowercase characters in ISO-9660 filenames. By default, only + * uppercase characters, numbers and a few other characters are allowed. + */ + unsigned int allow_lowercase :1; + + /** + * Allow all ASCII characters to be appear on an ISO-9660 filename. Note + * that "/" and "\0" characters are never allowed, even in RR names. + */ + unsigned int allow_full_ascii :1; + + /** + * Allow all characters to be part of Volume and Volset identifiers on + * the Primary Volume Descriptor. This breaks ISO-9660 contraints, but + * should work on modern systems. + */ + unsigned int relaxed_vol_atts :1; + + /** + * Allow paths in the Joliet tree to have more than 240 characters. + */ + unsigned int joliet_longer_paths :1; + + /** If files should be sorted based on their weight. */ + unsigned int sort_files :1; + + /** + * The following options set the default values for files and directory + * permissions, gid and uid. All these take one of three values: 0, 1 or 2. + * If 0, the corresponding attribute will be kept as setted in the IsoNode. + * Unless you have changed it, it corresponds to the value on disc, so it + * is suitable for backup purposes. If set to 1, the corresponding attrib. + * will be changed by a default suitable value. Finally, if you set it to + * 2, the attrib. will be changed with the value specified in the options + * below. Note that for mode attributes, only the permissions are set, the + * file type remains unchanged. + */ + unsigned int replace_dir_mode :2; + unsigned int replace_file_mode :2; + unsigned int replace_uid :2; + unsigned int replace_gid :2; + + mode_t dir_mode; /** Mode to use on dirs when replace_dir_mode == 2. */ + mode_t file_mode; /** Mode to use on files when replace_file_mode == 2. */ + uid_t uid; /** uid to use when replace_uid == 2. */ + gid_t gid; /** gid to use when replace_gid == 2. */ + + /** + * 0 to use IsoNode timestamps, 1 to use recording time, 2 to use + * values from timestamp field. This has only meaning if RR extensions + * are enabled. + */ + unsigned int replace_timestamps :2; + time_t timestamp; + + /** + * Charset for the RR filenames that will be created. + * NULL to use default charset, the locale one. + */ + char *output_charset; + + /** + * This flags control the type of the image to create. Libisofs support + * two kind of images: stand-alone and appendable. + * + * A stand-alone image is an image that is valid alone, and that can be + * mounted by its own. This is the kind of image you will want to create + * in most cases. A stand-alone image can be burned in an empty CD or DVD, + * or write to an .iso file for future burning or distribution. + * + * On the other side, an appendable image is not self contained, it refers + * to serveral files that are stored outside the image. Its usage is for + * multisession discs, where you add data in a new session, while the + * previous session data can still be accessed. In those cases, the old + * data is not written again. Instead, the new image refers to it, and thus + * it's only valid when appended to the original. Note that in those cases + * the image will be written after the original, and thus you will want + * to use a ms_block greater than 0. + * + * Note that if you haven't import a previous image (by means of + * iso_image_import()), the image will always be a stand-alone image, as + * there is no previous data to refer to. + */ + unsigned int appendable : 1; + + /** + * Start block of the image. It is supposed to be the lba where the first + * block of the image will be written on disc. All references inside the + * ISO image will take this into account, thus providing a mountable image. + * + * For appendable images, that are written to a new session, you should + * pass here the lba of the next writable address on disc. + * + * In stand alone images this is usually 0. However, you may want to + * provide a different ms_block if you don't plan to burn the image in the + * first session on disc, such as in some CD-Extra disc whether the data + * image is written in a new session after some audio tracks. + */ + uint32_t ms_block; + + /** + * When not NULL, it should point to a buffer of at least 64KiB, where + * libisofs will write the contents that should be written at the beginning + * of a overwriteable media, to grow the image. The growing of an image is + * a way, used by first time in growisofs by Andy Polyakov, to allow the + * appending of new data to non-multisession media, such as DVD+RW, in the + * same way you append a new session to a multisession disc, i.e., without + * need to write again the contents of the previous image. + * + * Note that if you want this kind of image growing, you will also need to + * set appendable to "1" and provide a valid ms_block after the previous + * image. + * + * You should initialize the buffer either with 0s, or with the contents of + * the first blocks of the image you're growing. In most cases, 0 is good + * enought. + */ + uint8_t *overwrite; + + /** + * Size, in number of blocks, of the FIFO buffer used between the writer + * thread and the burn_source. You have to provide at least a 32 blocks + * buffer. + */ + size_t fifo_size; +}; + +typedef struct ecma119_image Ecma119Image; +typedef struct ecma119_node Ecma119Node; +typedef struct joliet_node JolietNode; +typedef struct iso1999_node Iso1999Node; +typedef struct Iso_File_Src IsoFileSrc; +typedef struct Iso_Image_Writer IsoImageWriter; + +struct ecma119_image +{ + IsoImage *image; + Ecma119Node *root; + + unsigned int iso_level :2; + + /* extensions */ + unsigned int rockridge :1; + unsigned int joliet :1; + unsigned int eltorito :1; + unsigned int iso1999 :1; + + /* allways write timestamps in GMT */ + unsigned int always_gmt :1; + + /* relaxed constraints */ + unsigned int omit_version_numbers :1; + unsigned int allow_deep_paths :1; + unsigned int allow_longer_paths :1; + unsigned int max_37_char_filenames :1; + unsigned int no_force_dots :1; + unsigned int allow_lowercase :1; + unsigned int allow_full_ascii :1; + + unsigned int relaxed_vol_atts : 1; + + /** Allow paths on Joliet tree to be larger than 240 bytes */ + unsigned int joliet_longer_paths :1; + + /* + * Mode replace. If one of these flags is set, the correspodent values are + * replaced with values below. + */ + unsigned int replace_uid :1; + unsigned int replace_gid :1; + unsigned int replace_file_mode :1; + unsigned int replace_dir_mode :1; + unsigned int replace_timestamps :1; + + uid_t uid; + gid_t gid; + mode_t file_mode; + mode_t dir_mode; + time_t timestamp; + + /** + * if sort files or not. Sorting is based of the weight of each file + */ + int sort_files; + + /** + * In the CD, each file must have an unique inode number. So each + * time we add a new file, this is incremented. + */ + ino_t ino; + + char *input_charset; + char *output_charset; + + unsigned int appendable : 1; + uint32_t ms_block; /**< start block for a ms image */ + time_t now; /**< Time at which writing began. */ + + /** Total size of the output. This only includes the current volume. */ + off_t total_size; + uint32_t vol_space_size; + + /* Bytes already written, just for progress notification */ + off_t bytes_written; + int percent_written; + + /* + * Block being processed, either during image writing or structure + * size calculation. + */ + uint32_t curblock; + + /* + * number of dirs in ECMA-119 tree, computed together with dir position, + * and needed for path table computation in a efficient way + */ + size_t ndirs; + uint32_t path_table_size; + uint32_t l_path_table_pos; + uint32_t m_path_table_pos; + + /* + * Joliet related information + */ + JolietNode *joliet_root; + size_t joliet_ndirs; + uint32_t joliet_path_table_size; + uint32_t joliet_l_path_table_pos; + uint32_t joliet_m_path_table_pos; + + /* + * ISO 9660:1999 related information + */ + Iso1999Node *iso1999_root; + size_t iso1999_ndirs; + uint32_t iso1999_path_table_size; + uint32_t iso1999_l_path_table_pos; + uint32_t iso1999_m_path_table_pos; + + /* + * El-Torito related information + */ + struct el_torito_boot_catalog *catalog; + IsoFileSrc *cat; /**< location of the boot catalog in the new image */ + IsoFileSrc *bootimg; /**< location of the boot image in the new image */ + + /* + * Number of pad blocks that we need to write. Padding blocks are blocks + * filled by 0s that we put between the directory structures and the file + * data. These padding blocks are added by libisofs to improve the handling + * of image growing. The idea is that the first blocks in the image are + * overwritten with the volume descriptors of the new image. These first + * blocks usually correspond to the volume descriptors and directory + * structure of the old image, and can be safety overwritten. However, + * with very small images they might correspond to valid data. To ensure + * this never happens, what we do is to add padding bytes, to ensure no + * file data is written in the first 64 KiB, that are the bytes we usually + * overwrite. + */ + uint32_t pad_blocks; + + size_t nwriters; + IsoImageWriter **writers; + + /* tree of files sources */ + IsoRBTree *files; + + /* Buffer for communication between burn_source and writer thread */ + IsoRingBuffer *buffer; + + /* writer thread descriptor */ + pthread_t wthread; + pthread_attr_t th_attr; +}; + +#define BP(a,b) [(b) - (a) + 1] + +/* ECMA-119, 8.4 */ +struct ecma119_pri_vol_desc +{ + uint8_t vol_desc_type BP(1, 1); + uint8_t std_identifier BP(2, 6); + uint8_t vol_desc_version BP(7, 7); + uint8_t unused1 BP(8, 8); + uint8_t system_id BP(9, 40); + uint8_t volume_id BP(41, 72); + uint8_t unused2 BP(73, 80); + uint8_t vol_space_size BP(81, 88); + uint8_t unused3 BP(89, 120); + uint8_t vol_set_size BP(121, 124); + uint8_t vol_seq_number BP(125, 128); + uint8_t block_size BP(129, 132); + uint8_t path_table_size BP(133, 140); + uint8_t l_path_table_pos BP(141, 144); + uint8_t opt_l_path_table_pos BP(145, 148); + uint8_t m_path_table_pos BP(149, 152); + uint8_t opt_m_path_table_pos BP(153, 156); + uint8_t root_dir_record BP(157, 190); + uint8_t vol_set_id BP(191, 318); + uint8_t publisher_id BP(319, 446); + uint8_t data_prep_id BP(447, 574); + uint8_t application_id BP(575, 702); + uint8_t copyright_file_id BP(703, 739); + uint8_t abstract_file_id BP(740, 776); + uint8_t bibliographic_file_id BP(777, 813); + uint8_t vol_creation_time BP(814, 830); + uint8_t vol_modification_time BP(831, 847); + uint8_t vol_expiration_time BP(848, 864); + uint8_t vol_effective_time BP(865, 881); + uint8_t file_structure_version BP(882, 882); + uint8_t reserved1 BP(883, 883); + uint8_t app_use BP(884, 1395); + uint8_t reserved2 BP(1396, 2048); +}; + +/* ECMA-119, 8.5 */ +struct ecma119_sup_vol_desc +{ + uint8_t vol_desc_type BP(1, 1); + uint8_t std_identifier BP(2, 6); + uint8_t vol_desc_version BP(7, 7); + uint8_t vol_flags BP(8, 8); + uint8_t system_id BP(9, 40); + uint8_t volume_id BP(41, 72); + uint8_t unused2 BP(73, 80); + uint8_t vol_space_size BP(81, 88); + uint8_t esc_sequences BP(89, 120); + uint8_t vol_set_size BP(121, 124); + uint8_t vol_seq_number BP(125, 128); + uint8_t block_size BP(129, 132); + uint8_t path_table_size BP(133, 140); + uint8_t l_path_table_pos BP(141, 144); + uint8_t opt_l_path_table_pos BP(145, 148); + uint8_t m_path_table_pos BP(149, 152); + uint8_t opt_m_path_table_pos BP(153, 156); + uint8_t root_dir_record BP(157, 190); + uint8_t vol_set_id BP(191, 318); + uint8_t publisher_id BP(319, 446); + uint8_t data_prep_id BP(447, 574); + uint8_t application_id BP(575, 702); + uint8_t copyright_file_id BP(703, 739); + uint8_t abstract_file_id BP(740, 776); + uint8_t bibliographic_file_id BP(777, 813); + uint8_t vol_creation_time BP(814, 830); + uint8_t vol_modification_time BP(831, 847); + uint8_t vol_expiration_time BP(848, 864); + uint8_t vol_effective_time BP(865, 881); + uint8_t file_structure_version BP(882, 882); + uint8_t reserved1 BP(883, 883); + uint8_t app_use BP(884, 1395); + uint8_t reserved2 BP(1396, 2048); +}; + +/* ECMA-119, 8.2 */ +struct ecma119_boot_rec_vol_desc +{ + uint8_t vol_desc_type BP(1, 1); + uint8_t std_identifier BP(2, 6); + uint8_t vol_desc_version BP(7, 7); + uint8_t boot_sys_id BP(8, 39); + uint8_t boot_id BP(40, 71); + uint8_t boot_catalog BP(72, 75); + uint8_t unused BP(76, 2048); +}; + +/* ECMA-119, 9.1 */ +struct ecma119_dir_record +{ + uint8_t len_dr BP(1, 1); + uint8_t len_xa BP(2, 2); + uint8_t block BP(3, 10); + uint8_t length BP(11, 18); + uint8_t recording_time BP(19, 25); + uint8_t flags BP(26, 26); + uint8_t file_unit_size BP(27, 27); + uint8_t interleave_gap_size BP(28, 28); + uint8_t vol_seq_number BP(29, 32); + uint8_t len_fi BP(33, 33); + uint8_t file_id BP(34, 34); /* 34 to 33+len_fi */ + /* padding field (if len_fi is even) */ + /* system use (len_dr - len_su + 1 to len_dr) */ +}; + +/* ECMA-119, 9.4 */ +struct ecma119_path_table_record +{ + uint8_t len_di BP(1, 1); + uint8_t len_xa BP(2, 2); + uint8_t block BP(3, 6); + uint8_t parent BP(7, 8); + uint8_t dir_id BP(9, 9); /* 9 to 8+len_di */ + /* padding field (if len_di is odd) */ +}; + +/* ECMA-119, 8.3 */ +struct ecma119_vol_desc_terminator +{ + uint8_t vol_desc_type BP(1, 1); + uint8_t std_identifier BP(2, 6); + uint8_t vol_desc_version BP(7, 7); + uint8_t reserved BP(8, 2048); +}; + +#endif /*LIBISO_ECMA119_H_*/ diff --git a/libisofs/branches/thomas/libisofs/ecma119_tree.c b/libisofs/branches/thomas/libisofs/ecma119_tree.c new file mode 100644 index 00000000..943fa0a4 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/ecma119_tree.c @@ -0,0 +1,846 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "ecma119_tree.h" +#include "ecma119.h" +#include "node.h" +#include "util.h" +#include "filesrc.h" +#include "messages.h" +#include "image.h" +#include "stream.h" +#include "eltorito.h" + +#include +#include +#include + +static +int get_iso_name(Ecma119Image *img, IsoNode *iso, char **name) +{ + int ret, relaxed; + char *ascii_name; + char *isoname= NULL; + + if (iso->name == NULL) { + /* it is not necessarily an error, it can be the root */ + return ISO_SUCCESS; + } + + ret = str2ascii(img->input_charset, iso->name, &ascii_name); + if (ret < 0) { + iso_msg_submit(img->image->id, ret, 0, "Can't convert %s", iso->name); + return ret; + } + + if (img->allow_full_ascii) { + relaxed = 2; + } else { + relaxed = (int)img->allow_lowercase; + } + if (iso->type == LIBISO_DIR) { + if (img->max_37_char_filenames) { + isoname = iso_r_dirid(ascii_name, 37, relaxed); + } else if (img->iso_level == 1) { + if (relaxed) { + isoname = iso_r_dirid(ascii_name, 8, relaxed); + } else { + isoname = iso_1_dirid(ascii_name); + } + } else { + if (relaxed) { + isoname = iso_r_dirid(ascii_name, 8, relaxed); + } else { + isoname = iso_2_dirid(ascii_name); + } + } + } else { + if (img->max_37_char_filenames) { + isoname = iso_r_fileid(ascii_name, 36, relaxed, + img->no_force_dots ? 0 : 1); + } else if (img->iso_level == 1) { + if (relaxed) { + isoname = iso_r_fileid(ascii_name, 11, relaxed, + img->no_force_dots ? 0 : 1); + } else { + isoname = iso_1_fileid(ascii_name); + } + } else { + if (relaxed) { + isoname = iso_r_fileid(ascii_name, 30, relaxed, + img->no_force_dots ? 0 : 1); + } else { + isoname = iso_2_fileid(ascii_name); + } + } + } + free(ascii_name); + if (isoname != NULL) { + *name = isoname; + return ISO_SUCCESS; + } else { + /* + * only possible if mem error, as check for empty names is done + * in public tree + */ + return ISO_OUT_OF_MEM; + } +} + +static +int create_ecma119_node(Ecma119Image *img, IsoNode *iso, Ecma119Node **node) +{ + Ecma119Node *ecma; + + ecma = calloc(1, sizeof(Ecma119Node)); + if (ecma == NULL) { + return ISO_OUT_OF_MEM; + } + + /* take a ref to the IsoNode */ + ecma->node = iso; + iso_node_ref(iso); + + /* TODO #00009 : add true support for harlinks and inode numbers */ + ecma->nlink = 1; + ecma->ino = ++img->ino; + + *node = ecma; + return ISO_SUCCESS; +} + +/** + * Create a new ECMA-119 node representing a directory from a iso directory + * node. + */ +static +int create_dir(Ecma119Image *img, IsoDir *iso, Ecma119Node **node) +{ + int ret; + Ecma119Node **children; + struct ecma119_dir_info *dir_info; + + children = calloc(1, sizeof(void*) * iso->nchildren); + if (children == NULL) { + return ISO_OUT_OF_MEM; + } + + dir_info = calloc(1, sizeof(struct ecma119_dir_info)); + if (dir_info == NULL) { + free(children); + return ISO_OUT_OF_MEM; + } + + ret = create_ecma119_node(img, (IsoNode*)iso, node); + if (ret < 0) { + free(children); + free(dir_info); + return ret; + } + (*node)->type = ECMA119_DIR; + (*node)->info.dir = dir_info; + (*node)->info.dir->nchildren = 0; + (*node)->info.dir->children = children; + return ISO_SUCCESS; +} + +/** + * Create a new ECMA-119 node representing a regular file from a iso file + * node. + */ +static +int create_file(Ecma119Image *img, IsoFile *iso, Ecma119Node **node) +{ + int ret; + IsoFileSrc *src; + off_t size; + + size = iso_stream_get_size(iso->stream); + if (size > (off_t)0xffffffff) { + return iso_msg_submit(img->image->id, ISO_FILE_TOO_BIG, 0, + "File \"%s\" can't be added to image because " + "is greater than 4GB", iso->node.name); + } + + ret = iso_file_src_create(img, iso, &src); + if (ret < 0) { + return ret; + } + + ret = create_ecma119_node(img, (IsoNode*)iso, node); + if (ret < 0) { + /* + * the src doesn't need to be freed, it is free together with + * the Ecma119Image + */ + return ret; + } + (*node)->type = ECMA119_FILE; + (*node)->info.file = src; + + return ret; +} + +/** + * Create a new ECMA-119 node representing a regular file from an El-Torito + * boot catalog + */ +static +int create_boot_cat(Ecma119Image *img, IsoBoot *iso, Ecma119Node **node) +{ + int ret; + IsoFileSrc *src; + + ret = el_torito_catalog_file_src_create(img, &src); + if (ret < 0) { + return ret; + } + + ret = create_ecma119_node(img, (IsoNode*)iso, node); + if (ret < 0) { + /* + * the src doesn't need to be freed, it is free together with + * the Ecma119Image + */ + return ret; + } + (*node)->type = ECMA119_FILE; + (*node)->info.file = src; + + return ret; +} + +/** + * Create a new ECMA-119 node representing a symbolic link from a iso symlink + * node. + */ +static +int create_symlink(Ecma119Image *img, IsoSymlink *iso, Ecma119Node **node) +{ + int ret; + + ret = create_ecma119_node(img, (IsoNode*)iso, node); + if (ret < 0) { + return ret; + } + (*node)->type = ECMA119_SYMLINK; + return ISO_SUCCESS; +} + +/** + * Create a new ECMA-119 node representing a special file. + */ +static +int create_special(Ecma119Image *img, IsoSpecial *iso, Ecma119Node **node) +{ + int ret; + + ret = create_ecma119_node(img, (IsoNode*)iso, node); + if (ret < 0) { + return ret; + } + (*node)->type = ECMA119_SPECIAL; + return ISO_SUCCESS; +} + +void ecma119_node_free(Ecma119Node *node) +{ + if (node == NULL) { + return; + } + if (node->type == ECMA119_DIR) { + int i; + for (i = 0; i < node->info.dir->nchildren; i++) { + ecma119_node_free(node->info.dir->children[i]); + } + free(node->info.dir->children); + free(node->info.dir); + } + free(node->iso_name); + iso_node_unref(node->node); + free(node); +} + +/** + * + * @return + * 1 success, 0 node ignored, < 0 error + * + */ +static +int create_tree(Ecma119Image *image, IsoNode *iso, Ecma119Node **tree, + int depth, int pathlen) +{ + int ret; + Ecma119Node *node; + int max_path; + char *iso_name= NULL; + + if (image == NULL || iso == NULL || tree == NULL) { + return ISO_NULL_POINTER; + } + + if (iso->hidden & LIBISO_HIDE_ON_RR) { + /* file will be ignored */ + return 0; + } + ret = get_iso_name(image, iso, &iso_name); + if (ret < 0) { + return ret; + } + max_path = pathlen + 1 + (iso_name ? strlen(iso_name) : 0); + if (!image->rockridge) { + if ((iso->type == LIBISO_DIR && depth > 8) && !image->allow_deep_paths) { + free(iso_name); + return iso_msg_submit(image->image->id, ISO_FILE_IMGPATH_WRONG, 0, + "File \"%s\" can't be added, because directory depth " + "is greater than 8.", iso->name); + } else if (max_path > 255 && !image->allow_longer_paths) { + free(iso_name); + return iso_msg_submit(image->image->id, ISO_FILE_IMGPATH_WRONG, 0, + "File \"%s\" can't be added, because path length " + "is greater than 255 characters", iso->name); + } + } + + switch (iso->type) { + case LIBISO_FILE: + ret = create_file(image, (IsoFile*)iso, &node); + break; + case LIBISO_SYMLINK: + if (image->rockridge) { + ret = create_symlink(image, (IsoSymlink*)iso, &node); + } else { + /* symlinks are only supported when RR is enabled */ + ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0, + "File \"%s\" ignored. Symlinks need RockRidge extensions.", + iso->name); + } + break; + case LIBISO_SPECIAL: + if (image->rockridge) { + ret = create_special(image, (IsoSpecial*)iso, &node); + } else { + /* symlinks are only supported when RR is enabled */ + ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0, + "File \"%s\" ignored. Special files need RockRidge extensions.", + iso->name); + } + break; + case LIBISO_BOOT: + if (image->eltorito) { + ret = create_boot_cat(image, (IsoBoot*)iso, &node); + } else { + /* log and ignore */ + ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0, + "El-Torito catalog found on a image without El-Torito.", + iso->name); + } + break; + case LIBISO_DIR: + { + IsoNode *pos; + IsoDir *dir = (IsoDir*)iso; + ret = create_dir(image, dir, &node); + if (ret < 0) { + return ret; + } + pos = dir->children; + while (pos) { + int cret; + Ecma119Node *child; + cret = create_tree(image, pos, &child, depth + 1, max_path); + if (cret < 0) { + /* error */ + ecma119_node_free(node); + ret = cret; + break; + } else if (cret == ISO_SUCCESS) { + /* add child to this node */ + int nchildren = node->info.dir->nchildren++; + node->info.dir->children[nchildren] = child; + child->parent = node; + } + pos = pos->next; + } + } + break; + default: + /* should never happen */ + return ISO_ASSERT_FAILURE; + } + if (ret <= 0) { + free(iso_name); + return ret; + } + node->iso_name = iso_name; + *tree = node; + return ISO_SUCCESS; +} + +/** + * Compare the iso name of two ECMA-119 nodes + */ +static +int cmp_node_name(const void *f1, const void *f2) +{ + Ecma119Node *f = *((Ecma119Node**)f1); + Ecma119Node *g = *((Ecma119Node**)f2); + return strcmp(f->iso_name, g->iso_name); +} + +/** + * Sorts a the children of each directory in the ECMA-119 tree represented + * by \p root, acording to the order specified in ECMA-119, section 9.3. + */ +static +void sort_tree(Ecma119Node *root) +{ + size_t i; + + qsort(root->info.dir->children, root->info.dir->nchildren, sizeof(void*), + cmp_node_name); + for (i = 0; i < root->info.dir->nchildren; i++) { + if (root->info.dir->children[i]->type == ECMA119_DIR) + sort_tree(root->info.dir->children[i]); + } +} + +/** + * Ensures that the ISO name of each children of the given dir is unique, + * changing some of them if needed. + * It also ensures that resulting filename is always <= than given + * max_name_len, including extension. If needed, the extension will be reduced, + * but never under 3 characters. + */ +static +int mangle_single_dir(Ecma119Image *img, Ecma119Node *dir, int max_file_len, + int max_dir_len) +{ + int ret; + int i, nchildren; + Ecma119Node **children; + IsoHTable *table; + int need_sort = 0; + + nchildren = dir->info.dir->nchildren; + children = dir->info.dir->children; + + /* a hash table will temporary hold the names, for fast searching */ + ret = iso_htable_create((nchildren * 100) / 80, iso_str_hash, + (compare_function_t)strcmp, &table); + if (ret < 0) { + return ret; + } + for (i = 0; i < nchildren; ++i) { + char *name = children[i]->iso_name; + ret = iso_htable_add(table, name, name); + if (ret < 0) { + goto mangle_cleanup; + } + } + + for (i = 0; i < nchildren; ++i) { + char *name, *ext; + char full_name[40]; + int max; /* computed max len for name, without extension */ + int j = i; + int digits = 1; /* characters to change per name */ + + /* first, find all child with same name */ + while (j + 1 < nchildren && !cmp_node_name(children + i, children + j + + 1)) { + ++j; + } + if (j == i) { + /* name is unique */ + continue; + } + + /* + * A max of 7 characters is good enought, it allows handling up to + * 9,999,999 files with same name. We can increment this to + * max_name_len, but the int_pow() function must then be modified + * to return a bigger integer. + */ + while (digits < 8) { + int ok, k; + char *dot; + int change = 0; /* number to be written */ + + /* copy name to buffer */ + strcpy(full_name, children[i]->iso_name); + + /* compute name and extension */ + dot = strrchr(full_name, '.'); + if (dot != NULL && children[i]->type != ECMA119_DIR) { + + /* + * File (not dir) with extension + * Note that we don't need to check for placeholders, as + * tree reparent happens later, so no placeholders can be + * here at this time. + */ + int extlen; + full_name[dot - full_name] = '\0'; + name = full_name; + ext = dot + 1; + + /* + * For iso level 1 we force ext len to be 3, as name + * can't grow on the extension space + */ + extlen = (max_file_len == 12) ? 3 : strlen(ext); + max = max_file_len - extlen - 1 - digits; + if (max <= 0) { + /* this can happen if extension is too long */ + if (extlen + max > 3) { + /* + * reduce extension len, to give name an extra char + * note that max is negative or 0 + */ + extlen = extlen + max - 1; + ext[extlen] = '\0'; + max = max_file_len - extlen - 1 - digits; + } else { + /* + * error, we don't support extensions < 3 + * This can't happen with current limit of digits. + */ + ret = ISO_ERROR; + goto mangle_cleanup; + } + } + /* ok, reduce name by digits */ + if (name + max < dot) { + name[max] = '\0'; + } + } else { + /* Directory, or file without extension */ + if (children[i]->type == ECMA119_DIR) { + max = max_dir_len - digits; + dot = NULL; /* dots have no meaning in dirs */ + } else { + max = max_file_len - digits; + } + name = full_name; + if (max < strlen(name)) { + name[max] = '\0'; + } + /* let ext be an empty string */ + ext = name + strlen(name); + } + + ok = 1; + /* change name of each file */ + for (k = i; k <= j; ++k) { + char tmp[40]; + char fmt[16]; + if (dot != NULL) { + sprintf(fmt, "%%s%%0%dd.%%s", digits); + } else { + sprintf(fmt, "%%s%%0%dd%%s", digits); + } + while (1) { + sprintf(tmp, fmt, name, change, ext); + ++change; + if (change > int_pow(10, digits)) { + ok = 0; + break; + } + if (!iso_htable_get(table, tmp, NULL)) { + /* the name is unique, so it can be used */ + break; + } + } + if (ok) { + char *new = strdup(tmp); + if (new == NULL) { + ret = ISO_OUT_OF_MEM; + goto mangle_cleanup; + } + iso_msg_debug(img->image->id, "\"%s\" renamed to \"%s\"", + children[k]->iso_name, new); + + iso_htable_remove_ptr(table, children[k]->iso_name, NULL); + free(children[k]->iso_name); + children[k]->iso_name = new; + iso_htable_add(table, new, new); + + /* + * if we change a name we need to sort again children + * at the end + */ + need_sort = 1; + } else { + /* we need to increment digits */ + break; + } + } + if (ok) { + break; + } else { + ++digits; + } + } + if (digits == 8) { + ret = ISO_MANGLE_TOO_MUCH_FILES; + goto mangle_cleanup; + } + i = j; + } + + /* + * If needed, sort again the files inside dir + */ + if (need_sort) { + qsort(children, nchildren, sizeof(void*), cmp_node_name); + } + + ret = ISO_SUCCESS; + +mangle_cleanup : ; + iso_htable_destroy(table, NULL); + return ret; +} + +static +int mangle_dir(Ecma119Image *img, Ecma119Node *dir, int max_file_len, + int max_dir_len) +{ + int ret; + size_t i; + + ret = mangle_single_dir(img, dir, max_file_len, max_dir_len); + if (ret < 0) { + return ret; + } + + /* recurse */ + for (i = 0; i < dir->info.dir->nchildren; ++i) { + if (dir->info.dir->children[i]->type == ECMA119_DIR) { + ret = mangle_dir(img, dir->info.dir->children[i], max_file_len, + max_dir_len); + if (ret < 0) { + /* error */ + return ret; + } + } + } + return ISO_SUCCESS; +} + +static +int mangle_tree(Ecma119Image *img, int recurse) +{ + int max_file, max_dir; + + if (img->max_37_char_filenames) { + max_file = max_dir = 37; + } else if (img->iso_level == 1) { + max_file = 12; /* 8 + 3 + 1 */ + max_dir = 8; + } else { + max_file = max_dir = 31; + } + if (recurse) { + return mangle_dir(img, img->root, max_file, max_dir); + } else { + return mangle_single_dir(img, img->root, max_file, max_dir); + } +} + +/** + * Create a new ECMA-119 node representing a placeholder for a relocated + * dir. + * + * See IEEE P1282, section 4.1.5 for details + */ +static +int create_placeholder(Ecma119Node *parent, Ecma119Node *real, + Ecma119Node **node) +{ + Ecma119Node *ret; + + ret = calloc(1, sizeof(Ecma119Node)); + if (ret == NULL) { + return ISO_OUT_OF_MEM; + } + + /* + * TODO + * If real is a dir, while placeholder is a file, ISO name restricctions + * are different, what to do? + */ + ret->iso_name = strdup(real->iso_name); + if (ret->iso_name == NULL) { + free(ret); + return ISO_OUT_OF_MEM; + } + + /* take a ref to the IsoNode */ + ret->node = real->node; + iso_node_ref(real->node); + ret->parent = parent; + ret->type = ECMA119_PLACEHOLDER; + ret->info.real_me = real; + ret->ino = real->ino; + ret->nlink = real->nlink; + + *node = ret; + return ISO_SUCCESS; +} + +static +size_t max_child_name_len(Ecma119Node *dir) +{ + size_t ret = 0, i; + for (i = 0; i < dir->info.dir->nchildren; i++) { + size_t len = strlen(dir->info.dir->children[i]->iso_name); + ret = MAX(ret, len); + } + return ret; +} + +/** + * Relocates a directory, as specified in Rock Ridge Specification + * (see IEEE P1282, section 4.1.5). This is needed when the number of levels + * on a directory hierarchy exceeds 8, or the length of a path is higher + * than 255 characters, as specified in ECMA-119, section 6.8.2.1 + */ +static +int reparent(Ecma119Node *child, Ecma119Node *parent) +{ + int ret; + size_t i; + Ecma119Node *placeholder; + + /* replace the child in the original parent with a placeholder */ + for (i = 0; i < child->parent->info.dir->nchildren; i++) { + if (child->parent->info.dir->children[i] == child) { + ret = create_placeholder(child->parent, child, &placeholder); + if (ret < 0) { + return ret; + } + child->parent->info.dir->children[i] = placeholder; + break; + } + } + + /* just for debug, this should never happen... */ + if (i == child->parent->info.dir->nchildren) { + return ISO_ASSERT_FAILURE; + } + + /* keep track of the real parent */ + child->info.dir->real_parent = child->parent; + + /* add the child to its new parent */ + child->parent = parent; + parent->info.dir->nchildren++; + parent->info.dir->children = realloc(parent->info.dir->children, + sizeof(void*) * parent->info.dir->nchildren); + parent->info.dir->children[parent->info.dir->nchildren - 1] = child; + return ISO_SUCCESS; +} + +/** + * Reorder the tree, if necessary, to ensure that + * - the depth is at most 8 + * - each path length is at most 255 characters + * This restriction is imposed by ECMA-119 specification (ECMA-119, 6.8.2.1). + * + * @param dir + * Dir we are currently processing + * @param level + * Level of the directory in the hierarchy + * @param pathlen + * Length of the path until dir, including it + * @return + * 1 success, < 0 error + */ +static +int reorder_tree(Ecma119Image *img, Ecma119Node *dir, int level, int pathlen) +{ + int ret; + size_t max_path; + + max_path = pathlen + 1 + max_child_name_len(dir); + + if (level > 8 || max_path > 255) { + ret = reparent(dir, img->root); + if (ret < 0) { + return ret; + } + + /* + * we are appended to the root's children now, so there is no + * need to recurse (the root will hit us again) + */ + } else { + size_t i; + + for (i = 0; i < dir->info.dir->nchildren; i++) { + Ecma119Node *child = dir->info.dir->children[i]; + if (child->type == ECMA119_DIR) { + int newpathlen = pathlen + 1 + strlen(child->iso_name); + ret = reorder_tree(img, child, level + 1, newpathlen); + if (ret < 0) { + return ret; + } + } + } + } + return ISO_SUCCESS; +} + +int ecma119_tree_create(Ecma119Image *img) +{ + int ret; + Ecma119Node *root; + + ret = create_tree(img, (IsoNode*)img->image->root, &root, 1, 0); + if (ret <= 0) { + if (ret == 0) { + /* unexpected error, root ignored!! This can't happen */ + ret = ISO_ASSERT_FAILURE; + } + return ret; + } + img->root = root; + + iso_msg_debug(img->image->id, "Sorting the low level tree..."); + sort_tree(root); + + iso_msg_debug(img->image->id, "Mangling names..."); + ret = mangle_tree(img, 1); + if (ret < 0) { + return ret; + } + + if (img->rockridge && !img->allow_deep_paths) { + + /* reorder the tree, acording to RRIP, 4.1.5 */ + ret = reorder_tree(img, img->root, 1, 0); + if (ret < 0) { + return ret; + } + + /* + * and we need to remangle the root directory, as the function + * above could insert new directories into the root. + * Note that recurse = 0, as we don't need to recurse. + */ + ret = mangle_tree(img, 0); + if (ret < 0) { + return ret; + } + } + + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/ecma119_tree.h b/libisofs/branches/thomas/libisofs/ecma119_tree.h new file mode 100644 index 00000000..0cd05ee9 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/ecma119_tree.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#ifndef LIBISO_ECMA119_TREE_H_ +#define LIBISO_ECMA119_TREE_H_ + +#include "libisofs.h" +#include "ecma119.h" + +enum ecma119_node_type { + ECMA119_FILE, + ECMA119_DIR, + ECMA119_SYMLINK, + ECMA119_SPECIAL, + ECMA119_PLACEHOLDER +}; + +/** + * Struct with info about a node representing a directory + */ +struct ecma119_dir_info +{ + /* Block where the directory entries will be written on image */ + size_t block; + + size_t nchildren; + Ecma119Node **children; + + /* + * Size of the dir, i.e., sum of the lengths of all directory records. + * It is computed by calc_dir_size() [ecma119.c]. + * Note that this don't include the length of any SUSP Continuation + * Area needed by the dir, but it includes the size of the SUSP entries + * than fit in the directory records System Use Field. + */ + size_t len; + + /** + * Real parent if the dir has been reallocated. NULL otherwise. + */ + Ecma119Node *real_parent; +}; + +/** + * A node for a tree containing all the information necessary for writing + * an ISO9660 volume. + */ +struct ecma119_node +{ + /** + * Name in ASCII, conforming to selected ISO level. + * Version number is not include, it is added on the fly + */ + char *iso_name; + + Ecma119Node *parent; + + IsoNode *node; /*< reference to the iso node */ + + /* TODO #00009 : add true support for harlinks and inode numbers */ + ino_t ino; + nlink_t nlink; + + /**< file, symlink, special, directory or placeholder */ + enum ecma119_node_type type; + union + { + IsoFileSrc *file; + struct ecma119_dir_info *dir; + /** this field points to the relocated directory. */ + Ecma119Node *real_me; + } info; +}; + +/** + * + */ +int ecma119_tree_create(Ecma119Image *img); + +/** + * Free an Ecma119Node, and its children if node is a dir + */ +void ecma119_node_free(Ecma119Node *node); + +#endif /*LIBISO_ECMA119_TREE_H_*/ diff --git a/libisofs/branches/thomas/libisofs/eltorito.c b/libisofs/branches/thomas/libisofs/eltorito.c new file mode 100644 index 00000000..f8e94c99 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/eltorito.c @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "eltorito.h" +#include "stream.h" +#include "fsource.h" +#include "filesrc.h" +#include "image.h" +#include "messages.h" +#include "writer.h" + +#include +#include + +/** + * This table should be written with accuracy values at offset + * 8 of boot image, when used ISOLINUX boot loader + */ +struct boot_info_table { + uint8_t bi_pvd BP(1, 4); /* LBA of primary volume descriptor */ + uint8_t bi_file BP(5, 8); /* LBA of boot file */ + uint8_t bi_length BP(9, 12); /* Length of boot file */ + uint8_t bi_csum BP(13, 16); /* Checksum of boot file */ + uint8_t bi_reserved BP(17, 56); /* Reserved */ +}; + +/** + * Structure for each one of the four entries in a partition table on a + * hard disk image. + */ +struct partition_desc { + uint8_t boot_ind; + uint8_t begin_chs[3]; + uint8_t type; + uint8_t end_chs[3]; + uint8_t start[4]; + uint8_t size[4]; +}; + +/** + * Structures for a Master Boot Record of a hard disk image. + */ +struct hard_disc_mbr { + uint8_t code_area[440]; + uint8_t opt_disk_sg[4]; + uint8_t pad[2]; + struct partition_desc partition[4]; + uint8_t sign1; + uint8_t sign2; +}; + +/** + * Sets the load segment for the initial boot image. This is only for + * no emulation boot images, and is a NOP for other image types. + */ +void el_torito_set_load_seg(ElToritoBootImage *bootimg, short segment) +{ + if (bootimg->type != ELTORITO_NO_EMUL) + return; + bootimg->load_seg = segment; +} + +/** + * Sets the number of sectors (512b) to be load at load segment during + * the initial boot procedure. This is only for no emulation boot images, + * and is a NOP for other image types. + */ +void el_torito_set_load_size(ElToritoBootImage *bootimg, short sectors) +{ + if (bootimg->type != ELTORITO_NO_EMUL) + return; + bootimg->load_size = sectors; +} + +/** + * Marks the specified boot image as not bootable + */ +void el_torito_set_no_bootable(ElToritoBootImage *bootimg) +{ + bootimg->bootable = 0; +} + +/** + * Specifies that this image needs to be patched. This involves the writting + * of a 56 bytes boot information table at offset 8 of the boot image file. + * The original boot image file won't be modified. + * This is needed for isolinux boot images. + */ +void el_torito_patch_isolinux_image(ElToritoBootImage *bootimg) +{ + bootimg->isolinux = 1; +} + +static +int iso_tree_add_boot_node(IsoDir *parent, const char *name, IsoBoot **boot) +{ + IsoBoot *node; + IsoNode **pos; + time_t now; + + if (parent == NULL || name == NULL || boot == NULL) { + return ISO_NULL_POINTER; + } + if (boot) { + *boot = NULL; + } + + /* check if the name is valid */ + if (!iso_node_is_valid_name(name)) { + return ISO_WRONG_ARG_VALUE; + } + + /* find place where to insert */ + pos = &(parent->children); + while (*pos != NULL && strcmp((*pos)->name, name) < 0) { + pos = &((*pos)->next); + } + if (*pos != NULL && !strcmp((*pos)->name, name)) { + /* a node with same name already exists */ + return ISO_NODE_NAME_NOT_UNIQUE; + } + + node = calloc(1, sizeof(IsoBoot)); + if (node == NULL) { + return ISO_OUT_OF_MEM; + } + + node->node.refcount = 1; + node->node.type = LIBISO_BOOT; + node->node.name = strdup(name); + if (node->node.name == NULL) { + free(node); + return ISO_OUT_OF_MEM; + } + + /* atributes from parent */ + node->node.mode = S_IFREG | (parent->node.mode & 0444); + node->node.uid = parent->node.uid; + node->node.gid = parent->node.gid; + node->node.hidden = parent->node.hidden; + + /* current time */ + now = time(NULL); + node->node.atime = now; + node->node.ctime = now; + node->node.mtime = now; + + /* add to dir */ + node->node.parent = parent; + node->node.next = *pos; + *pos = (IsoNode*)node; + + if (boot) { + *boot = node; + } + return ++parent->nchildren; +} + + +static +int create_image(IsoImage *image, const char *image_path, + enum eltorito_boot_media_type type, + struct el_torito_boot_image **bootimg) +{ + int ret; + struct el_torito_boot_image *boot; + int boot_media_type = 0; + int load_sectors = 0; /* number of sector to load */ + unsigned char partition_type = 0; + IsoNode *imgfile; + IsoStream *stream; + + ret = iso_tree_path_to_node(image, image_path, &imgfile); + if (ret < 0) { + return ret; + } + if (ret == 0) { + return ISO_NODE_DOESNT_EXIST; + } + + if (imgfile->type != LIBISO_FILE) { + return ISO_BOOT_IMAGE_NOT_VALID; + } + + stream = ((IsoFile*)imgfile)->stream; + + /* we need to read the image at least two times */ + if (!iso_stream_is_repeatable(stream)) { + return ISO_BOOT_IMAGE_NOT_VALID; + } + + switch (type) { + case ELTORITO_FLOPPY_EMUL: + switch (iso_stream_get_size(stream)) { + case 1200 * 1024: + boot_media_type = 1; /* 1.2 meg diskette */ + break; + case 1440 * 1024: + boot_media_type = 2; /* 1.44 meg diskette */ + break; + case 2880 * 1024: + boot_media_type = 3; /* 2.88 meg diskette */ + break; + default: + iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, + "Invalid image size %d Kb. Must be one of 1.2, 1.44" + "or 2.88 Mb", iso_stream_get_size(stream) / 1024); + return ISO_BOOT_IMAGE_NOT_VALID; + break; + } + /* it seems that for floppy emulation we need to load + * a single sector (512b) */ + load_sectors = 1; + break; + case ELTORITO_HARD_DISC_EMUL: + { + size_t i; + struct hard_disc_mbr mbr; + int used_partition; + + /* read the MBR on disc and get the type of the partition */ + ret = iso_stream_open(stream); + if (ret < 0) { + iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, ret, + "Can't open image file."); + return ret; + } + ret = iso_stream_read(stream, &mbr, sizeof(mbr)); + iso_stream_close(stream); + if (ret != sizeof(mbr)) { + iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, + "Can't read MBR from image file."); + return ret < 0 ? ret : ISO_FILE_READ_ERROR; + } + + /* check valid MBR signature */ + if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) { + iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, + "Invalid MBR. Wrong signature."); + return ISO_BOOT_IMAGE_NOT_VALID; + } + + /* ensure single partition */ + used_partition = -1; + for (i = 0; i < 4; ++i) { + if (mbr.partition[i].type != 0) { + /* it's an used partition */ + if (used_partition != -1) { + iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, + "Invalid MBR. At least 2 partitions: %d and " + "%d, are being used\n", used_partition, i); + return ISO_BOOT_IMAGE_NOT_VALID; + } else + used_partition = i; + } + } + partition_type = mbr.partition[used_partition].type; + } + boot_media_type = 4; + + /* only load the MBR */ + load_sectors = 1; + break; + case ELTORITO_NO_EMUL: + boot_media_type = 0; + break; + } + + boot = calloc(1, sizeof(struct el_torito_boot_image)); + if (boot == NULL) { + return ISO_OUT_OF_MEM; + } + boot->image = (IsoFile*)imgfile; + iso_node_ref(imgfile); /* get our ref */ + boot->bootable = 1; + boot->type = boot_media_type; + boot->load_size = load_sectors; + boot->partition_type = partition_type; + + if (bootimg) { + *bootimg = boot; + } + + return ISO_SUCCESS; +} + +int iso_image_set_boot_image(IsoImage *image, const char *image_path, + enum eltorito_boot_media_type type, + const char *catalog_path, + ElToritoBootImage **boot) +{ + int ret; + struct el_torito_boot_catalog *catalog; + ElToritoBootImage *boot_image= NULL; + IsoBoot *cat_node= NULL; + + if (image == NULL || image_path == NULL || catalog_path == NULL) { + return ISO_NULL_POINTER; + } + if (image->bootcat != NULL) { + return ISO_IMAGE_ALREADY_BOOTABLE; + } + + /* create the node for the catalog */ + { + IsoDir *parent; + char *catdir = NULL, *catname = NULL; + catdir = strdup(catalog_path); + if (catdir == NULL) { + return ISO_OUT_OF_MEM; + } + + /* get both the dir and the name */ + catname = strrchr(catdir, '/'); + if (catname == NULL) { + free(catdir); + return ISO_WRONG_ARG_VALUE; + } + if (catname == catdir) { + /* we are apending catalog to root node */ + parent = image->root; + } else { + IsoNode *p; + catname[0] = '\0'; + ret = iso_tree_path_to_node(image, catdir, &p); + if (ret <= 0) { + free(catdir); + return ret < 0 ? ret : ISO_NODE_DOESNT_EXIST; + } + if (p->type != LIBISO_DIR) { + free(catdir); + return ISO_WRONG_ARG_VALUE; + } + parent = (IsoDir*)p; + } + catname++; + ret = iso_tree_add_boot_node(parent, catname, &cat_node); + free(catdir); + if (ret < 0) { + return ret; + } + } + + /* create the boot image */ + ret = create_image(image, image_path, type, &boot_image); + if (ret < 0) { + goto boot_image_cleanup; + } + + /* creates the catalog with the given image */ + catalog = malloc(sizeof(struct el_torito_boot_catalog)); + if (catalog == NULL) { + ret = ISO_OUT_OF_MEM; + goto boot_image_cleanup; + } + catalog->image = boot_image; + catalog->node = cat_node; + iso_node_ref((IsoNode*)cat_node); + image->bootcat = catalog; + + if (boot) { + *boot = boot_image; + } + + return ISO_SUCCESS; + +boot_image_cleanup:; + if (cat_node) { + iso_node_take((IsoNode*)cat_node); + iso_node_unref((IsoNode*)cat_node); + } + if (boot_image) { + iso_node_unref((IsoNode*)boot_image->image); + free(boot_image); + } + return ret; +} + +/** + * Get El-Torito boot image of an ISO image, if any. + * + * This can be useful, for example, to check if a volume read from a previous + * session or an existing image is bootable. It can also be useful to get + * the image and catalog tree nodes. An application would want those, for + * example, to prevent the user removing it. + * + * Both nodes are owned by libisofs and should not be freed. You can get your + * own ref with iso_node_ref(). You can can also check if the node is already + * on the tree by getting its parent (note that when reading El-Torito info + * from a previous image, the nodes might not be on the tree even if you haven't + * removed them). Remember that you'll need to get a new ref + * (with iso_node_ref()) before inserting them again to the tree, and probably + * you will also need to set the name or permissions. + * + * @param image + * The image from which to get the boot image. + * @param boot + * If not NULL, it will be filled with a pointer to the boot image, if + * any. That object is owned by the IsoImage and should not be freed by + * the user, nor dereferenced once the last reference to the IsoImage was + * disposed via iso_image_unref(). + * @param imgnode + * When not NULL, it will be filled with the image tree node. No extra ref + * is added, you can use iso_node_ref() to get one if you need it. + * @param catnode + * When not NULL, it will be filled with the catnode tree node. No extra + * ref is added, you can use iso_node_ref() to get one if you need it. + * @return + * 1 on success, 0 is the image is not bootable (i.e., it has no El-Torito + * image), < 0 error. + */ +int iso_image_get_boot_image(IsoImage *image, ElToritoBootImage **boot, + IsoFile **imgnode, IsoBoot **catnode) +{ + if (image == NULL) { + return ISO_NULL_POINTER; + } + if (image->bootcat == NULL) { + return 0; + } + + /* ok, image is bootable */ + if (boot) { + *boot = image->bootcat->image; + } + if (imgnode) { + *imgnode = image->bootcat->image->image; + } + if (catnode) { + *catnode = image->bootcat->node; + } + return ISO_SUCCESS; +} + +/** + * Removes the El-Torito bootable image. + * + * The IsoBoot node that acts as placeholder for the catalog is also removed + * for the image tree, if there. + * If the image is not bootable (don't have el-torito boot image) this function + * just returns. + */ +void iso_image_remove_boot_image(IsoImage *image) +{ + if (image == NULL || image->bootcat == NULL) + return; + + /* + * remove catalog node from its parent + * (the reference will be disposed next) + */ + iso_node_take((IsoNode*)image->bootcat->node); + + /* free boot catalog and image, including references to nodes */ + el_torito_boot_catalog_free(image->bootcat); + image->bootcat = NULL; +} + +void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat) +{ + struct el_torito_boot_image *image; + + if (cat == NULL) { + return; + } + + image = cat->image; + iso_node_unref((IsoNode*)image->image); + free(image); + iso_node_unref((IsoNode*)cat->node); + free(cat); +} + +/** + * Stream that generates the contents of a El-Torito catalog. + */ +struct catalog_stream +{ + Ecma119Image *target; + uint8_t buffer[BLOCK_SIZE]; + int offset; /* -1 if stream is not openned */ +}; + +static void +write_validation_entry(uint8_t *buf) +{ + size_t i; + int checksum; + + struct el_torito_validation_entry *ve = + (struct el_torito_validation_entry*)buf; + ve->header_id[0] = 1; + ve->platform_id[0] = 0; /* 0: 80x86, 1: PowerPC, 2: Mac */ + ve->key_byte1[0] = 0x55; + ve->key_byte2[0] = 0xAA; + + /* calculate the checksum, to ensure sum of all words is 0 */ + checksum = 0; + for (i = 0; i < sizeof(struct el_torito_validation_entry); i += 2) { + checksum -= (int16_t) ((buf[i+1] << 8) | buf[i]); + } + iso_lsb(ve->checksum, checksum, 2); +} + +/** + * Write one section entry. + * Currently this is used only for default image (the only supported just now) + */ +static void +write_section_entry(uint8_t *buf, Ecma119Image *t) +{ + struct el_torito_boot_image *img; + struct el_torito_section_entry *se = + (struct el_torito_section_entry*)buf; + + img = t->catalog->image; + + se->boot_indicator[0] = img->bootable ? 0x88 : 0x00; + se->boot_media_type[0] = img->type; + iso_lsb(se->load_seg, img->load_seg, 2); + se->system_type[0] = img->partition_type; + iso_lsb(se->sec_count, img->load_size, 2); + iso_lsb(se->block, t->bootimg->block, 4); +} + +static +int catalog_open(IsoStream *stream) +{ + struct catalog_stream *data; + if (stream == NULL) { + return ISO_NULL_POINTER; + } + data = stream->data; + + if (data->offset != -1) { + return ISO_FILE_ALREADY_OPENNED; + } + + memset(data->buffer, 0, BLOCK_SIZE); + + /* fill the buffer with the catalog contents */ + write_validation_entry(data->buffer); + + /* write default entry */ + write_section_entry(data->buffer + 32, data->target); + + data->offset = 0; + return ISO_SUCCESS; +} + +static +int catalog_close(IsoStream *stream) +{ + struct catalog_stream *data; + if (stream == NULL) { + return ISO_NULL_POINTER; + } + data = stream->data; + + if (data->offset == -1) { + return ISO_FILE_NOT_OPENNED; + } + data->offset = -1; + return ISO_SUCCESS; +} + +static +off_t catalog_get_size(IsoStream *stream) +{ + return BLOCK_SIZE; +} + +static +int catalog_read(IsoStream *stream, void *buf, size_t count) +{ + size_t len; + struct catalog_stream *data; + if (stream == NULL || buf == NULL) { + return ISO_NULL_POINTER; + } + if (count == 0) { + return ISO_WRONG_ARG_VALUE; + } + data = stream->data; + + if (data->offset == -1) { + return ISO_FILE_NOT_OPENNED; + } + + len = MIN(count, BLOCK_SIZE - data->offset); + memcpy(buf, data->buffer + data->offset, len); + return len; +} + +static +int catalog_is_repeatable(IsoStream *stream) +{ + return 1; +} + +/** + * fs_id will be the id reserved for El-Torito + * dev_id will be 0 for catalog, 1 for boot image (if needed) + * we leave ino_id for future use when we support multiple boot images + */ +static +void catalog_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, + ino_t *ino_id) +{ + *fs_id = ISO_ELTORITO_FS_ID; + *dev_id = 0; + *ino_id = 0; +} + +static +char *catalog_get_name(IsoStream *stream) +{ + return strdup("El-Torito Boot Catalog"); +} + +static +void catalog_free(IsoStream *stream) +{ + free(stream->data); +} + +IsoStreamIface catalog_stream_class = { + catalog_open, + catalog_close, + catalog_get_size, + catalog_read, + catalog_is_repeatable, + catalog_get_id, + catalog_get_name, + catalog_free +}; + +/** + * Create an IsoStream for writing El-Torito catalog for a given target. + */ +static +int catalog_stream_new(Ecma119Image *target, IsoStream **stream) +{ + IsoStream *str; + struct catalog_stream *data; + + if (target == NULL || stream == NULL || target->catalog == NULL) { + return ISO_NULL_POINTER; + } + + str = malloc(sizeof(IsoStream)); + if (str == NULL) { + return ISO_OUT_OF_MEM; + } + data = malloc(sizeof(struct catalog_stream)); + if (str == NULL) { + free(str); + return ISO_OUT_OF_MEM; + } + + /* fill data */ + data->target = target; + data->offset = -1; + + str->refcount = 1; + str->data = data; + str->class = &catalog_stream_class; + + *stream = str; + return ISO_SUCCESS; +} + +int el_torito_catalog_file_src_create(Ecma119Image *target, IsoFileSrc **src) +{ + int ret; + IsoFileSrc *file; + IsoStream *stream; + + if (target == NULL || src == NULL || target->catalog == NULL) { + return ISO_OUT_OF_MEM; + } + + if (target->cat != NULL) { + /* catalog file src already created */ + *src = target->cat; + return ISO_SUCCESS; + } + + file = malloc(sizeof(IsoFileSrc)); + if (file == NULL) { + return ISO_OUT_OF_MEM; + } + + ret = catalog_stream_new(target, &stream); + if (ret < 0) { + free(file); + return ret; + } + + /* fill fields */ + file->prev_img = 0; /* TODO allow copy of old img catalog???? */ + file->block = 0; /* to be filled later */ + file->sort_weight = 1000; /* slightly high */ + file->stream = stream; + + ret = iso_file_src_add(target, file, src); + if (ret <= 0) { + iso_stream_unref(stream); + free(file); + } else { + target->cat = *src; + } + return ret; +} + +/******************* EL-TORITO WRITER *******************************/ + +static +int eltorito_writer_compute_data_blocks(IsoImageWriter *writer) +{ + /* nothing to do, the files are written by the file writer */ + return ISO_SUCCESS; +} + +/** + * Write the Boot Record Volume Descriptor (ECMA-119, 8.2) + */ +static +int eltorito_writer_write_vol_desc(IsoImageWriter *writer) +{ + Ecma119Image *t; + struct el_torito_boot_catalog *cat; + struct ecma119_boot_rec_vol_desc vol; + + if (writer == NULL) { + return ISO_NULL_POINTER; + } + + t = writer->target; + cat = t->catalog; + + iso_msg_debug(t->image->id, "Write El-Torito boot record"); + + memset(&vol, 0, sizeof(struct ecma119_boot_rec_vol_desc)); + vol.vol_desc_type[0] = 0; + memcpy(vol.std_identifier, "CD001", 5); + vol.vol_desc_version[0] = 1; + memcpy(vol.boot_sys_id, "EL TORITO SPECIFICATION", 23); + iso_lsb(vol.boot_catalog, t->cat->block, 4); + + return iso_write(t, &vol, sizeof(struct ecma119_boot_rec_vol_desc)); +} + +/** + * Patch an isolinux boot image. + * + * @return + * 1 on success, 0 error (but continue), < 0 error + */ +static +int patch_boot_image(uint8_t *buf, Ecma119Image *t, size_t imgsize) +{ + struct boot_info_table *info; + uint32_t checksum; + size_t offset; + + if (imgsize < 64) { + return iso_msg_submit(t->image->id, ISO_ISOLINUX_CANT_PATCH, 0, + "Isolinux image too small. We won't patch it."); + } + + /* compute checksum, as the the sum of all 32 bit words in boot image + * from offset 64 */ + checksum = 0; + offset = (size_t) 64; + + while (offset <= imgsize - 4) { + checksum += iso_read_lsb(buf + offset, 4); + offset += 4; + } + if (offset != imgsize) { + /* file length not multiple of 4 */ + return iso_msg_submit(t->image->id, ISO_ISOLINUX_CANT_PATCH, 0, + "Unexpected isolinux image length. Patch might not work."); + } + + /* patch boot info table */ + info = (struct boot_info_table*)(buf + 8); + /*memset(info, 0, sizeof(struct boot_info_table));*/ + iso_lsb(info->bi_pvd, t->ms_block + 16, 4); + iso_lsb(info->bi_file, t->bootimg->block, 4); + iso_lsb(info->bi_length, imgsize, 4); + iso_lsb(info->bi_csum, checksum, 4); + return ISO_SUCCESS; +} + +static +int eltorito_writer_write_data(IsoImageWriter *writer) +{ + /* + * We have nothing to write, but if we need to patch an isolinux image, + * this is a good place to do so. + */ + Ecma119Image *t; + int ret; + + if (writer == NULL) { + return ISO_NULL_POINTER; + } + + t = writer->target; + + if (t->catalog->image->isolinux) { + /* we need to patch the image */ + size_t size; + uint8_t *buf; + IsoStream *new = NULL; + IsoStream *original = t->bootimg->stream; + size = (size_t) iso_stream_get_size(original); + buf = malloc(size); + if (buf == NULL) { + return ISO_OUT_OF_MEM; + } + ret = iso_stream_open(original); + if (ret < 0) { + return ret; + } + ret = iso_stream_read(original, buf, size); + iso_stream_close(original); + if (ret != size) { + return (ret < 0) ? ret : ISO_FILE_READ_ERROR; + } + + /* ok, patch the read buffer */ + ret = patch_boot_image(buf, t, size); + if (ret < 0) { + return ret; + } + + /* replace the original stream with a memory stream that reads from + * the patched buffer */ + ret = iso_memory_stream_new(buf, size, &new); + if (ret < 0) { + return ret; + } + t->bootimg->stream = new; + iso_stream_unref(original); + } + return ISO_SUCCESS; +} + +static +int eltorito_writer_free_data(IsoImageWriter *writer) +{ + /* nothing to do */ + return ISO_SUCCESS; +} + +int eltorito_writer_create(Ecma119Image *target) +{ + int ret; + IsoImageWriter *writer; + IsoFile *bootimg; + IsoFileSrc *src; + + writer = malloc(sizeof(IsoImageWriter)); + if (writer == NULL) { + return ISO_OUT_OF_MEM; + } + + writer->compute_data_blocks = eltorito_writer_compute_data_blocks; + writer->write_vol_desc = eltorito_writer_write_vol_desc; + writer->write_data = eltorito_writer_write_data; + writer->free_data = eltorito_writer_free_data; + writer->data = NULL; + writer->target = target; + + /* add this writer to image */ + target->writers[target->nwriters++] = writer; + + /* + * get catalog and image file sources. + * Note that the catalog may be already added, when creating the low + * level ECMA-119 tree. + */ + if (target->cat == NULL) { + ret = el_torito_catalog_file_src_create(target, &src); + if (ret < 0) { + return ret; + } + } + bootimg = target->catalog->image->image; + ret = iso_file_src_create(target, bootimg, &src); + if (ret < 0) { + return ret; + } + target->bootimg = src; + + /* if we have selected to patch the image, it needs to be copied always */ + if (target->catalog->image->isolinux) { + src->prev_img = 0; + } + + /* we need the bootable volume descriptor */ + target->curblock++; + return ISO_SUCCESS; +} + diff --git a/libisofs/branches/thomas/libisofs/eltorito.h b/libisofs/branches/thomas/libisofs/eltorito.h new file mode 100644 index 00000000..3c507997 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/eltorito.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/** + * Declare El-Torito related structures. + * References: + * "El Torito" Bootable CD-ROM Format Specification Version 1.0 (1995) + */ + +#ifndef LIBISO_ELTORITO_H +#define LIBISO_ELTORITO_H + +#include "ecma119.h" +#include "node.h" + +/** + * A node that acts as a placeholder for an El-Torito catalog. + */ +struct Iso_Boot +{ + IsoNode node; +}; + +struct el_torito_boot_catalog { + IsoBoot *node; /* node of the catalog */ + struct el_torito_boot_image *image; /* default boot image */ +}; + +struct el_torito_boot_image { + IsoFile *image; + + unsigned int bootable:1; /**< If the entry is bootable. */ + unsigned int isolinux:1; /**< If the image will be patched */ + unsigned char type; /**< The type of image */ + unsigned char partition_type; /**< type of partition for HD-emul images */ + short load_seg; /**< Load segment for the initial boot image. */ + short load_size; /**< Number of sectors to load. */ +}; + +/** El-Torito, 2.1 */ +struct el_torito_validation_entry { + uint8_t header_id BP(1, 1); + uint8_t platform_id BP(2, 2); + uint8_t reserved BP(3, 4); + uint8_t id_string BP(5, 28); + uint8_t checksum BP(29, 30); + uint8_t key_byte1 BP(31, 31); + uint8_t key_byte2 BP(32, 32); +}; + +/** El-Torito, 2.2 */ +struct el_torito_default_entry { + uint8_t boot_indicator BP(1, 1); + uint8_t boot_media_type BP(2, 2); + uint8_t load_seg BP(3, 4); + uint8_t system_type BP(5, 5); + uint8_t unused1 BP(6, 6); + uint8_t sec_count BP(7, 8); + uint8_t block BP(9, 12); + uint8_t unused2 BP(13, 32); +}; + +/** El-Torito, 2.3 */ +struct el_torito_section_header { + uint8_t header_indicator BP(1, 1); + uint8_t platform_id BP(2, 2); + uint8_t number BP(3, 4); + uint8_t character BP(5, 32); +}; + +/** El-Torito, 2.4 */ +struct el_torito_section_entry { + uint8_t boot_indicator BP(1, 1); + uint8_t boot_media_type BP(2, 2); + uint8_t load_seg BP(3, 4); + uint8_t system_type BP(5, 5); + uint8_t unused1 BP(6, 6); + uint8_t sec_count BP(7, 8); + uint8_t block BP(9, 12); + uint8_t selec_criteria BP(13, 13); + uint8_t vendor_sc BP(14, 32); +}; + +void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat); + +/** + * Create a IsoFileSrc for writing the el-torito catalog for the given + * target, and add it to target. If the target already has a src for the + * catalog, it just returns. + */ +int el_torito_catalog_file_src_create(Ecma119Image *target, IsoFileSrc **src); + +/** + * Create a writer for el-torito information. + */ +int eltorito_writer_create(Ecma119Image *target); + +#endif /* LIBISO_ELTORITO_H */ diff --git a/libisofs/branches/thomas/libisofs/filesrc.c b/libisofs/branches/thomas/libisofs/filesrc.c new file mode 100644 index 00000000..a4a2bfca --- /dev/null +++ b/libisofs/branches/thomas/libisofs/filesrc.c @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "filesrc.h" +#include "node.h" +#include "util.h" +#include "writer.h" +#include "messages.h" +#include "image.h" + +#include +#include + +int iso_file_src_cmp(const void *n1, const void *n2) +{ + const IsoFileSrc *f1, *f2; + unsigned int fs_id1, fs_id2; + dev_t dev_id1, dev_id2; + ino_t ino_id1, ino_id2; + + f1 = (const IsoFileSrc *)n1; + f2 = (const IsoFileSrc *)n2; + + iso_stream_get_id(f1->stream, &fs_id1, &dev_id1, &ino_id1); + iso_stream_get_id(f2->stream, &fs_id2, &dev_id2, &ino_id2); + + if (fs_id1 < fs_id2) { + return -1; + } else if (fs_id1 > fs_id2) { + return 1; + } else { + /* files belong to the same fs */ + if (dev_id1 > dev_id2) { + return -1; + } else if (dev_id1 < dev_id2) { + return 1; + } else { + /* files belong to same device in same fs */ + return (ino_id1 < ino_id2) ? -1 : (ino_id1 > ino_id2) ? 1 : 0; + } + } +} + +int iso_file_src_create(Ecma119Image *img, IsoFile *file, IsoFileSrc **src) +{ + int ret; + IsoFileSrc *fsrc; + unsigned int fs_id; + dev_t dev_id; + ino_t ino_id; + + if (img == NULL || file == NULL || src == NULL) { + return ISO_NULL_POINTER; + } + + iso_stream_get_id(file->stream, &fs_id, &dev_id, &ino_id); + + fsrc = malloc(sizeof(IsoFileSrc)); + if (fsrc == NULL) { + return ISO_OUT_OF_MEM; + } + + /* fill key and other atts */ + fsrc->prev_img = file->msblock ? 1 : 0; + fsrc->block = file->msblock; + fsrc->sort_weight = file->sort_weight; + fsrc->stream = file->stream; + + /* insert the filesrc in the tree */ + ret = iso_rbtree_insert(img->files, fsrc, (void**)src); + if (ret <= 0) { + free(fsrc); + return ret; + } + iso_stream_ref(fsrc->stream); + return ISO_SUCCESS; +} + +/** + * Add a given IsoFileSrc to the given image target. + * + * The IsoFileSrc will be cached in a tree to prevent the same file for + * being written several times to image. If you call again this function + * with a node that refers to the same source file, the previously + * created one will be returned. + * + * @param img + * The image where this file is to be written + * @param new + * The IsoFileSrc to add + * @param src + * Will be filled with a pointer to the IsoFileSrc really present in + * the tree. It could be different than new if the same file already + * exists in the tree. + * @return + * 1 on success, 0 if file already exists on tree, < 0 error + */ +int iso_file_src_add(Ecma119Image *img, IsoFileSrc *new, IsoFileSrc **src) +{ + int ret; + + if (img == NULL || new == NULL || src == NULL) { + return ISO_NULL_POINTER; + } + + /* insert the filesrc in the tree */ + ret = iso_rbtree_insert(img->files, new, (void**)src); + return ret; +} + +void iso_file_src_free(void *node) +{ + iso_stream_unref(((IsoFileSrc*)node)->stream); + free(node); +} + +off_t iso_file_src_get_size(IsoFileSrc *file) +{ + return iso_stream_get_size(file->stream); +} + +static int cmp_by_weight(const void *f1, const void *f2) +{ + IsoFileSrc *f = *((IsoFileSrc**)f1); + IsoFileSrc *g = *((IsoFileSrc**)f2); + /* higher weighted first */ + return g->sort_weight - f->sort_weight; +} + +static +int is_ms_file(void *arg) +{ + IsoFileSrc *f = (IsoFileSrc *)arg; + return f->prev_img ? 0 : 1; +} + +static +int filesrc_writer_compute_data_blocks(IsoImageWriter *writer) +{ + size_t i, size; + Ecma119Image *t; + IsoFileSrc **filelist; + int (*inc_item)(void *); + + if (writer == NULL) { + return ISO_ASSERT_FAILURE; + } + + t = writer->target; + + /* on appendable images, ms files shouldn't be included */ + if (t->appendable) { + inc_item = is_ms_file; + } else { + inc_item = NULL; + } + + /* store the filesrcs in a array */ + filelist = (IsoFileSrc**)iso_rbtree_to_array(t->files, inc_item, &size); + if (filelist == NULL) { + return ISO_OUT_OF_MEM; + } + + /* sort files by weight, if needed */ + if (t->sort_files) { + qsort(filelist, size, sizeof(void*), cmp_by_weight); + } + + /* fill block value */ + for (i = 0; i < size; ++i) { + IsoFileSrc *file = filelist[i]; + file->block = t->curblock; + t->curblock += DIV_UP(iso_file_src_get_size(file), BLOCK_SIZE); + } + + /* the list is only needed by this writer, store locally */ + writer->data = filelist; + return ISO_SUCCESS; +} + +static +int filesrc_writer_write_vol_desc(IsoImageWriter *writer) +{ + /* nothing needed */ + return ISO_SUCCESS; +} + +/* open a file, i.e., its Stream */ +static inline +int filesrc_open(IsoFileSrc *file) +{ + return iso_stream_open(file->stream); +} + +static inline +int filesrc_close(IsoFileSrc *file) +{ + return iso_stream_close(file->stream); +} + +/** + * @return + * 1 ok, 0 EOF, < 0 error + */ +static +int filesrc_read(IsoFileSrc *file, char *buf, size_t count) +{ + size_t bytes = 0; + + /* loop to ensure the full buffer is filled */ + do { + ssize_t result; + result = iso_stream_read(file->stream, buf + bytes, count - bytes); + if (result < 0) { + /* fill buffer with 0s and return */ + memset(buf + bytes, 0, count - bytes); + return result; + } + if (result == 0) + break; + bytes += result; + } while (bytes < count); + + if (bytes < count) { + /* eof */ + memset(buf + bytes, 0, count - bytes); + return 0; + } else { + return 1; + } +} + +static +int filesrc_writer_write_data(IsoImageWriter *writer) +{ + int res; + size_t i, b; + Ecma119Image *t; + IsoFileSrc *file; + IsoFileSrc **filelist; + char *name; + char buffer[BLOCK_SIZE]; + + if (writer == NULL) { + return ISO_ASSERT_FAILURE; + } + + t = writer->target; + filelist = writer->data; + + iso_msg_debug(t->image->id, "Writing Files..."); + + i = 0; + while ((file = filelist[i++]) != NULL) { + + /* + * TODO WARNING + * when we allow files greater than 4GB, current DIV_UP implementation + * can overflow!! + */ + uint32_t nblocks = DIV_UP(iso_file_src_get_size(file), BLOCK_SIZE); + + res = filesrc_open(file); + if (res < 0) { + /* + * UPS, very ugly error, the best we can do is just to write + * 0's to image + */ + name = iso_stream_get_name(file->stream); + iso_report_errfile(name, ISO_FILE_CANT_WRITE, 0, 0); + res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, res, + "File \"%s\" can't be opened. Filling with 0s.", name); + free(name); + if (res < 0) { + return res; /* aborted due to error severity */ + } + memset(buffer, 0, BLOCK_SIZE); + for (b = 0; b < nblocks; ++b) { + res = iso_write(t, buffer, BLOCK_SIZE); + if (res < 0) { + /* ko, writer error, we need to go out! */ + return res; + } + } + continue; + } else if (res > 1) { + name = iso_stream_get_name(file->stream); + iso_report_errfile(name, ISO_FILE_CANT_WRITE, 0, 0); + res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, 0, + "Size of file \"%s\" has changed. It will be %s", name, + (res == 2 ? "truncated" : "padded with 0's")); + free(name); + if (res < 0) { + filesrc_close(file); + return res; /* aborted due to error severity */ + } + } +#ifdef LIBISOFS_VERBOSE_DEBUG + else { + name = iso_stream_get_name(file->stream); + iso_msg_debug(t->image->id, "Writing file %s", name); + free(name); + } +#endif + + /* write file contents to image */ + for (b = 0; b < nblocks; ++b) { + int wres; + res = filesrc_read(file, buffer, BLOCK_SIZE); + if (res < 0) { + /* read error */ + break; + } + wres = iso_write(t, buffer, BLOCK_SIZE); + if (wres < 0) { + /* ko, writer error, we need to go out! */ + filesrc_close(file); + return wres; + } + } + + filesrc_close(file); + + if (b < nblocks) { + /* premature end of file, due to error or eof */ + char *name = iso_stream_get_name(file->stream); + iso_report_errfile(name, ISO_FILE_CANT_WRITE, 0, 0); + if (res < 0) { + /* error */ + res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, res, + "Read error in file %s.", name); + } else { + /* eof */ + res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, 0, + "Premature end of file %s.", name); + } + free(name); + + if (res < 0) { + return res; /* aborted due error severity */ + } + + /* fill with 0s */ + iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, 0, + "Filling with 0"); + memset(buffer, 0, BLOCK_SIZE); + while (b++ < nblocks) { + res = iso_write(t, buffer, BLOCK_SIZE); + if (res < 0) { + /* ko, writer error, we need to go out! */ + return res; + } + } + } + } + + return ISO_SUCCESS; +} + +static +int filesrc_writer_free_data(IsoImageWriter *writer) +{ + /* free the list of files (contents are free together with the tree) */ + free(writer->data); + return ISO_SUCCESS; +} + +int iso_file_src_writer_create(Ecma119Image *target) +{ + IsoImageWriter *writer; + + writer = malloc(sizeof(IsoImageWriter)); + if (writer == NULL) { + return ISO_ASSERT_FAILURE; + } + + writer->compute_data_blocks = filesrc_writer_compute_data_blocks; + writer->write_vol_desc = filesrc_writer_write_vol_desc; + writer->write_data = filesrc_writer_write_data; + writer->free_data = filesrc_writer_free_data; + writer->data = NULL; + writer->target = target; + + /* add this writer to image */ + target->writers[target->nwriters++] = writer; + + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/filesrc.h b/libisofs/branches/thomas/libisofs/filesrc.h new file mode 100644 index 00000000..5a3c03c8 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/filesrc.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ +#ifndef LIBISO_FILESRC_H_ +#define LIBISO_FILESRC_H_ + +#include "libisofs.h" +#include "stream.h" +#include "ecma119.h" + +#include + +struct Iso_File_Src +{ + unsigned int prev_img :1; /**< if the file comes from a previous image */ + uint32_t block; /**< Block where this file will be written on image */ + int sort_weight; + IsoStream *stream; +}; + +int iso_file_src_cmp(const void *n1, const void *n2); + +/** + * Create a new IsoFileSrc to get data from a specific IsoFile. + * + * The IsoFileSrc will be cached in a tree to prevent the same file for + * being written several times to image. If you call again this function + * with a node that refers to the same source file, the previously + * created one will be returned. No new IsoFileSrc is created in that case. + * + * @param img + * The image where this file is to be written + * @param file + * The IsoNode we want to write + * @param src + * Will be filled with a pointer to the IsoFileSrc + * @return + * 1 on success, < 0 on error + */ +int iso_file_src_create(Ecma119Image *img, IsoFile *file, IsoFileSrc **src); + +/** + * Add a given IsoFileSrc to the given image target. + * + * The IsoFileSrc will be cached in a tree to prevent the same file for + * being written several times to image. If you call again this function + * with a node that refers to the same source file, the previously + * created one will be returned. + * + * @param img + * The image where this file is to be written + * @param new + * The IsoFileSrc to add + * @param src + * Will be filled with a pointer to the IsoFileSrc really present in + * the tree. It could be different than new if the same file already + * exists in the tree. + * @return + * 1 on success, 0 if file already exists on tree, < 0 error + */ +int iso_file_src_add(Ecma119Image *img, IsoFileSrc *new, IsoFileSrc **src); + +/** + * Free the IsoFileSrc especific data + */ +void iso_file_src_free(void *node); + +/** + * Get the size of the file this IsoFileSrc represents + */ +off_t iso_file_src_get_size(IsoFileSrc *file); + +/** + * Create a Writer for file contents. + * + * It takes care of written the files in the correct order. + */ +int iso_file_src_writer_create(Ecma119Image *target); + +#endif /*LIBISO_FILESRC_H_*/ diff --git a/libisofs/branches/thomas/libisofs/fs_image.c b/libisofs/branches/thomas/libisofs/fs_image.c new file mode 100644 index 00000000..817668df --- /dev/null +++ b/libisofs/branches/thomas/libisofs/fs_image.c @@ -0,0 +1,2642 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/* + * Filesystem/FileSource implementation to access an ISO image, using an + * IsoDataSource to read image data. + */ + +#include "libisofs.h" +#include "ecma119.h" +#include "messages.h" +#include "rockridge.h" +#include "image.h" +#include "tree.h" +#include "eltorito.h" + +#include +#include +#include +#include +#include + + +/** + * Options for image reading. + * There are four kind of options: + * - Related to multisession support. + * In most cases, an image begins at LBA 0 of the data source. However, + * in multisession discs, the later image begins in the last session on + * disc. The block option can be used to specify the start of that last + * session. + * - Related to the tree that will be read. + * As default, when Rock Ridge extensions are present in the image, that + * will be used to get the tree. If RR extensions are not present, libisofs + * will use the Joliet extensions if available. Finally, the plain ISO-9660 + * tree is used if neither RR nor Joliet extensions are available. With + * norock, nojoliet, and preferjoliet options, you can change this + * default behavior. + * - Related to default POSIX attributes. + * When Rock Ridege extensions are not used, libisofs can't figure out what + * are the the permissions, uid or gid for the files. You should supply + * default values for that. + */ +struct iso_read_opts +{ + /** + * Block where the image begins, usually 0, can be different on a + * multisession disc. + */ + uint32_t block; + + unsigned int norock : 1; /*< Do not read Rock Ridge extensions */ + unsigned int nojoliet : 1; /*< Do not read Joliet extensions */ + unsigned int noiso1999 : 1; /*< Do not read ISO 9660:1999 enhanced tree */ + + /** + * When both Joliet and RR extensions are present, the RR tree is used. + * If you prefer using Joliet, set this to 1. + */ + unsigned int preferjoliet : 1; + + uid_t uid; /**< Default uid when no RR */ + gid_t gid; /**< Default uid when no RR */ + mode_t dir_mode; /**< Default mode when no RR (only permissions) */ + mode_t file_mode; + /* TODO #00024 : option to convert names to lower case for iso reading */ + + /** + * Input charset for RR file names. NULL to use default locale charset. + */ + char *input_charset; +}; + +/** + * Return information for image. + * Both size, hasRR and hasJoliet will be filled by libisofs with suitable + * values. + */ +struct iso_read_image_features +{ + /** + * Will be filled with the size (in 2048 byte block) of the image, as + * reported in the PVM. + */ + uint32_t size; + + /** It will be set to 1 if RR extensions are present, to 0 if not. */ + unsigned int hasRR :1; + + /** It will be set to 1 if Joliet extensions are present, to 0 if not. */ + unsigned int hasJoliet :1; + + /** + * It will be set to 1 if the image is an ISO 9660:1999, i.e. it has + * a version 2 Enhanced Volume Descriptor. + */ + unsigned int hasIso1999 :1; + + /** It will be set to 1 if El-Torito boot record is present, to 0 if not.*/ + unsigned int hasElTorito :1; +}; + +static int ifs_fs_open(IsoImageFilesystem *fs); +static int ifs_fs_close(IsoImageFilesystem *fs); +static int iso_file_source_new_ifs(IsoImageFilesystem *fs, + IsoFileSource *parent, struct ecma119_dir_record *record, + IsoFileSource **src); + +/** unique identifier for each image */ +unsigned int fs_dev_id = 0; + +/** + * Should the RR extensions be read? + */ +enum read_rr_ext { + RR_EXT_NO = 0, /*< Do not use RR extensions */ + RR_EXT_110 = 1, /*< RR extensions conforming version 1.10 */ + RR_EXT_112 = 2 /*< RR extensions conforming version 1.12 */ +}; + +/** + * Private data for the image IsoFilesystem + */ +typedef struct +{ + /** DataSource from where data will be read */ + IsoDataSource *src; + + /** unique id for the each image (filesystem instance) */ + unsigned int id; + + /** + * Counter of the times the filesystem has been openned still pending of + * close. It is used to keep track of when we need to actually open or + * close the IsoDataSource. + */ + unsigned int open_count; + + uid_t uid; /**< Default uid when no RR */ + gid_t gid; /**< Default uid when no RR */ + mode_t dir_mode; /**< Default mode when no RR (only permissions) */ + mode_t file_mode; + + int msgid; + + char *input_charset; /**< Input charset for RR names */ + char *local_charset; /**< For RR names, will be set to the locale one */ + + /** + * Will be filled with the block lba of the extend for the root directory + * of the hierarchy that will be read, either from the PVD (ISO, RR) or + * from the SVD (Joliet) + */ + uint32_t iso_root_block; + + /** + * Will be filled with the block lba of the extend for the root directory, + * as read from the PVM + */ + uint32_t pvd_root_block; + + /** + * Will be filled with the block lba of the extend for the root directory, + * as read from the SVD + */ + uint32_t svd_root_block; + + /** + * Will be filled with the block lba of the extend for the root directory, + * as read from the enhanced volume descriptor (ISO 9660:1999) + */ + uint32_t evd_root_block; + + /** + * If we need to read RR extensions. i.e., if the image contains RR + * extensions, and the user wants to read them. + */ + enum read_rr_ext rr; + + /** + * Bytes skipped within the System Use field of a directory record, before + * the beginning of the SUSP system user entries. See IEEE 1281, SUSP. 5.3. + */ + uint8_t len_skp; + + /* Volume attributes */ + char *volset_id; + char *volume_id; /**< Volume identifier. */ + char *publisher_id; /**< Volume publisher. */ + char *data_preparer_id; /**< Volume data preparer. */ + char *system_id; /**< Volume system identifier. */ + char *application_id; /**< Volume application id */ + char *copyright_file_id; + char *abstract_file_id; + char *biblio_file_id; + + /* extension information */ + + /** + * RR version being used in image. + * 0 no RR extension, 1 RRIP 1.10, 2 RRIP 1.12 + */ + enum read_rr_ext rr_version; + + /** If Joliet extensions are available on image */ + unsigned int joliet : 1; + + /** If ISO 9660:1999 is available on image */ + unsigned int iso1999 : 1; + + /** + * Number of blocks of the volume, as reported in the PVM. + */ + uint32_t nblocks; + + /* el-torito information */ + unsigned int eltorito : 1; /* is el-torito available */ + unsigned int bootable:1; /**< If the entry is bootable. */ + unsigned char type; /**< The type of image */ + unsigned char partition_type; /**< type of partition for HD-emul images */ + short load_seg; /**< Load segment for the initial boot image. */ + short load_size; /**< Number of sectors to load. */ + uint32_t imgblock; /**< Block for El-Torito boot image */ + uint32_t catblock; /**< Block for El-Torito catalog */ + +} _ImageFsData; + +typedef struct image_fs_data ImageFileSourceData; + +struct image_fs_data +{ + IsoImageFilesystem *fs; /**< reference to the image it belongs to */ + IsoFileSource *parent; /**< reference to the parent (NULL if root) */ + + struct stat info; /**< filled struct stat */ + char *name; /**< name of this file */ + + uint32_t block; /**< block of the extend */ + unsigned int opened : 2; /**< 0 not opened, 1 opened file, 2 opened dir */ + + /* info for content reading */ + struct + { + /** + * - For regular files, once opened it points to a temporary data + * buffer of 2048 bytes. + * - For dirs, once opened it points to a IsoFileSource* array with + * its children + * - For symlinks, it points to link destination + */ + void *content; + + /** + * - For regular files, number of bytes already read. + */ + off_t offset; + } data; +}; + +struct child_list +{ + IsoFileSource *file; + struct child_list *next; +}; + +void child_list_free(struct child_list *list) +{ + struct child_list *temp; + struct child_list *next = list; + while (next != NULL) { + temp = next->next; + iso_file_source_unref(next->file); + free(next); + next = temp; + } +} + +static +char* ifs_get_path(IsoFileSource *src) +{ + ImageFileSourceData *data; + data = src->data; + + if (data->parent == NULL) { + return strdup(""); + } else { + char *path = ifs_get_path(data->parent); + int pathlen = strlen(path); + path = realloc(path, pathlen + strlen(data->name) + 2); + path[pathlen] = '/'; + path[pathlen + 1] = '\0'; + return strcat(path, data->name); + } +} + +static +char* ifs_get_name(IsoFileSource *src) +{ + ImageFileSourceData *data; + data = src->data; + return data->name == NULL ? NULL : strdup(data->name); +} + +static +int ifs_lstat(IsoFileSource *src, struct stat *info) +{ + ImageFileSourceData *data; + + if (src == NULL || info == NULL) { + return ISO_NULL_POINTER; + } + + data = src->data; + *info = data->info; + return ISO_SUCCESS; +} + +static +int ifs_stat(IsoFileSource *src, struct stat *info) +{ + ImageFileSourceData *data; + + if (src == NULL || info == NULL || src->data == NULL) { + return ISO_NULL_POINTER; + } + + data = (ImageFileSourceData*)src->data; + + if (S_ISLNK(data->info.st_mode)) { + /* TODO #00012 : support follow symlinks on image filesystem */ + return ISO_FILE_BAD_PATH; + } + *info = data->info; + return ISO_SUCCESS; +} + +static +int ifs_access(IsoFileSource *src) +{ + /* we always have access, it is controlled by DataSource */ + return ISO_SUCCESS; +} + +/** + * Read all directory records in a directory, and creates an IsoFileSource for + * each of them, storing them in the data field of the IsoFileSource for the + * given dir. + */ +static +int read_dir(ImageFileSourceData *data) +{ + int ret; + uint32_t size; + uint32_t block; + IsoImageFilesystem *fs; + _ImageFsData *fsdata; + struct ecma119_dir_record *record; + uint8_t buffer[BLOCK_SIZE]; + IsoFileSource *child = NULL; + uint32_t pos = 0; + uint32_t tlen = 0; + + if (data == NULL) { + return ISO_NULL_POINTER; + } + + fs = data->fs; + fsdata = fs->data; + + block = data->block; + ret = fsdata->src->read_block(fsdata->src, block, buffer); + if (ret < 0) { + return ret; + } + + /* "." entry, get size of the dir and skip */ + record = (struct ecma119_dir_record *)(buffer + pos); + size = iso_read_bb(record->length, 4, NULL); + tlen += record->len_dr[0]; + pos += record->len_dr[0]; + + /* skip ".." */ + record = (struct ecma119_dir_record *)(buffer + pos); + tlen += record->len_dr[0]; + pos += record->len_dr[0]; + + while (tlen < size) { + + record = (struct ecma119_dir_record *)(buffer + pos); + if (pos == 2048 || record->len_dr[0] == 0) { + /* + * The directory entries are splitted in several blocks + * read next block + */ + ret = fsdata->src->read_block(fsdata->src, ++block, buffer); + if (ret < 0) { + return ret; + } + tlen += 2048 - pos; + pos = 0; + continue; + } + + /* + * What about ignoring files with existence flag? + * if (record->flags[0] & 0x01) + * continue; + */ + + /* + * For a extrange reason, mkisofs relocates directories under + * a RR_MOVED dir. It seems that it is only used for that purposes, + * and thus it should be removed from the iso tree before + * generating a new image with libisofs, that don't uses it. + */ + if (data->parent == NULL && record->len_fi[0] == 8 + && !strncmp((char*)record->file_id, "RR_MOVED", 8)) { + + iso_msg_debug(fsdata->msgid, "Skipping RR_MOVE entry."); + + tlen += record->len_dr[0]; + pos += record->len_dr[0]; + continue; + } + + /* + * We pass a NULL parent instead of dir, to prevent the circular + * reference from child to parent. + */ + ret = iso_file_source_new_ifs(fs, NULL, record, &child); + if (ret < 0) { + return ret; + } + + /* add to the child list */ + if (ret != 0) { + struct child_list *node; + node = malloc(sizeof(struct child_list)); + if (node == NULL) { + iso_file_source_unref(child); + return ISO_OUT_OF_MEM; + } + /* + * Note that we insert in reverse order. This leads to faster + * addition here, but also when adding to the tree, as insertion + * will be done, sorted, in the first position of the list. + */ + node->next = data->data.content; + node->file = child; + data->data.content = node; + } + + tlen += record->len_dr[0]; + pos += record->len_dr[0]; + } + + return ISO_SUCCESS; +} + +static +int ifs_open(IsoFileSource *src) +{ + int ret; + ImageFileSourceData *data; + + if (src == NULL || src->data == NULL) { + return ISO_NULL_POINTER; + } + data = (ImageFileSourceData*)src->data; + + if (data->opened) { + return ISO_FILE_ALREADY_OPENNED; + } + + if (S_ISDIR(data->info.st_mode)) { + /* ensure fs is openned */ + ret = data->fs->open(data->fs); + if (ret < 0) { + return ret; + } + + /* + * Cache all directory entries. + * This can waste more memory, but improves as disc is read in much more + * sequencially way, thus reducing jump between tracks on disc + */ + ret = read_dir(data); + data->fs->close(data->fs); + + if (ret < 0) { + /* free probably allocated children */ + child_list_free((struct child_list*)data->data.content); + } else { + data->opened = 2; + } + + return ret; + } else if (S_ISREG(data->info.st_mode)) { + /* ensure fs is openned */ + ret = data->fs->open(data->fs); + if (ret < 0) { + return ret; + } + data->data.content = malloc(BLOCK_SIZE); + if (data->data.content == NULL) { + return ISO_OUT_OF_MEM; + } + data->data.offset = 0; + data->opened = 1; + } else { + /* symlinks and special files inside image can't be openned */ + return ISO_FILE_ERROR; + } + return ISO_SUCCESS; +} + +static +int ifs_close(IsoFileSource *src) +{ + ImageFileSourceData *data; + + if (src == NULL || src->data == NULL) { + return ISO_NULL_POINTER; + } + data = (ImageFileSourceData*)src->data; + + if (!data->opened) { + return ISO_FILE_NOT_OPENNED; + } + + if (data->opened == 2) { + /* + * close a dir, free all pending pre-allocated children. + * not that we don't need to close the filesystem, it was already + * closed + */ + child_list_free((struct child_list*) data->data.content); + data->data.content = NULL; + data->opened = 0; + } else if (data->opened == 1) { + /* close regular file */ + free(data->data.content); + data->fs->close(data->fs); + data->data.content = NULL; + data->opened = 0; + } else { + /* TODO only dirs and files supported for now */ + return ISO_ERROR; + } + + return ISO_SUCCESS; +} + +/** + * Attempts to read up to count bytes from the given source into + * the buffer starting at buf. + * + * The file src must be open() before calling this, and close() when no + * more needed. Not valid for dirs. On symlinks it reads the destination + * file. + * + * @return + * number of bytes read, 0 if EOF, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_FILE_NOT_OPENNED + * ISO_FILE_IS_DIR + * ISO_OUT_OF_MEM + * ISO_INTERRUPTED + */ +static +int ifs_read(IsoFileSource *src, void *buf, size_t count) +{ + int ret; + ImageFileSourceData *data; + uint32_t read = 0; + + if (src == NULL || src->data == NULL || buf == NULL) { + return ISO_NULL_POINTER; + } + if (count == 0) { + return ISO_WRONG_ARG_VALUE; + } + data = (ImageFileSourceData*)src->data; + + if (!data->opened) { + return ISO_FILE_NOT_OPENNED; + } else if (data->opened != 1) { + return ISO_FILE_IS_DIR; + } + + while (read < count && data->data.offset < data->info.st_size) { + size_t bytes; + uint8_t *orig; + + if (data->data.offset % BLOCK_SIZE == 0) { + /* we need to buffer next block */ + uint32_t block; + _ImageFsData *fsdata; + + if (data->data.offset >= data->info.st_size) { + /* EOF */ + break; + } + fsdata = data->fs->data; + block = data->block + (data->data.offset / BLOCK_SIZE); + ret = fsdata->src->read_block(fsdata->src, block, + data->data.content); + if (ret < 0) { + return ret; + } + } + + /* how much can I read */ + bytes = MIN(BLOCK_SIZE - (data->data.offset % BLOCK_SIZE), + count - read); + if (data->data.offset + (off_t)bytes > data->info.st_size) { + bytes = data->info.st_size - data->data.offset; + } + orig = data->data.content; + orig += data->data.offset % BLOCK_SIZE; + memcpy((uint8_t*)buf + read, orig, bytes); + read += bytes; + data->data.offset += (off_t)bytes; + } + return read; +} + +static +int ifs_readdir(IsoFileSource *src, IsoFileSource **child) +{ + ImageFileSourceData *data, *cdata; + struct child_list *children; + + if (src == NULL || src->data == NULL || child == NULL) { + return ISO_NULL_POINTER; + } + data = (ImageFileSourceData*)src->data; + + if (!data->opened) { + return ISO_FILE_NOT_OPENNED; + } else if (data->opened != 2) { + return ISO_FILE_IS_NOT_DIR; + } + + /* return the first child and free it */ + if (data->data.content == NULL) { + return 0; /* EOF */ + } + + children = (struct child_list*)data->data.content; + *child = children->file; + cdata = (ImageFileSourceData*)(*child)->data; + + /* set the ref to the parent */ + cdata->parent = src; + iso_file_source_ref(src); + + /* free the first element of the list */ + data->data.content = children->next; + free(children); + + return ISO_SUCCESS; +} + +/** + * Read the destination of a symlink. You don't need to open the file + * to call this. + * + * @param buf + * allocated buffer of at least bufsiz bytes. + * The dest. will be copied there, and it will be NULL-terminated + * @param bufsiz + * characters to be copied. Destination link will be truncated if + * it is larger than given size. This include the \0 character. + * @return + * 1 on success, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_WRONG_ARG_VALUE -> if bufsiz <= 0 + * ISO_FILE_IS_NOT_SYMLINK + * ISO_OUT_OF_MEM + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * + */ +static +int ifs_readlink(IsoFileSource *src, char *buf, size_t bufsiz) +{ + char *dest; + size_t len; + ImageFileSourceData *data; + + if (src == NULL || buf == NULL || src->data == NULL) { + return ISO_NULL_POINTER; + } + + if (bufsiz <= 0) { + return ISO_WRONG_ARG_VALUE; + } + + data = (ImageFileSourceData*)src->data; + + if (!S_ISLNK(data->info.st_mode)) { + return ISO_FILE_IS_NOT_SYMLINK; + } + + dest = (char*)data->data.content; + len = strlen(dest); + if (bufsiz <= len) { + len = bufsiz - 1; + } + + strncpy(buf, dest, len); + buf[len] = '\0'; + + return ISO_SUCCESS; +} + +static +IsoFilesystem* ifs_get_filesystem(IsoFileSource *src) +{ + ImageFileSourceData *data; + + if (src == NULL) { + return NULL; + } + + data = src->data; + return data->fs; +} + +static +void ifs_free(IsoFileSource *src) +{ + ImageFileSourceData *data; + + data = src->data; + + /* close the file if it is already openned */ + if (data->opened) { + src->class->close(src); + } + + /* free destination if it is a link */ + if (S_ISLNK(data->info.st_mode)) { + free(data->data.content); + } + iso_filesystem_unref(data->fs); + if (data->parent != NULL) { + iso_file_source_unref(data->parent); + } + free(data->name); + free(data); +} + +IsoFileSourceIface ifs_class = { + 0, /* version */ + ifs_get_path, + ifs_get_name, + ifs_lstat, + ifs_stat, + ifs_access, + ifs_open, + ifs_close, + ifs_read, + ifs_readdir, + ifs_readlink, + ifs_get_filesystem, + ifs_free +}; + +/** + * Read a file name from a directory record, doing the needed charset + * conversion + */ +static +char *get_name(_ImageFsData *fsdata, const char *str, size_t len) +{ + int ret; + char *name = NULL; + if (strcmp(fsdata->local_charset, fsdata->input_charset)) { + /* charset conversion needed */ + ret = strnconv(str, fsdata->input_charset, fsdata->local_charset, len, + &name); + if (ret == 1) { + return name; + } else { + ret = iso_msg_submit(fsdata->msgid, ISO_FILENAME_WRONG_CHARSET, ret, + "Charset conversion error. Can't convert %s from %s to %s", + str, fsdata->input_charset, fsdata->local_charset); + if (ret < 0) { + return NULL; /* aborted */ + } + /* fallback */ + } + } + + /* we reach here when the charset conversion is not needed or has failed */ + + name = malloc(len + 1); + if (name == NULL) { + return NULL; + } + memcpy(name, str, len); + name[len] = '\0'; + return name; +} + +/** + * + * @return + * 1 success, 0 record ignored (not an error, can be a relocated dir), + * < 0 error + */ +static +int iso_file_source_new_ifs(IsoImageFilesystem *fs, IsoFileSource *parent, + struct ecma119_dir_record *record, + IsoFileSource **src) +{ + int ret; + struct stat atts; + time_t recorded; + _ImageFsData *fsdata; + IsoFileSource *ifsrc = NULL; + ImageFileSourceData *ifsdata = NULL; + + int namecont = 0; /* 1 if found a NM with CONTINUE flag */ + char *name = NULL; + + /* 1 if found a SL with CONTINUE flag, + * 2 if found a component with continue flag */ + int linkdestcont = 0; + char *linkdest = NULL; + + uint32_t relocated_dir = 0; + + if (fs == NULL || fs->data == NULL || record == NULL || src == NULL) { + return ISO_NULL_POINTER; + } + + fsdata = (_ImageFsData*)fs->data; + + memset(&atts, 0, sizeof(struct stat)); + + /* + * First of all, check for unsupported ECMA-119 features + */ + + /* check for unsupported multiextend */ + if (record->flags[0] & 0x80) { + iso_msg_submit(fsdata->msgid, ISO_UNSUPPORTED_ECMA119, 0, + "Unsupported image. This image makes use of Multi-Extend" + " features, that are not supported at this time. If you " + "need support for that, please request us this feature."); + return ISO_UNSUPPORTED_ECMA119; + } + + /* check for unsupported interleaved mode */ + if (record->file_unit_size[0] || record->interleave_gap_size[0]) { + iso_msg_submit(fsdata->msgid, ISO_UNSUPPORTED_ECMA119, 0, + "Unsupported image. This image has at least one file recorded " + "in interleaved mode. We don't support this mode, as we think " + "it's not used. If you're reading this, then we're wrong :) " + "Please contact libisofs developers, so we can fix this."); + return ISO_UNSUPPORTED_ECMA119; + } + + /* + * Check for extended attributes, that are not supported. Note that even + * if we don't support them, it is easy to ignore them. + */ + if (record->len_xa[0]) { + iso_msg_submit(fsdata->msgid, ISO_UNSUPPORTED_ECMA119, 0, + "Unsupported image. This image has at least one file with " + "Extended Attributes, that are not supported"); + return ISO_UNSUPPORTED_ECMA119; + } + + /* TODO #00013 : check for unsupported flags when reading a dir record */ + + /* + * The idea is to read all the RR entries (if we want to do that and RR + * extensions exist on image), storing the info we want from that. + * Then, we need some sanity checks. + * Finally, we select what kind of node it is, and set values properly. + */ + + if (fsdata->rr) { + struct susp_sys_user_entry *sue; + SuspIterator *iter; + + + iter = susp_iter_new(fsdata->src, record, fsdata->len_skp, + fsdata->msgid); + if (iter == NULL) { + return ISO_OUT_OF_MEM; + } + + while ((ret = susp_iter_next(iter, &sue)) > 0) { + + /* ignore entries from different version */ + if (sue->version[0] != 1) + continue; + + if (SUSP_SIG(sue, 'P', 'X')) { + ret = read_rr_PX(sue, &atts); + if (ret < 0) { + /* notify and continue */ + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR_WARN, ret, + "Invalid PX entry"); + } + } else if (SUSP_SIG(sue, 'T', 'F')) { + ret = read_rr_TF(sue, &atts); + if (ret < 0) { + /* notify and continue */ + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR_WARN, ret, + "Invalid TF entry"); + } + } else if (SUSP_SIG(sue, 'N', 'M')) { + if (name != NULL && namecont == 0) { + /* ups, RR standard violation */ + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR_WARN, 0, + "New NM entry found without previous" + "CONTINUE flag. Ignored"); + continue; + } + ret = read_rr_NM(sue, &name, &namecont); + if (ret < 0) { + /* notify and continue */ + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR_WARN, ret, + "Invalid NM entry"); + } + } else if (SUSP_SIG(sue, 'S', 'L')) { + if (linkdest != NULL && linkdestcont == 0) { + /* ups, RR standard violation */ + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR_WARN, 0, + "New SL entry found without previous" + "CONTINUE flag. Ignored"); + continue; + } + ret = read_rr_SL(sue, &linkdest, &linkdestcont); + if (ret < 0) { + /* notify and continue */ + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR_WARN, ret, + "Invalid SL entry"); + } + } else if (SUSP_SIG(sue, 'R', 'E')) { + /* + * this directory entry refers to a relocated directory. + * We simply ignore it, as it will be correctly handled + * when found the CL + */ + susp_iter_free(iter); + free(name); + return 0; /* it's not an error */ + } else if (SUSP_SIG(sue, 'C', 'L')) { + /* + * This entry is a placeholder for a relocated dir. + * We need to ignore other entries, with the exception of NM. + * Then we create a directory node that represents the + * relocated dir, and iterate over its children. + */ + relocated_dir = iso_read_bb(sue->data.CL.child_loc, 4, NULL); + if (relocated_dir == 0) { + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR, 0, + "Invalid SL entry, no child location"); + break; + } + } else if (SUSP_SIG(sue, 'P', 'N')) { + ret = read_rr_PN(sue, &atts); + if (ret < 0) { + /* notify and continue */ + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR_WARN, ret, + "Invalid PN entry"); + } + } else if (SUSP_SIG(sue, 'S', 'F')) { + ret = iso_msg_submit(fsdata->msgid, ISO_UNSUPPORTED_RR, 0, + "Sparse files not supported."); + break; + } else if (SUSP_SIG(sue, 'R', 'R')) { + /* TODO I've seen this RR on mkisofs images. what's this? */ + continue; + } else if (SUSP_SIG(sue, 'S', 'P')) { + /* + * Ignore this, to prevent the hint message, if we are dealing + * with root node (SP is only valid in "." of root node) + */ + if (parent != NULL) { + /* notify and continue */ + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR, 0, + "SP entry found in a directory entry other " + "than '.' entry of root node"); + } + continue; + } else if (SUSP_SIG(sue, 'E', 'R')) { + /* + * Ignore this, to prevent the hint message, if we are dealing + * with root node (ER is only valid in "." of root node) + */ + if (parent != NULL) { + /* notify and continue */ + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR, 0, + "ER entry found in a directory entry other " + "than '.' entry of root node"); + } + continue; + } else { + ret = iso_msg_submit(fsdata->msgid, ISO_SUSP_UNHANDLED, 0, + "Unhandled SUSP entry %c%c.", sue->sig[0], sue->sig[1]); + } + } + + susp_iter_free(iter); + + /* check for RR problems */ + + if (ret < 0) { + /* error was already submitted above */ + iso_msg_debug(fsdata->msgid, "Error parsing RR entries"); + } else if (!relocated_dir && atts.st_mode == (mode_t) 0 ) { + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR, 0, "Mandatory " + "Rock Ridge PX entry is not present or it " + "contains invalid values."); + } else { + /* ensure both name and link dest are finished */ + if (namecont != 0) { + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR, 0, + "Incomplete RR name, last NM entry continues"); + } + if (linkdestcont != 0) { + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR, 0, + "Incomplete link destination, last SL entry continues"); + } + } + + if (ret < 0) { + free(name); + return ret; + } + + /* convert name to needed charset */ + if (strcmp(fsdata->input_charset, fsdata->local_charset) && name) { + /* we need to convert name charset */ + char *newname = NULL; + ret = strconv(name, fsdata->input_charset, fsdata->local_charset, + &newname); + if (ret < 0) { + /* its just a hint message */ + ret = iso_msg_submit(fsdata->msgid, ISO_FILENAME_WRONG_CHARSET, + ret, "Charset conversion error. Can't " + "convert %s from %s to %s", name, + fsdata->input_charset, fsdata->local_charset); + free(newname); + if (ret < 0) { + free(name); + return ret; + } + } else { + free(name); + name = newname; + } + } + + /* convert link destination to needed charset */ + if (strcmp(fsdata->input_charset, fsdata->local_charset) && linkdest) { + /* we need to convert name charset */ + char *newlinkdest = NULL; + ret = strconv(linkdest, fsdata->input_charset, + fsdata->local_charset, &newlinkdest); + if (ret < 0) { + ret = iso_msg_submit(fsdata->msgid, ISO_FILENAME_WRONG_CHARSET, + ret, "Charset conversion error. Can't " + "convert %s from %s to %s", name, + fsdata->input_charset, fsdata->local_charset); + free(newlinkdest); + if (ret < 0) { + free(name); + return ret; + } + } else { + free(linkdest); + linkdest = newlinkdest; + } + } + + } else { + /* RR extensions are not read / used */ + atts.st_gid = fsdata->gid; + atts.st_uid = fsdata->uid; + if (record->flags[0] & 0x02) { + atts.st_mode = S_IFDIR | fsdata->dir_mode; + } else { + atts.st_mode = S_IFREG | fsdata->file_mode; + } + } + + /* + * if we haven't RR extensions, or no NM entry is present, + * we use the name in directory record + */ + if (!name) { + size_t len; + + if (record->len_fi[0] == 1 && record->file_id[0] == 0) { + /* "." entry, we can call this for root node, so... */ + if (!(atts.st_mode & S_IFDIR)) { + return iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0, + "Wrong ISO file name. \".\" not dir"); + } + } else { + + name = get_name(fsdata, (char*)record->file_id, record->len_fi[0]); + if (name == NULL) { + return iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0, + "Can't retrieve file name"); + } + + /* remove trailing version number */ + len = strlen(name); + if (len > 2 && name[len-2] == ';' && name[len-1] == '1') { + if (len > 3 && name[len-3] == '.') { + /* + * the "." is mandatory, so in most cases is included only + * for standard compliance + */ + name[len-3] = '\0'; + } else { + name[len-2] = '\0'; + } + } + } + } + + if (relocated_dir) { + + /* + * We are dealing with a placeholder for a relocated dir. + * Thus, we need to read attributes for this directory from the "." + * entry of the relocated dir. + */ + uint8_t buffer[BLOCK_SIZE]; + + ret = fsdata->src->read_block(fsdata->src, relocated_dir, buffer); + if (ret < 0) { + return ret; + } + + ret = iso_file_source_new_ifs(fs, parent, (struct ecma119_dir_record*) + buffer, src); + if (ret <= 0) { + return ret; + } + + /* but the real name is the name of the placeholder */ + ifsdata = (ImageFileSourceData*) (*src)->data; + ifsdata->name = name; + return ISO_SUCCESS; + } + + if (fsdata->rr != RR_EXT_112) { + /* + * Only RRIP 1.12 provides valid inode numbers. If not, it is not easy + * to generate those serial numbers, and we use extend block instead. + * It BREAKS POSIX SEMANTICS, but its suitable for our needs + */ + atts.st_ino = (ino_t) iso_read_bb(record->block, 4, NULL); + if (fsdata->rr == 0) { + atts.st_nlink = 1; + } + } + + /* + * if we haven't RR extensions, or a needed TF time stamp is not present, + * we use plain iso recording time + */ + recorded = iso_datetime_read_7(record->recording_time); + if (atts.st_atime == (time_t) 0) { + atts.st_atime = recorded; + } + if (atts.st_ctime == (time_t) 0) { + atts.st_ctime = recorded; + } + if (atts.st_mtime == (time_t) 0) { + atts.st_mtime = recorded; + } + + /* the size is read from iso directory record */ + atts.st_size = iso_read_bb(record->length, 4, NULL); + + /* Fill last entries */ + atts.st_dev = fsdata->id; + atts.st_blksize = BLOCK_SIZE; + atts.st_blocks = DIV_UP(atts.st_size, BLOCK_SIZE); + + /* TODO #00014 : more sanity checks to ensure dir record info is valid */ + if (S_ISLNK(atts.st_mode) && (linkdest == NULL)) { + ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_RR, 0, + "Link without destination."); + free(name); + return ret; + } + + /* ok, we can now create the file source */ + ifsdata = calloc(1, sizeof(ImageFileSourceData)); + if (ifsdata == NULL) { + ret = ISO_OUT_OF_MEM; + goto ifs_cleanup; + } + ifsrc = calloc(1, sizeof(IsoFileSource)); + if (ifsrc == NULL) { + ret = ISO_OUT_OF_MEM; + goto ifs_cleanup; + } + + /* fill data */ + ifsdata->fs = fs; + iso_filesystem_ref(fs); + if (parent != NULL) { + ifsdata->parent = parent; + iso_file_source_ref(parent); + } + ifsdata->info = atts; + ifsdata->name = name; + ifsdata->block = iso_read_bb(record->block, 4, NULL); + + if (S_ISLNK(atts.st_mode)) { + ifsdata->data.content = linkdest; + } + + ifsrc->class = &ifs_class; + ifsrc->data = ifsdata; + ifsrc->refcount = 1; + + *src = ifsrc; + return ISO_SUCCESS; + +ifs_cleanup: ; + free(name); + free(linkdest); + free(ifsdata); + free(ifsrc); + return ret; +} + +static +int ifs_get_root(IsoFilesystem *fs, IsoFileSource **root) +{ + int ret; + _ImageFsData *data; + uint8_t buffer[BLOCK_SIZE]; + + if (fs == NULL || fs->data == NULL || root == NULL) { + return ISO_NULL_POINTER; + } + + data = (_ImageFsData*)fs->data; + + /* open the filesystem */ + ret = ifs_fs_open((IsoImageFilesystem*)fs); + if (ret < 0) { + return ret; + } + + /* read extend for root record */ + ret = data->src->read_block(data->src, data->iso_root_block, buffer); + if (ret < 0) { + ifs_fs_close((IsoImageFilesystem*)fs); + return ret; + } + + /* get root attributes from "." entry */ + ret = iso_file_source_new_ifs((IsoImageFilesystem*)fs, NULL, + (struct ecma119_dir_record*) buffer, root); + + ifs_fs_close((IsoImageFilesystem*)fs); + return ret; +} + +/** + * Find a file inside a node. + * + * @param file + * it is not modified if requested file is not found + * @return + * 1 success, 0 not found, < 0 error + */ +static +int ifs_get_file(IsoFileSource *dir, const char *name, IsoFileSource **file) +{ + int ret; + IsoFileSource *src; + + ret = iso_file_source_open(dir); + if (ret < 0) { + return ret; + } + while ((ret = iso_file_source_readdir(dir, &src)) == 1) { + char *fname = iso_file_source_get_name(src); + if (!strcmp(name, fname)) { + free(fname); + *file = src; + ret = ISO_SUCCESS; + break; + } + free(fname); + iso_file_source_unref(src); + } + iso_file_source_close(dir); + return ret; +} + +static +int ifs_get_by_path(IsoFilesystem *fs, const char *path, IsoFileSource **file) +{ + int ret; + _ImageFsData *data; + IsoFileSource *src; + char *ptr, *brk_info, *component; + + if (fs == NULL || fs->data == NULL || path == NULL || file == NULL) { + return ISO_NULL_POINTER; + } + + if (path[0] != '/') { + /* only absolute paths supported */ + return ISO_FILE_BAD_PATH; + } + + data = (_ImageFsData*)fs->data; + + /* open the filesystem */ + ret = ifs_fs_open((IsoImageFilesystem*)fs); + if (ret < 0) { + return ret; + } + + ret = ifs_get_root(fs, &src); + if (ret < 0) { + return ret; + } + if (!strcmp(path, "/")) { + /* we are looking for root */ + *file = src; + ret = ISO_SUCCESS; + goto get_path_exit; + } + + ptr = strdup(path); + if (ptr == NULL) { + iso_file_source_unref(src); + ret = ISO_OUT_OF_MEM; + goto get_path_exit; + } + + component = strtok_r(ptr, "/", &brk_info); + while (component) { + IsoFileSource *child = NULL; + + ImageFileSourceData *fdata; + fdata = src->data; + if (!S_ISDIR(fdata->info.st_mode)) { + ret = ISO_FILE_BAD_PATH; + break; + } + + ret = ifs_get_file(src, component, &child); + iso_file_source_unref(src); + if (ret <= 0) { + break; + } + + src = child; + component = strtok_r(NULL, "/", &brk_info); + } + + free(ptr); + if (ret < 0) { + iso_file_source_unref(src); + } else if (ret == 0) { + ret = ISO_FILE_DOESNT_EXIST; + } else { + *file = src; + } + + get_path_exit:; + ifs_fs_close((IsoImageFilesystem*)fs); + return ret; +} + +unsigned int ifs_get_id(IsoFilesystem *fs) +{ + return ISO_IMAGE_FS_ID; +} + +static +int ifs_fs_open(IsoImageFilesystem *fs) +{ + _ImageFsData *data; + + if (fs == NULL || fs->data == NULL) { + return ISO_NULL_POINTER; + } + + data = (_ImageFsData*)fs->data; + + if (data->open_count == 0) { + /* we need to actually open the data source */ + int res = data->src->open(data->src); + if (res < 0) { + return res; + } + } + ++data->open_count; + return ISO_SUCCESS; +} + +static +int ifs_fs_close(IsoImageFilesystem *fs) +{ + _ImageFsData *data; + + if (fs == NULL || fs->data == NULL) { + return ISO_NULL_POINTER; + } + + data = (_ImageFsData*)fs->data; + + if (--data->open_count == 0) { + /* we need to actually close the data source */ + return data->src->close(data->src); + } + return ISO_SUCCESS; +} + +static +void ifs_fs_free(IsoFilesystem *fs) +{ + IsoImageFilesystem *ifs; + _ImageFsData *data; + + ifs = (IsoImageFilesystem*)fs; + data = (_ImageFsData*) fs->data; + + /* close data source if already openned */ + if (data->open_count > 0) { + data->src->close(data->src); + } + + /* free our ref to datasource */ + iso_data_source_unref(data->src); + + /* free volume atts */ + free(data->volset_id); + free(data->volume_id); + free(data->publisher_id); + free(data->data_preparer_id); + free(data->system_id); + free(data->application_id); + free(data->copyright_file_id); + free(data->abstract_file_id); + free(data->biblio_file_id); + + free(data->input_charset); + free(data->local_charset); + free(data); +} + +/** + * Read the SUSP system user entries of the "." entry of the root directory, + * indentifying when Rock Ridge extensions are being used. + * + * @return + * 1 success, 0 ignored, < 0 error + */ +static +int read_root_susp_entries(_ImageFsData *data, uint32_t block) +{ + int ret; + unsigned char buffer[2048]; + struct ecma119_dir_record *record; + struct susp_sys_user_entry *sue; + SuspIterator *iter; + + ret = data->src->read_block(data->src, block, buffer); + if (ret < 0) { + return ret; + } + + /* record will be the "." directory entry for the root record */ + record = (struct ecma119_dir_record *)buffer; + + /* + * TODO #00015 : take care of CD-ROM XA discs when reading SP entry + * SUSP specification claims that for CD-ROM XA the SP entry + * is not at position BP 1, but at BP 15. Is that used? + * In that case, we need to set info->len_skp to 15!! + */ + + iter = susp_iter_new(data->src, record, data->len_skp, data->msgid); + if (iter == NULL) { + return ISO_OUT_OF_MEM; + } + + /* first entry must be an SP system use entry */ + ret = susp_iter_next(iter, &sue); + if (ret < 0) { + /* error */ + susp_iter_free(iter); + return ret; + } else if (ret == 0 || !SUSP_SIG(sue, 'S', 'P') ) { + iso_msg_debug(data->msgid, "SUSP/RR is not being used."); + susp_iter_free(iter); + return ISO_SUCCESS; + } + + /* it is a SP system use entry */ + if (sue->version[0] != 1 || sue->data.SP.be[0] != 0xBE + || sue->data.SP.ef[0] != 0xEF) { + + susp_iter_free(iter); + return iso_msg_submit(data->msgid, ISO_UNSUPPORTED_SUSP, 0, + "SUSP SP system use entry seems to be wrong. " + "Ignoring Rock Ridge Extensions."); + } + + iso_msg_debug(data->msgid, "SUSP/RR is being used."); + + /* + * The LEN_SKP field, defined in IEEE 1281, SUSP. 5.3, specifies the + * number of bytes to be skipped within each System Use field. + * I think this will be always 0, but given that support this standard + * feature is easy... + */ + data->len_skp = sue->data.SP.len_skp[0]; + + /* + * Ok, now search for ER entry. + * Just notice that the attributes for root dir are read elsewhere. + * + * TODO #00016 : handle non RR ER entries + * + * if several ER are present, we need to identify the position of + * what refers to RR, and then look for corresponding ES entry in + * each directory record. I have not implemented this (it's not used, + * no?), but if we finally need it, it can be easily implemented in + * the iterator, transparently for the rest of the code. + */ + while ((ret = susp_iter_next(iter, &sue)) > 0) { + + /* ignore entries from different version */ + if (sue->version[0] != 1) + continue; + + if (SUSP_SIG(sue, 'E', 'R')) { + + if (data->rr_version) { + ret = iso_msg_submit(data->msgid, ISO_SUSP_MULTIPLE_ER, 0, + "More than one ER has found. This is not supported. " + "It will be ignored, but can cause problems. " + "Please notify us about this."); + if (ret < 0) { + break; + } + } + + /* + * it seems that Rock Ridge can be identified with any + * of the following + */ + if ( sue->data.ER.len_id[0] == 10 && + !strncmp((char*)sue->data.ER.ext_id, "RRIP_1991A", 10) ) { + + iso_msg_debug(data->msgid, + "Suitable Rock Ridge ER found. Version 1.10."); + data->rr_version = RR_EXT_110; + + } else if ( (sue->data.ER.len_id[0] == 10 && + !strncmp((char*)sue->data.ER.ext_id, "IEEE_P1282", 10)) + || (sue->data.ER.len_id[0] == 9 && + !strncmp((char*)sue->data.ER.ext_id, "IEEE_1282", 9)) ) { + + iso_msg_debug(data->msgid, + "Suitable Rock Ridge ER found. Version 1.12."); + data->rr_version = RR_EXT_112; + } else { + ret = iso_msg_submit(data->msgid, ISO_SUSP_MULTIPLE_ER, 0, + "Not Rock Ridge ER found.\n" + "That will be ignored, but can cause problems in " + "image reading. Please notify us about this"); + if (ret < 0) { + break; + } + } + } + } + + susp_iter_free(iter); + + if (ret < 0) { + return ret; + } + + return ISO_SUCCESS; +} + +static +int read_pvm(_ImageFsData *data, uint32_t block) +{ + int ret; + struct ecma119_pri_vol_desc *pvm; + struct ecma119_dir_record *rootdr; + uint8_t buffer[BLOCK_SIZE]; + + /* read PVM */ + ret = data->src->read_block(data->src, block, buffer); + if (ret < 0) { + return ret; + } + + pvm = (struct ecma119_pri_vol_desc *)buffer; + + /* sanity checks */ + if (pvm->vol_desc_type[0] != 1 || pvm->vol_desc_version[0] != 1 + || strncmp((char*)pvm->std_identifier, "CD001", 5) + || pvm->file_structure_version[0] != 1) { + + return ISO_WRONG_PVD; + } + + /* ok, it is a valid PVD */ + + /* fill volume attributes */ + /* TODO take care of input charset */ + data->volset_id = strcopy((char*)pvm->vol_set_id, 128); + data->volume_id = strcopy((char*)pvm->volume_id, 32); + data->publisher_id = strcopy((char*)pvm->publisher_id, 128); + data->data_preparer_id = strcopy((char*)pvm->data_prep_id, 128); + data->system_id = strcopy((char*)pvm->system_id, 32); + data->application_id = strcopy((char*)pvm->application_id, 128); + data->copyright_file_id = strcopy((char*)pvm->copyright_file_id, 37); + data->abstract_file_id = strcopy((char*)pvm->abstract_file_id, 37); + data->biblio_file_id = strcopy((char*)pvm->bibliographic_file_id, 37); + + data->nblocks = iso_read_bb(pvm->vol_space_size, 4, NULL); + + rootdr = (struct ecma119_dir_record*) pvm->root_dir_record; + data->pvd_root_block = iso_read_bb(rootdr->block, 4, NULL); + + /* + * TODO #00017 : take advantage of other atts of PVD + * PVD has other things that could be interesting, but that don't have a + * member in IsoImage, such as creation date. In a multisession disc, we + * could keep the creation date and update the modification date, for + * example. + */ + + return ISO_SUCCESS; +} + +/** + * @return + * 1 success, 0 ignored, < 0 error + */ +static +int read_el_torito_boot_catalog(_ImageFsData *data, uint32_t block) +{ + int ret; + struct el_torito_validation_entry *ve; + struct el_torito_default_entry *entry; + unsigned char buffer[BLOCK_SIZE]; + + ret = data->src->read_block(data->src, block, buffer); + if (ret < 0) { + return ret; + } + + ve = (struct el_torito_validation_entry*)buffer; + + /* check if it is a valid catalog (TODO: check also the checksum)*/ + if ( (ve->header_id[0] != 1) || (ve->key_byte1[0] != 0x55) + || (ve->key_byte2[0] != 0xAA) ) { + + return iso_msg_submit(data->msgid, ISO_WRONG_EL_TORITO, 0, + "Wrong or damaged El-Torito Catalog. El-Torito info " + "will be ignored."); + } + + /* check for a valid platform */ + if (ve->platform_id[0] != 0) { + return iso_msg_submit(data->msgid, ISO_UNSUPPORTED_EL_TORITO, 0, + "Unsupported El-Torito platform. Only 80x86 is " + "supported. El-Torito info will be ignored."); + } + + /* ok, once we are here we assume it is a valid catalog */ + + /* parse the default entry */ + entry = (struct el_torito_default_entry *)(buffer + 32); + + data->eltorito = 1; + data->bootable = entry->boot_indicator[0] ? 1 : 0; + data->type = entry->boot_media_type[0]; + data->partition_type = entry->system_type[0]; + data->load_seg = iso_read_lsb(entry->load_seg, 2); + data->load_size = iso_read_lsb(entry->sec_count, 2); + data->imgblock = iso_read_lsb(entry->block, 4); + + /* TODO #00018 : check if there are more entries in the boot catalog */ + + return ISO_SUCCESS; +} + +int iso_image_filesystem_new(IsoDataSource *src, struct iso_read_opts *opts, + int msgid, IsoImageFilesystem **fs) +{ + int ret; + uint32_t block; + IsoImageFilesystem *ifs; + _ImageFsData *data; + uint8_t buffer[BLOCK_SIZE]; + + if (src == NULL || opts == NULL || fs == NULL) { + return ISO_NULL_POINTER; + } + + data = calloc(1, sizeof(_ImageFsData)); + if (data == NULL) { + return ISO_OUT_OF_MEM; + } + + ifs = calloc(1, sizeof(IsoImageFilesystem)); + if (ifs == NULL) { + free(data); + return ISO_OUT_OF_MEM; + } + + /* get our ref to IsoDataSource */ + data->src = src; + iso_data_source_ref(src); + data->open_count = 0; + + /* get an id for the filesystem */ + data->id = ++fs_dev_id; + + /* fill data from opts */ + data->gid = opts->gid; + data->uid = opts->uid; + data->file_mode = opts->file_mode & ~S_IFMT; + data->dir_mode = opts->dir_mode & ~S_IFMT; + data->msgid = msgid; + + setlocale(LC_CTYPE, ""); + data->local_charset = strdup(nl_langinfo(CODESET)); + if (data->local_charset == NULL) { + ret = ISO_OUT_OF_MEM; + goto fs_cleanup; + } + + strncpy(ifs->type, "iso ", 4); + ifs->data = data; + ifs->refcount = 1; + ifs->version = 0; + ifs->get_root = ifs_get_root; + ifs->get_by_path = ifs_get_by_path; + ifs->get_id = ifs_get_id; + ifs->open = ifs_fs_open; + ifs->close = ifs_fs_close; + ifs->free = ifs_fs_free; + + /* read Volume Descriptors and ensure it is a valid image */ + + /* 1. first, open the filesystem */ + ifs_fs_open(ifs); + + /* 2. read primary volume description */ + ret = read_pvm(data, opts->block + 16); + if (ret < 0) { + goto fs_cleanup; + } + + /* 3. read next volume descriptors */ + block = opts->block + 17; + do { + ret = src->read_block(src, block, buffer); + if (ret < 0) { + /* cleanup and exit */ + goto fs_cleanup; + } + switch (buffer[0]) { + case 0: + /* boot record */ + { + struct ecma119_boot_rec_vol_desc *vol; + vol = (struct ecma119_boot_rec_vol_desc*)buffer; + + /* some sanity checks */ + if (strncmp((char*)vol->std_identifier, "CD001", 5) + || vol->vol_desc_version[0] != 1 + || strncmp((char*)vol->boot_sys_id, + "EL TORITO SPECIFICATION", 23)) { + + ret = iso_msg_submit(data->msgid, + ISO_UNSUPPORTED_EL_TORITO, 0, + "Unsupported Boot Vol. Desc. Only El-Torito " + "Specification, Version 1.0 Volume " + "Descriptors are supported. Ignoring boot info"); + if (ret < 0) { + goto fs_cleanup; + } + break; + } + data->catblock = iso_read_lsb(vol->boot_catalog, 4); + ret = read_el_torito_boot_catalog(data, data->catblock); + if (ret < 0) { + goto fs_cleanup; + } + } + break; + case 2: + /* suplementary volume descritor */ + { + struct ecma119_sup_vol_desc *sup; + struct ecma119_dir_record *root; + + sup = (struct ecma119_sup_vol_desc*)buffer; + if (sup->esc_sequences[0] == 0x25 && + sup->esc_sequences[1] == 0x2F && + (sup->esc_sequences[2] == 0x40 || + sup->esc_sequences[2] == 0x43 || + sup->esc_sequences[2] == 0x45) ) { + + /* it's a Joliet Sup. Vol. Desc. */ + iso_msg_debug(data->msgid, "Found Joliet extensions"); + data->joliet = 1; + root = (struct ecma119_dir_record*)sup->root_dir_record; + data->svd_root_block = iso_read_bb(root->block, 4, NULL); + /* TODO #00019 : set IsoImage attribs from Joliet SVD? */ + /* TODO #00020 : handle RR info in Joliet tree */ + } else if (sup->vol_desc_version[0] == 2) { + /* + * It is an Enhanced Volume Descriptor, image is an + * ISO 9660:1999 + */ + iso_msg_debug(data->msgid, "Found ISO 9660:1999"); + data->iso1999 = 1; + root = (struct ecma119_dir_record*)sup->root_dir_record; + data->evd_root_block = iso_read_bb(root->block, 4, NULL); + /* TODO #00021 : handle RR info in ISO 9660:1999 tree */ + } else { + ret = iso_msg_submit(data->msgid, ISO_UNSUPPORTED_VD, 0, + "Unsupported Sup. Vol. Desc found."); + if (ret < 0) { + goto fs_cleanup; + } + } + } + break; + case 255: + /* + * volume set terminator + * ignore, as it's checked in loop end condition + */ + break; + default: + ret = iso_msg_submit(data->msgid, ISO_UNSUPPORTED_VD, 0, + "Ignoring Volume descriptor %x.", buffer[0]); + if (ret < 0) { + goto fs_cleanup; + } + break; + } + block++; + } while (buffer[0] != 255); + + /* 4. check if RR extensions are being used */ + ret = read_root_susp_entries(data, data->pvd_root_block); + if (ret < 0) { + goto fs_cleanup; + } + + /* user doesn't want to read RR extensions */ + if (opts->norock) { + data->rr = RR_EXT_NO; + } else { + data->rr = data->rr_version; + } + + /* select what tree to read */ + if (data->rr) { + /* RR extensions are available */ + if (!opts->nojoliet && opts->preferjoliet && data->joliet) { + /* if user prefers joliet, that is used */ + iso_msg_debug(data->msgid, "Reading Joliet extensions."); + data->input_charset = strdup("UCS-2BE"); + data->rr = RR_EXT_NO; + data->iso_root_block = data->svd_root_block; + } else { + /* RR will be used */ + iso_msg_debug(data->msgid, "Reading Rock Ridge extensions."); + data->iso_root_block = data->pvd_root_block; + } + } else { + /* RR extensions are not available */ + if (!opts->nojoliet && data->joliet) { + /* joliet will be used */ + iso_msg_debug(data->msgid, "Reading Joliet extensions."); + data->input_charset = strdup("UCS-2BE"); + data->iso_root_block = data->svd_root_block; + } else if (!opts->noiso1999 && data->iso1999) { + /* we will read ISO 9660:1999 */ + iso_msg_debug(data->msgid, "Reading ISO-9660:1999 tree."); + data->iso_root_block = data->evd_root_block; + } else { + /* default to plain iso */ + iso_msg_debug(data->msgid, "Reading plain ISO-9660 tree."); + data->iso_root_block = data->pvd_root_block; + data->input_charset = strdup("ASCII"); + } + } + + if (data->input_charset == NULL) { + if (opts->input_charset != NULL) { + data->input_charset = strdup(opts->input_charset); + } else { + data->input_charset = strdup(data->local_charset); + } + } + if (data->input_charset == NULL) { + ret = ISO_OUT_OF_MEM; + goto fs_cleanup; + } + + /* and finally return. Note that we keep the DataSource opened */ + + *fs = ifs; + return ISO_SUCCESS; + + fs_cleanup: ; + ifs_fs_free(ifs); + free(ifs); + return ret; +} + +static +int image_builder_create_node(IsoNodeBuilder *builder, IsoImage *image, + IsoFileSource *src, IsoNode **node) +{ + int ret; + struct stat info; + IsoNode *new; + char *name; + ImageFileSourceData *data; + + if (builder == NULL || src == NULL || node == NULL || src->data == NULL) { + return ISO_NULL_POINTER; + } + + data = (ImageFileSourceData*)src->data; + + name = iso_file_source_get_name(src); + + /* get info about source */ + ret = iso_file_source_lstat(src, &info); + if (ret < 0) { + return ret; + } + + new = NULL; + switch (info.st_mode & S_IFMT) { + case S_IFREG: + { + /* source is a regular file */ + _ImageFsData *fsdata = data->fs->data; + + if (fsdata->eltorito && data->block == fsdata->catblock) { + + if (image->bootcat->node != NULL) { + ret = iso_msg_submit(image->id, ISO_EL_TORITO_WARN, 0, + "More than one catalog node has been found. " + "We can continue, but that could lead to " + "problems"); + if (ret < 0) { + return ret; + } + iso_node_unref((IsoNode*)image->bootcat->node); + } + + /* we create a placeholder for the catalog instead of + * a regular file */ + new = calloc(1, sizeof(IsoBoot)); + if (new == NULL) { + ret = ISO_OUT_OF_MEM; + free(name); + return ret; + } + + /* and set the image node */ + image->bootcat->node = (IsoBoot*)new; + new->type = LIBISO_BOOT; + new->refcount = 1; + } else { + IsoStream *stream; + IsoFile *file; + + ret = iso_file_source_stream_new(src, &stream); + if (ret < 0) { + free(name); + return ret; + } + /* take a ref to the src, as stream has taken our ref */ + iso_file_source_ref(src); + + file = calloc(1, sizeof(IsoFile)); + if (file == NULL) { + free(name); + iso_stream_unref(stream); + return ISO_OUT_OF_MEM; + } + + /* the msblock is taken from the image */ + file->msblock = data->block; + + /* + * and we set the sort weight based on the block on image, to + * improve performance on image modifying. + */ + file->sort_weight = INT_MAX - data->block; + + file->stream = stream; + file->node.type = LIBISO_FILE; + new = (IsoNode*) file; + new->refcount = 0; + + if (fsdata->eltorito && data->block == fsdata->imgblock) { + /* it is boot image node */ + if (image->bootcat->image->image != NULL) { + ret = iso_msg_submit(image->id, ISO_EL_TORITO_WARN, 0, + "More than one image node has been found."); + if (ret < 0) { + free(name); + iso_stream_unref(stream); + return ret; + } + } else { + /* and set the image node */ + image->bootcat->image->image = file; + new->refcount++; + } + } + } + } + break; + case S_IFDIR: + { + /* source is a directory */ + new = calloc(1, sizeof(IsoDir)); + if (new == NULL) { + free(name); + return ISO_OUT_OF_MEM; + } + new->type = LIBISO_DIR; + new->refcount = 0; + } + break; + case S_IFLNK: + { + /* source is a symbolic link */ + char dest[PATH_MAX]; + IsoSymlink *link; + + ret = iso_file_source_readlink(src, dest, PATH_MAX); + if (ret < 0) { + free(name); + return ret; + } + link = malloc(sizeof(IsoSymlink)); + if (link == NULL) { + free(name); + return ISO_OUT_OF_MEM; + } + link->dest = strdup(dest); + link->node.type = LIBISO_SYMLINK; + new = (IsoNode*) link; + new->refcount = 0; + } + break; + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + { + /* source is an special file */ + IsoSpecial *special; + special = malloc(sizeof(IsoSpecial)); + if (special == NULL) { + free(name); + return ISO_OUT_OF_MEM; + } + special->dev = info.st_rdev; + special->node.type = LIBISO_SPECIAL; + new = (IsoNode*) special; + new->refcount = 0; + } + break; + } + + /* fill fields */ + new->refcount++; + new->name = name; + new->mode = info.st_mode; + new->uid = info.st_uid; + new->gid = info.st_gid; + new->atime = info.st_atime; + new->mtime = info.st_mtime; + new->ctime = info.st_ctime; + + new->hidden = 0; + + new->parent = NULL; + new->next = NULL; + + *node = new; + return ISO_SUCCESS; +} + +/** + * Create a new builder, that is exactly a copy of an old builder, but where + * create_node() function has been replaced by image_builder_create_node. + */ +static +int iso_image_builder_new(IsoNodeBuilder *old, IsoNodeBuilder **builder) +{ + IsoNodeBuilder *b; + + if (builder == NULL) { + return ISO_NULL_POINTER; + } + + b = malloc(sizeof(IsoNodeBuilder)); + if (b == NULL) { + return ISO_OUT_OF_MEM; + } + + b->refcount = 1; + b->create_file_data = old->create_file_data; + b->create_node_data = old->create_node_data; + b->create_file = old->create_file; + b->create_node = image_builder_create_node; + b->free = old->free; + + *builder = b; + return ISO_SUCCESS; +} + +/** + * Create a file source to access the El-Torito boot image, when it is not + * accessible from the ISO filesystem. + */ +static +int create_boot_img_filesrc(IsoImageFilesystem *fs, IsoFileSource **src) +{ + int ret; + struct stat atts; + _ImageFsData *fsdata; + IsoFileSource *ifsrc = NULL; + ImageFileSourceData *ifsdata = NULL; + + if (fs == NULL || fs->data == NULL || src == NULL) { + return ISO_NULL_POINTER; + } + + fsdata = (_ImageFsData*)fs->data; + + memset(&atts, 0, sizeof(struct stat)); + atts.st_mode = S_IFREG; + atts.st_ino = fsdata->imgblock; /* not the best solution, but... */ + atts.st_nlink = 1; + + /* + * this is the greater problem. We don't know the size. For now, we + * just use a single block of data. In a future, maybe we could figure out + * a better idea. Another alternative is to use several blocks, that way + * is less probable that we throw out valid data. + */ + atts.st_size = (off_t)BLOCK_SIZE; + + /* Fill last entries */ + atts.st_dev = fsdata->id; + atts.st_blksize = BLOCK_SIZE; + atts.st_blocks = DIV_UP(atts.st_size, BLOCK_SIZE); + + /* ok, we can now create the file source */ + ifsdata = calloc(1, sizeof(ImageFileSourceData)); + if (ifsdata == NULL) { + ret = ISO_OUT_OF_MEM; + goto boot_fs_cleanup; + } + ifsrc = calloc(1, sizeof(IsoFileSource)); + if (ifsrc == NULL) { + ret = ISO_OUT_OF_MEM; + goto boot_fs_cleanup; + } + + /* fill data */ + ifsdata->fs = fs; + iso_filesystem_ref(fs); + ifsdata->parent = NULL; + ifsdata->info = atts; + ifsdata->name = NULL; + ifsdata->block = fsdata->imgblock; + + ifsrc->class = &ifs_class; + ifsrc->data = ifsdata; + ifsrc->refcount = 1; + + *src = ifsrc; + return ISO_SUCCESS; + +boot_fs_cleanup: ; + free(ifsdata); + free(ifsrc); + return ret; +} + + +int iso_image_import(IsoImage *image, IsoDataSource *src, + struct iso_read_opts *opts, + IsoReadImageFeatures **features) +{ + int ret; + IsoImageFilesystem *fs; + IsoFilesystem *fsback; + IsoNodeBuilder *blback; + IsoDir *oldroot; + IsoFileSource *newroot; + _ImageFsData *data; + struct el_torito_boot_catalog *oldbootcat; + + if (image == NULL || src == NULL || opts == NULL) { + return ISO_NULL_POINTER; + } + + ret = iso_image_filesystem_new(src, opts, image->id, &fs); + if (ret < 0) { + return ret; + } + data = fs->data; + + /* get root from filesystem */ + ret = fs->get_root(fs, &newroot); + if (ret < 0) { + return ret; + } + + /* backup image filesystem, builder and root */ + fsback = image->fs; + blback = image->builder; + oldroot = image->root; + oldbootcat = image->bootcat; /* could be NULL */ + + image->bootcat = NULL; + + /* create new builder */ + ret = iso_image_builder_new(blback, &image->builder); + if (ret < 0) { + goto import_revert; + } + + image->fs = fs; + + /* create new root, and set root attributes from source */ + ret = iso_node_new_root(&image->root); + if (ret < 0) { + goto import_revert; + } + { + struct stat info; + + /* I know this will not fail */ + iso_file_source_lstat(newroot, &info); + image->root->node.mode = info.st_mode; + image->root->node.uid = info.st_uid; + image->root->node.gid = info.st_gid; + image->root->node.atime = info.st_atime; + image->root->node.mtime = info.st_mtime; + image->root->node.ctime = info.st_ctime; + } + + /* if old image has el-torito, add a new catalog */ + if (data->eltorito) { + struct el_torito_boot_catalog *catalog; + ElToritoBootImage *boot_image= NULL; + + boot_image = calloc(1, sizeof(ElToritoBootImage)); + if (boot_image == NULL) { + ret = ISO_OUT_OF_MEM; + goto import_revert; + } + boot_image->bootable = data->bootable; + boot_image->type = data->type; + boot_image->partition_type = data->partition_type; + boot_image->load_seg = data->load_seg; + boot_image->load_size = data->load_size; + + catalog = calloc(1, sizeof(struct el_torito_boot_catalog)); + if (catalog == NULL) { + ret = ISO_OUT_OF_MEM; + goto import_revert; + } + catalog->image = boot_image; + image->bootcat = catalog; + } + + /* recursively add image */ + ret = iso_add_dir_src_rec(image, image->root, newroot); + + /* error during recursive image addition? */ + if (ret < 0) { + iso_node_builder_unref(image->builder); + goto import_revert; + } + + if (data->eltorito) { + /* if catalog and image nodes were not filled, we create them here */ + if (image->bootcat->image->image == NULL) { + IsoFileSource *src; + IsoNode *node; + ret = create_boot_img_filesrc(fs, &src); + if (ret < 0) { + iso_node_builder_unref(image->builder); + goto import_revert; + } + ret = image_builder_create_node(image->builder, image, src, &node); + if (ret < 0) { + iso_node_builder_unref(image->builder); + goto import_revert; + } + image->bootcat->image->image = (IsoFile*)node; + + /* warn about hidden images */ + iso_msg_submit(image->id, ISO_EL_TORITO_HIDDEN, 0, + "Found hidden El-Torito image. Its size could not " + "be figure out, so image modify or boot image " + "patching may lead to bad results."); + } + if (image->bootcat->node == NULL) { + IsoNode *node = calloc(1, sizeof(IsoBoot)); + if (node == NULL) { + ret = ISO_OUT_OF_MEM; + goto import_revert; + } + node->type = LIBISO_BOOT; + node->mode = S_IFREG; + node->refcount = 1; + image->bootcat->node = (IsoBoot*)node; + } + } + + iso_node_builder_unref(image->builder); + + /* free old root */ + iso_node_unref((IsoNode*)oldroot); + + /* free old boot catalog */ + el_torito_boot_catalog_free(oldbootcat); + + /* set volume attributes */ + iso_image_set_volset_id(image, data->volset_id); + iso_image_set_volume_id(image, data->volume_id); + iso_image_set_publisher_id(image, data->publisher_id); + iso_image_set_data_preparer_id(image, data->data_preparer_id); + iso_image_set_system_id(image, data->system_id); + iso_image_set_application_id(image, data->application_id); + iso_image_set_copyright_file_id(image, data->copyright_file_id); + iso_image_set_abstract_file_id(image, data->abstract_file_id); + iso_image_set_biblio_file_id(image, data->biblio_file_id); + + if (features != NULL) { + *features = malloc(sizeof(IsoReadImageFeatures)); + if (*features == NULL) { + ret = ISO_OUT_OF_MEM; + goto import_cleanup; + } + (*features)->hasJoliet = data->joliet; + (*features)->hasRR = data->rr_version != 0; + (*features)->hasIso1999 = data->iso1999; + (*features)->hasElTorito = data->eltorito; + (*features)->size = data->nblocks; + } + + ret = ISO_SUCCESS; + goto import_cleanup; + + import_revert:; + + iso_node_unref((IsoNode*)image->root); + el_torito_boot_catalog_free(image->bootcat); + image->root = oldroot; + image->fs = fsback; + image->bootcat = oldbootcat; + + import_cleanup:; + + /* recover backed fs and builder */ + image->fs = fsback; + image->builder = blback; + + iso_file_source_unref(newroot); + fs->close(fs); + iso_filesystem_unref(fs); + + return ret; +} + +const char *iso_image_fs_get_volset_id(IsoImageFilesystem *fs) +{ + _ImageFsData *data = (_ImageFsData*) fs->data; + return data->volset_id; +} + +const char *iso_image_fs_get_volume_id(IsoImageFilesystem *fs) +{ + _ImageFsData *data = (_ImageFsData*) fs->data; + return data->volume_id; +} + +const char *iso_image_fs_get_publisher_id(IsoImageFilesystem *fs) +{ + _ImageFsData *data = (_ImageFsData*) fs->data; + return data->publisher_id; +} + +const char *iso_image_fs_get_data_preparer_id(IsoImageFilesystem *fs) +{ + _ImageFsData *data = (_ImageFsData*) fs->data; + return data->data_preparer_id; +} + +const char *iso_image_fs_get_system_id(IsoImageFilesystem *fs) +{ + _ImageFsData *data = (_ImageFsData*) fs->data; + return data->system_id; +} + +const char *iso_image_fs_get_application_id(IsoImageFilesystem *fs) +{ + _ImageFsData *data = (_ImageFsData*) fs->data; + return data->application_id; +} + +const char *iso_image_fs_get_copyright_file_id(IsoImageFilesystem *fs) +{ + _ImageFsData *data = (_ImageFsData*) fs->data; + return data->copyright_file_id; +} + +const char *iso_image_fs_get_abstract_file_id(IsoImageFilesystem *fs) +{ + _ImageFsData *data; + data = (_ImageFsData*) fs->data; + return data->abstract_file_id; +} + +const char *iso_image_fs_get_biblio_file_id(IsoImageFilesystem *fs) +{ + _ImageFsData *data = (_ImageFsData*) fs->data; + return data->biblio_file_id; +} + +int iso_read_opts_new(IsoReadOpts **opts, int profile) +{ + IsoReadOpts *ropts; + + if (opts == NULL) { + return ISO_NULL_POINTER; + } + if (profile != 0) { + return ISO_WRONG_ARG_VALUE; + } + + ropts = calloc(1, sizeof(IsoReadOpts)); + if (ropts == NULL) { + return ISO_OUT_OF_MEM; + } + + ropts->file_mode = 0444; + ropts->dir_mode = 0555; + *opts = ropts; + return ISO_SUCCESS; +} + +void iso_read_opts_free(IsoReadOpts *opts) +{ + if (opts == NULL) { + return; + } + + free(opts->input_charset); + free(opts); +} + +int iso_read_opts_set_start_block(IsoReadOpts *opts, uint32_t block) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->block = block; + return ISO_SUCCESS; +} + +int iso_read_opts_set_no_rockridge(IsoReadOpts *opts, int norr) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->norock = norr ? 1 :0; + return ISO_SUCCESS; +} + +int iso_read_opts_set_no_joliet(IsoReadOpts *opts, int nojoliet) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->nojoliet = nojoliet ? 1 :0; + return ISO_SUCCESS; +} + +int iso_read_opts_set_no_iso1999(IsoReadOpts *opts, int noiso1999) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->noiso1999 = noiso1999 ? 1 :0; + return ISO_SUCCESS; +} + +int iso_read_opts_set_preferjoliet(IsoReadOpts *opts, int preferjoliet) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->preferjoliet = preferjoliet ? 1 :0; + return ISO_SUCCESS; +} + +int iso_read_opts_set_default_uid(IsoReadOpts *opts, uid_t uid) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->uid = uid; + return ISO_SUCCESS; +} + +int iso_read_opts_set_default_gid(IsoReadOpts *opts, gid_t gid) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->gid = gid; + return ISO_SUCCESS; +} + +int iso_read_opts_set_default_permissions(IsoReadOpts *opts, mode_t file_perm, + mode_t dir_perm) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->file_mode = file_perm; + opts->dir_mode = dir_perm; + return ISO_SUCCESS; +} + +int iso_read_opts_set_input_charset(IsoReadOpts *opts, const char *charset) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->input_charset = charset ? strdup(charset) : NULL; + return ISO_SUCCESS; +} + +/** + * Destroy an IsoReadImageFeatures object obtained with iso_image_import. + */ +void iso_read_image_features_destroy(IsoReadImageFeatures *f) +{ + if (f) { + free(f); + } +} + +/** + * Get the size (in 2048 byte block) of the image, as reported in the PVM. + */ +uint32_t iso_read_image_features_get_size(IsoReadImageFeatures *f) +{ + return f->size; +} + +/** + * Whether RockRidge extensions are present in the image imported. + */ +int iso_read_image_features_has_rockridge(IsoReadImageFeatures *f) +{ + return f->hasRR; +} + +/** + * Whether Joliet extensions are present in the image imported. + */ +int iso_read_image_features_has_joliet(IsoReadImageFeatures *f) +{ + return f->hasJoliet; +} + +/** + * Whether the image is recorded according to ISO 9660:1999, i.e. it has + * a version 2 Enhanced Volume Descriptor. + */ +int iso_read_image_features_has_iso1999(IsoReadImageFeatures *f) +{ + return f->hasIso1999; +} + +/** + * Whether El-Torito boot record is present present in the image imported. + */ +int iso_read_image_features_has_eltorito(IsoReadImageFeatures *f) +{ + return f->hasElTorito; +} diff --git a/libisofs/branches/thomas/libisofs/fs_local.c b/libisofs/branches/thomas/libisofs/fs_local.c new file mode 100644 index 00000000..e9a28b73 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/fs_local.c @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/* + * Filesystem/FileSource implementation to access the local filesystem. + */ + +#include "fsource.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static +int iso_file_source_new_lfs(IsoFileSource *parent, const char *name, + IsoFileSource **src); + +/* + * We can share a local filesystem object, as it has no private atts. + */ +IsoFilesystem *lfs= NULL; + +typedef struct +{ + /** reference to the parent (if root it points to itself) */ + IsoFileSource *parent; + char *name; + unsigned int openned :2; /* 0: not openned, 1: file, 2:dir */ + union + { + int fd; + DIR *dir; + } info; +} _LocalFsFileSource; + +static +char* lfs_get_path(IsoFileSource *src) +{ + _LocalFsFileSource *data; + data = src->data; + + if (data->parent == src) { + return strdup("/"); + } else { + char *path = lfs_get_path(data->parent); + int pathlen = strlen(path); + path = realloc(path, pathlen + strlen(data->name) + 2); + if (pathlen != 1) { + /* pathlen can only be 1 for root */ + path[pathlen] = '/'; + path[pathlen + 1] = '\0'; + } + return strcat(path, data->name); + } +} + +static +char* lfs_get_name(IsoFileSource *src) +{ + _LocalFsFileSource *data; + data = src->data; + return strdup(data->name); +} + +static +int lfs_lstat(IsoFileSource *src, struct stat *info) +{ + _LocalFsFileSource *data; + char *path; + + if (src == NULL || info == NULL) { + return ISO_NULL_POINTER; + } + data = src->data; + path = lfs_get_path(src); + + if (lstat(path, info) != 0) { + int err; + + /* error, choose an appropriate return code */ + switch (errno) { + case EACCES: + err = ISO_FILE_ACCESS_DENIED; + break; + case ENOTDIR: + case ENAMETOOLONG: + case ELOOP: + err = ISO_FILE_BAD_PATH; + break; + case ENOENT: + err = ISO_FILE_DOESNT_EXIST; + break; + case EFAULT: + case ENOMEM: + err = ISO_OUT_OF_MEM; + break; + default: + err = ISO_FILE_ERROR; + break; + } + return err; + } + free(path); + return ISO_SUCCESS; +} + +static +int lfs_stat(IsoFileSource *src, struct stat *info) +{ + _LocalFsFileSource *data; + char *path; + + if (src == NULL || info == NULL) { + return ISO_NULL_POINTER; + } + data = src->data; + path = lfs_get_path(src); + + if (stat(path, info) != 0) { + int err; + + /* error, choose an appropriate return code */ + switch (errno) { + case EACCES: + err = ISO_FILE_ACCESS_DENIED; + break; + case ENOTDIR: + case ENAMETOOLONG: + case ELOOP: + err = ISO_FILE_BAD_PATH; + break; + case ENOENT: + err = ISO_FILE_DOESNT_EXIST; + break; + case EFAULT: + case ENOMEM: + err = ISO_OUT_OF_MEM; + break; + default: + err = ISO_FILE_ERROR; + break; + } + return err; + } + free(path); + return ISO_SUCCESS; +} + +static +int lfs_access(IsoFileSource *src) +{ + int ret; + _LocalFsFileSource *data; + char *path; + + if (src == NULL) { + return ISO_NULL_POINTER; + } + data = src->data; + path = lfs_get_path(src); + + ret = iso_eaccess(path); + free(path); + return ret; +} + +static +int lfs_open(IsoFileSource *src) +{ + int err; + struct stat info; + _LocalFsFileSource *data; + char *path; + + if (src == NULL) { + return ISO_NULL_POINTER; + } + + data = src->data; + if (data->openned) { + return ISO_FILE_ALREADY_OPENNED; + } + + /* is a file or a dir ? */ + err = lfs_stat(src, &info); + if (err < 0) { + return err; + } + + path = lfs_get_path(src); + if (S_ISDIR(info.st_mode)) { + data->info.dir = opendir(path); + data->openned = data->info.dir ? 2 : 0; + } else { + data->info.fd = open(path, O_RDONLY); + data->openned = data->info.fd != -1 ? 1 : 0; + } + free(path); + + /* + * check for possible errors, note that many of possible ones are + * parsed in the lstat call above + */ + if (data->openned == 0) { + switch (errno) { + case EACCES: + err = ISO_FILE_ACCESS_DENIED; + break; + case EFAULT: + case ENOMEM: + err = ISO_OUT_OF_MEM; + break; + default: + err = ISO_FILE_ERROR; + break; + } + return err; + } + + return ISO_SUCCESS; +} + +static +int lfs_close(IsoFileSource *src) +{ + int ret; + _LocalFsFileSource *data; + + if (src == NULL) { + return ISO_NULL_POINTER; + } + + data = src->data; + switch (data->openned) { + case 1: /* not dir */ + ret = close(data->info.fd) == 0 ? ISO_SUCCESS : ISO_FILE_ERROR; + break; + case 2: /* directory */ + ret = closedir(data->info.dir) == 0 ? ISO_SUCCESS : ISO_FILE_ERROR; + break; + default: + ret = ISO_FILE_NOT_OPENNED; + break; + } + if (ret == ISO_SUCCESS) { + data->openned = 0; + } + return ret; +} + +static +int lfs_read(IsoFileSource *src, void *buf, size_t count) +{ + _LocalFsFileSource *data; + + if (src == NULL || buf == NULL) { + return ISO_NULL_POINTER; + } + if (count == 0) { + return ISO_WRONG_ARG_VALUE; + } + + data = src->data; + switch (data->openned) { + case 1: /* not dir */ + { + int ret; + ret = read(data->info.fd, buf, count); + if (ret < 0) { + /* error on read */ + switch (errno) { + case EINTR: + ret = ISO_INTERRUPTED; + break; + case EFAULT: + ret = ISO_OUT_OF_MEM; + break; + case EIO: + ret = ISO_FILE_READ_ERROR; + break; + default: + ret = ISO_FILE_ERROR; + break; + } + } + return ret; + } + case 2: /* directory */ + return ISO_FILE_IS_DIR; + default: + return ISO_FILE_NOT_OPENNED; + } +} + +static +int lfs_readdir(IsoFileSource *src, IsoFileSource **child) +{ + _LocalFsFileSource *data; + + if (src == NULL || child == NULL) { + return ISO_NULL_POINTER; + } + + data = src->data; + switch (data->openned) { + case 1: /* not dir */ + return ISO_FILE_IS_NOT_DIR; + case 2: /* directory */ + { + struct dirent *entry; + int ret; + + /* while to skip "." and ".." dirs */ + while (1) { + entry = readdir(data->info.dir); + if (entry == NULL) { + if (errno == EBADF) + return ISO_FILE_ERROR; + else + return 0; /* EOF */ + } + if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) { + break; + } + } + + /* create the new FileSrc */ + ret = iso_file_source_new_lfs(src, entry->d_name, child); + return ret; + } + default: + return ISO_FILE_NOT_OPENNED; + } +} + +static +int lfs_readlink(IsoFileSource *src, char *buf, size_t bufsiz) +{ + int size; + _LocalFsFileSource *data; + char *path; + + if (src == NULL || buf == NULL) { + return ISO_NULL_POINTER; + } + + if (bufsiz <= 0) { + return ISO_WRONG_ARG_VALUE; + } + + data = src->data; + path = lfs_get_path(src); + + /* + * invoke readlink, with bufsiz -1 to reserve an space for + * the NULL character + */ + size = readlink(path, buf, bufsiz - 1); + free(path); + if (size < 0) { + /* error */ + switch (errno) { + case EACCES: + return ISO_FILE_ACCESS_DENIED; + case ENOTDIR: + case ENAMETOOLONG: + case ELOOP: + return ISO_FILE_BAD_PATH; + case ENOENT: + return ISO_FILE_DOESNT_EXIST; + case EINVAL: + return ISO_FILE_IS_NOT_SYMLINK; + case EFAULT: + case ENOMEM: + return ISO_OUT_OF_MEM; + default: + return ISO_FILE_ERROR; + } + } + + /* NULL-terminate the buf */ + buf[size] = '\0'; + return ISO_SUCCESS; +} + +static +IsoFilesystem* lfs_get_filesystem(IsoFileSource *src) +{ + return src == NULL ? NULL : lfs; +} + +static +void lfs_free(IsoFileSource *src) +{ + _LocalFsFileSource *data; + + data = src->data; + + /* close the file if it is already openned */ + if (data->openned) { + src->class->close(src); + } + if (data->parent != src) { + iso_file_source_unref(data->parent); + } + free(data->name); + free(data); + iso_filesystem_unref(lfs); +} + +IsoFileSourceIface lfs_class = { + 0, /* version */ + lfs_get_path, + lfs_get_name, + lfs_lstat, + lfs_stat, + lfs_access, + lfs_open, + lfs_close, + lfs_read, + lfs_readdir, + lfs_readlink, + lfs_get_filesystem, + lfs_free +}; + +/** + * + * @return + * 1 success, < 0 error + */ +static +int iso_file_source_new_lfs(IsoFileSource *parent, const char *name, + IsoFileSource **src) +{ + IsoFileSource *lfs_src; + _LocalFsFileSource *data; + + if (src == NULL) { + return ISO_NULL_POINTER; + } + + if (lfs == NULL) { + /* this should never happen */ + return ISO_ASSERT_FAILURE; + } + + /* allocate memory */ + data = malloc(sizeof(_LocalFsFileSource)); + if (data == NULL) { + return ISO_OUT_OF_MEM; + } + lfs_src = malloc(sizeof(IsoFileSource)); + if (lfs_src == NULL) { + free(data); + return ISO_OUT_OF_MEM; + } + + /* fill struct */ + data->name = name ? strdup(name) : NULL; + data->openned = 0; + if (parent) { + data->parent = parent; + iso_file_source_ref(parent); + } else { + data->parent = lfs_src; + } + + lfs_src->refcount = 1; + lfs_src->data = data; + lfs_src->class = &lfs_class; + + /* take a ref to local filesystem */ + iso_filesystem_ref(lfs); + + /* return */ + *src = lfs_src; + return ISO_SUCCESS; +} + +static +int lfs_get_root(IsoFilesystem *fs, IsoFileSource **root) +{ + if (fs == NULL || root == NULL) { + return ISO_NULL_POINTER; + } + return iso_file_source_new_lfs(NULL, NULL, root); +} + +static +int lfs_get_by_path(IsoFilesystem *fs, const char *path, IsoFileSource **file) +{ + int ret; + IsoFileSource *src; + struct stat info; + char *ptr, *brk_info, *component; + + if (fs == NULL || path == NULL || file == NULL) { + return ISO_NULL_POINTER; + } + + /* + * first of all check that it is a valid path. + */ + if (lstat(path, &info) != 0) { + int err; + + /* error, choose an appropriate return code */ + switch (errno) { + case EACCES: + err = ISO_FILE_ACCESS_DENIED; + break; + case ENOTDIR: + case ENAMETOOLONG: + case ELOOP: + err = ISO_FILE_BAD_PATH; + break; + case ENOENT: + err = ISO_FILE_DOESNT_EXIST; + break; + case EFAULT: + case ENOMEM: + err = ISO_OUT_OF_MEM; + break; + default: + err = ISO_FILE_ERROR; + break; + } + return err; + } + + /* ok, path is valid. create the file source */ + ret = lfs_get_root(fs, &src); + if (ret < 0) { + return ret; + } + if (!strcmp(path, "/")) { + /* we are looking for root */ + *file = src; + return ISO_SUCCESS; + } + + ptr = strdup(path); + if (ptr == NULL) { + iso_file_source_unref(src); + return ISO_OUT_OF_MEM; + } + + component = strtok_r(ptr, "/", &brk_info); + while (component) { + IsoFileSource *child = NULL; + if (!strcmp(component, ".")) { + child = src; + } else if (!strcmp(component, "..")) { + child = ((_LocalFsFileSource*)src->data)->parent; + iso_file_source_ref(child); + iso_file_source_unref(src); + } else { + ret = iso_file_source_new_lfs(src, component, &child); + iso_file_source_unref(src); + if (ret < 0) { + break; + } + } + + src = child; + component = strtok_r(NULL, "/", &brk_info); + } + + free(ptr); + if (ret > 0) { + *file = src; + } + return ret; +} + +static +unsigned int lfs_get_id(IsoFilesystem *fs) +{ + return ISO_LOCAL_FS_ID; +} + +static +int lfs_fs_open(IsoFilesystem *fs) +{ + /* open() operation is not needed */ + return ISO_SUCCESS; +} + +static +int lfs_fs_close(IsoFilesystem *fs) +{ + /* close() operation is not needed */ + return ISO_SUCCESS; +} + +static +void lfs_fs_free(IsoFilesystem *fs) +{ + lfs = NULL; +} + +int iso_local_filesystem_new(IsoFilesystem **fs) +{ + if (fs == NULL) { + return ISO_NULL_POINTER; + } + + if (lfs != NULL) { + /* just take a new ref */ + iso_filesystem_ref(lfs); + } else { + + lfs = malloc(sizeof(IsoFilesystem)); + if (lfs == NULL) { + return ISO_OUT_OF_MEM; + } + + /* fill struct */ + strncpy(lfs->type, "file", 4); + lfs->refcount = 1; + lfs->version = 0; + lfs->data = NULL; /* we don't need private data */ + lfs->get_root = lfs_get_root; + lfs->get_by_path = lfs_get_by_path; + lfs->get_id = lfs_get_id; + lfs->open = lfs_fs_open; + lfs->close = lfs_fs_close; + lfs->free = lfs_fs_free; + } + *fs = lfs; + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/fsource.c b/libisofs/branches/thomas/libisofs/fsource.c new file mode 100644 index 00000000..b6e5c5f6 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/fsource.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "fsource.h" +#include + +/** + * Values belong 1000 are reserved for libisofs usage + */ +unsigned int iso_fs_global_id = 1000; + +void iso_file_source_ref(IsoFileSource *src) +{ + ++src->refcount; +} + +void iso_file_source_unref(IsoFileSource *src) +{ + if (--src->refcount == 0) { + src->class->free(src); + free(src); + } +} + +void iso_filesystem_ref(IsoFilesystem *fs) +{ + ++fs->refcount; +} + +void iso_filesystem_unref(IsoFilesystem *fs) +{ + if (--fs->refcount == 0) { + fs->free(fs); + free(fs); + } +} + +/* + * this are just helpers to invoque methods in class + */ + +inline +char* iso_file_source_get_path(IsoFileSource *src) +{ + return src->class->get_path(src); +} + +inline +char* iso_file_source_get_name(IsoFileSource *src) +{ + return src->class->get_name(src); +} + +inline +int iso_file_source_lstat(IsoFileSource *src, struct stat *info) +{ + return src->class->lstat(src, info); +} + +inline +int iso_file_source_access(IsoFileSource *src) +{ + return src->class->access(src); +} + +inline +int iso_file_source_stat(IsoFileSource *src, struct stat *info) +{ + return src->class->stat(src, info); +} + +inline +int iso_file_source_open(IsoFileSource *src) +{ + return src->class->open(src); +} + +inline +int iso_file_source_close(IsoFileSource *src) +{ + return src->class->close(src); +} + +inline +int iso_file_source_read(IsoFileSource *src, void *buf, size_t count) +{ + return src->class->read(src, buf, count); +} + +inline +int iso_file_source_readdir(IsoFileSource *src, IsoFileSource **child) +{ + return src->class->readdir(src, child); +} + +inline +int iso_file_source_readlink(IsoFileSource *src, char *buf, size_t bufsiz) +{ + return src->class->readlink(src, buf, bufsiz); +} + +inline +IsoFilesystem* iso_file_source_get_filesystem(IsoFileSource *src) +{ + return src->class->get_filesystem(src); +} diff --git a/libisofs/branches/thomas/libisofs/fsource.h b/libisofs/branches/thomas/libisofs/fsource.h new file mode 100644 index 00000000..44dda9a4 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/fsource.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#ifndef LIBISO_FSOURCE_H_ +#define LIBISO_FSOURCE_H_ + +/* + * Definitions for the file sources. Most functions/structures related with + * this were moved to libisofs.h. + */ + +#include "libisofs.h" + +#define ISO_LOCAL_FS_ID 1 +#define ISO_IMAGE_FS_ID 2 +#define ISO_ELTORITO_FS_ID 3 +#define ISO_MEM_FS_ID 4 + +/** + * Create a new IsoFilesystem to deal with local filesystem. + * + * @return + * 1 sucess, < 0 error + */ +int iso_local_filesystem_new(IsoFilesystem **fs); + +#endif /*LIBISO_FSOURCE_H_*/ diff --git a/libisofs/branches/thomas/libisofs/image.c b/libisofs/branches/thomas/libisofs/image.c new file mode 100644 index 00000000..4539ac9d --- /dev/null +++ b/libisofs/branches/thomas/libisofs/image.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "libisofs.h" +#include "image.h" +#include "node.h" +#include "messages.h" +#include "eltorito.h" + +#include +#include + +/** + * Create a new image, empty. + * + * The image will be owned by you and should be unref() when no more needed. + * + * @param name + * Name of the image. This will be used as volset_id and volume_id. + * @param image + * Location where the image pointer will be stored. + * @return + * 1 sucess, < 0 error + */ +int iso_image_new(const char *name, IsoImage **image) +{ + int res; + IsoImage *img; + + if (image == NULL) { + return ISO_NULL_POINTER; + } + + img = calloc(1, sizeof(IsoImage)); + if (img == NULL) { + return ISO_OUT_OF_MEM; + } + + /* local filesystem will be used by default */ + res = iso_local_filesystem_new(&(img->fs)); + if (res < 0) { + free(img); + return ISO_OUT_OF_MEM; + } + + /* use basic builder as default */ + res = iso_node_basic_builder_new(&(img->builder)); + if (res < 0) { + iso_filesystem_unref(img->fs); + free(img); + return ISO_OUT_OF_MEM; + } + + /* fill image fields */ + res = iso_node_new_root(&img->root); + if (res < 0) { + iso_node_builder_unref(img->builder); + iso_filesystem_unref(img->fs); + free(img); + return res; + } + img->refcount = 1; + img->id = iso_message_id++; + + if (name != NULL) { + img->volset_id = strdup(name); + img->volume_id = strdup(name); + } + *image = img; + return ISO_SUCCESS; +} + +/** + * Increments the reference counting of the given image. + */ +void iso_image_ref(IsoImage *image) +{ + ++image->refcount; +} + +/** + * Decrements the reference couting of the given image. + * If it reaches 0, the image is free, together with its tree nodes (whether + * their refcount reach 0 too, of course). + */ +void iso_image_unref(IsoImage *image) +{ + if (--image->refcount == 0) { + int nexcl; + + /* we need to free the image */ + if (image->user_data_free != NULL) { + /* free attached data */ + image->user_data_free(image->user_data); + } + + for (nexcl = 0; nexcl < image->nexcludes; ++nexcl) { + free(image->excludes[nexcl]); + } + free(image->excludes); + + iso_node_unref((IsoNode*)image->root); + iso_node_builder_unref(image->builder); + iso_filesystem_unref(image->fs); + el_torito_boot_catalog_free(image->bootcat); + free(image->volset_id); + free(image->volume_id); + free(image->publisher_id); + free(image->data_preparer_id); + free(image->system_id); + free(image->application_id); + free(image->copyright_file_id); + free(image->abstract_file_id); + free(image->biblio_file_id); + free(image); + } +} + +/** + * Attach user defined data to the image. Use this if your application needs + * to store addition info together with the IsoImage. If the image already + * has data attached, the old data will be freed. + * + * @param data + * Pointer to application defined data that will be attached to the + * image. You can pass NULL to remove any already attached data. + * @param give_up + * Function that will be called when the image does not need the data + * any more. It receives the data pointer as an argumente, and eventually + * causes data to be free. It can be NULL if you don't need it. + */ +int iso_image_attach_data(IsoImage *image, void *data, void (*give_up)(void*)) +{ + if (image == NULL || (data != NULL && free == NULL)) { + return ISO_NULL_POINTER; + } + + if (image->user_data != NULL) { + /* free previously attached data */ + if (image->user_data_free) { + image->user_data_free(image->user_data); + } + image->user_data = NULL; + image->user_data_free = NULL; + } + + if (data != NULL) { + image->user_data = data; + image->user_data_free = give_up; + } + return ISO_SUCCESS; +} + +/** + * The the data previously attached with iso_image_attach_data() + */ +void *iso_image_get_attached_data(IsoImage *image) +{ + return image->user_data; +} + +IsoDir *iso_image_get_root(const IsoImage *image) +{ + return image->root; +} + +void iso_image_set_volset_id(IsoImage *image, const char *volset_id) +{ + free(image->volset_id); + image->volset_id = strdup(volset_id); +} + +const char *iso_image_get_volset_id(const IsoImage *image) +{ + return image->volset_id; +} + +void iso_image_set_volume_id(IsoImage *image, const char *volume_id) +{ + free(image->volume_id); + image->volume_id = strdup(volume_id); +} + +const char *iso_image_get_volume_id(const IsoImage *image) +{ + return image->volume_id; +} + +void iso_image_set_publisher_id(IsoImage *image, const char *publisher_id) +{ + free(image->publisher_id); + image->publisher_id = strdup(publisher_id); +} + +const char *iso_image_get_publisher_id(const IsoImage *image) +{ + return image->publisher_id; +} + +void iso_image_set_data_preparer_id(IsoImage *image, + const char *data_preparer_id) +{ + free(image->data_preparer_id); + image->data_preparer_id = strdup(data_preparer_id); +} + +const char *iso_image_get_data_preparer_id(const IsoImage *image) +{ + return image->data_preparer_id; +} + +void iso_image_set_system_id(IsoImage *image, const char *system_id) +{ + free(image->system_id); + image->system_id = strdup(system_id); +} + +const char *iso_image_get_system_id(const IsoImage *image) +{ + return image->system_id; +} + +void iso_image_set_application_id(IsoImage *image, const char *application_id) +{ + free(image->application_id); + image->application_id = strdup(application_id); +} + +const char *iso_image_get_application_id(const IsoImage *image) +{ + return image->application_id; +} + +void iso_image_set_copyright_file_id(IsoImage *image, + const char *copyright_file_id) +{ + free(image->copyright_file_id); + image->copyright_file_id = strdup(copyright_file_id); +} + +const char *iso_image_get_copyright_file_id(const IsoImage *image) +{ + return image->copyright_file_id; +} + +void iso_image_set_abstract_file_id(IsoImage *image, + const char *abstract_file_id) +{ + free(image->abstract_file_id); + image->abstract_file_id = strdup(abstract_file_id); +} + +const char *iso_image_get_abstract_file_id(const IsoImage *image) +{ + return image->abstract_file_id; +} + +void iso_image_set_biblio_file_id(IsoImage *image, const char *biblio_file_id) +{ + free(image->biblio_file_id); + image->biblio_file_id = strdup(biblio_file_id); +} + +const char *iso_image_get_biblio_file_id(const IsoImage *image) +{ + return image->biblio_file_id; +} + +int iso_image_get_msg_id(IsoImage *image) +{ + return image->id; +} diff --git a/libisofs/branches/thomas/libisofs/image.h b/libisofs/branches/thomas/libisofs/image.h new file mode 100644 index 00000000..b86aca1e --- /dev/null +++ b/libisofs/branches/thomas/libisofs/image.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ +#ifndef LIBISO_IMAGE_H_ +#define LIBISO_IMAGE_H_ + +#include "libisofs.h" +#include "node.h" +#include "fsource.h" +#include "builder.h" + +/* + * Image is a context for image manipulation. + * Global objects such as the message_queues must belogn to that + * context. Thus we will have, for example, a msg queue per image, + * so images are completelly independent and can be managed together. + * (Usefull, for example, in Multiple-Document-Interface GUI apps. + * [The stuff we have in init belongs really to image!] + */ + +struct Iso_Image +{ + + int refcount; + + IsoDir *root; + + char *volset_id; + + char *volume_id; /**< Volume identifier. */ + char *publisher_id; /**< Volume publisher. */ + char *data_preparer_id; /**< Volume data preparer. */ + char *system_id; /**< Volume system identifier. */ + char *application_id; /**< Volume application id */ + char *copyright_file_id; + char *abstract_file_id; + char *biblio_file_id; + + /* el-torito boot catalog */ + struct el_torito_boot_catalog *bootcat; + + /* image identifier, for message origin identifier */ + int id; + + /** + * Default filesystem to use when adding files to the image tree. + */ + IsoFilesystem *fs; + + /* + * Default builder to use when adding files to the image tree. + */ + IsoNodeBuilder *builder; + + /** + * Whether to follow symlinks or just add them as symlinks + */ + unsigned int follow_symlinks : 1; + + /** + * Whether to skip hidden files + */ + unsigned int ignore_hidden : 1; + + /** + * Flags that determine what special files should be ignore. It is a + * bitmask: + * bit0: ignore FIFOs + * bit1: ignore Sockets + * bit2: ignore char devices + * bit3: ignore block devices + */ + int ignore_special; + + /** + * Files to exclude. Wildcard support is included. + */ + char** excludes; + int nexcludes; + + /** + * if the dir already contains a node with the same name, whether to + * replace or not the old node with the new. + */ + enum iso_replace_mode replace; + + /* TODO + enum iso_replace_mode (*confirm_replace)(IsoFileSource *src, IsoNode *node); + */ + + /** + * When this is not NULL, it is a pointer to a function that will + * be called just before a file will be added. You can control where + * the file will be in fact added or ignored. + * + * @return + * 1 add, 0 ignore, < 0 cancel + */ + int (*report)(IsoImage *image, IsoFileSource *src); + + /** + * User supplied data + */ + void *user_data; + void (*user_data_free)(void *ptr); +}; + +#endif /*LIBISO_IMAGE_H_*/ diff --git a/libisofs/branches/thomas/libisofs/iso1999.c b/libisofs/branches/thomas/libisofs/iso1999.c new file mode 100644 index 00000000..827d5ad6 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/iso1999.c @@ -0,0 +1,1016 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "iso1999.h" +#include "messages.h" +#include "writer.h" +#include "image.h" +#include "filesrc.h" +#include "eltorito.h" + +#include +#include +#include + +static +int get_iso1999_name(Ecma119Image *t, const char *str, char **fname) +{ + int ret; + char *name; + + if (fname == NULL) { + return ISO_ASSERT_FAILURE; + } + + if (str == NULL) { + /* not an error, can be root node */ + *fname = NULL; + return ISO_SUCCESS; + } + + if (!strcmp(t->input_charset, t->output_charset)) { + /* no conversion needed */ + name = strdup(str); + } else { + ret = strconv(str, t->input_charset, t->output_charset, &name); + if (ret < 0) { + ret = iso_msg_submit(t->image->id, ISO_FILENAME_WRONG_CHARSET, ret, + "Charset conversion error. Can't convert %s from %s to %s", + str, t->input_charset, t->output_charset); + if (ret < 0) { + return ret; /* aborted */ + } + + /* use the original name, it's the best we can do */ + name = strdup(str); + } + } + + /* ISO 9660:1999 7.5.1 */ + if (strlen(name) > 207) { + name[207] = '\0'; + } + + *fname = name; + + return ISO_SUCCESS; +} + +static +void iso1999_node_free(Iso1999Node *node) +{ + if (node == NULL) { + return; + } + if (node->type == ISO1999_DIR) { + int i; + for (i = 0; i < node->info.dir->nchildren; i++) { + iso1999_node_free(node->info.dir->children[i]); + } + free(node->info.dir->children); + free(node->info.dir); + } + iso_node_unref(node->node); + free(node->name); + free(node); +} + +/** + * Create a low level ISO 9660:1999 node + * @return + * 1 success, 0 ignored, < 0 error + */ +static +int create_node(Ecma119Image *t, IsoNode *iso, Iso1999Node **node) +{ + int ret; + Iso1999Node *n; + + n = calloc(1, sizeof(Iso1999Node)); + if (n == NULL) { + return ISO_OUT_OF_MEM; + } + + if (iso->type == LIBISO_DIR) { + IsoDir *dir = (IsoDir*) iso; + n->info.dir = calloc(1, sizeof(struct iso1999_dir_info)); + if (n->info.dir == NULL) { + free(n); + return ISO_OUT_OF_MEM; + } + n->info.dir->children = calloc(sizeof(void*), dir->nchildren); + if (n->info.dir->children == NULL) { + free(n->info.dir); + free(n); + return ISO_OUT_OF_MEM; + } + n->type = ISO1999_DIR; + } else if (iso->type == LIBISO_FILE) { + /* it's a file */ + off_t size; + IsoFileSrc *src; + IsoFile *file = (IsoFile*) iso; + + size = iso_stream_get_size(file->stream); + if (size > (off_t)0xffffffff) { + free(n); + return iso_msg_submit(t->image->id, ISO_FILE_TOO_BIG, 0, + "File \"%s\" can't be added to image because is " + "greater than 4GB", iso->name); + return 0; + } + + ret = iso_file_src_create(t, file, &src); + if (ret < 0) { + free(n); + return ret; + } + n->info.file = src; + n->type = ISO1999_FILE; + } else if (iso->type == LIBISO_BOOT) { + /* it's a el-torito boot catalog, that we write as a file */ + IsoFileSrc *src; + + ret = el_torito_catalog_file_src_create(t, &src); + if (ret < 0) { + free(n); + return ret; + } + n->info.file = src; + n->type = ISO1999_FILE; + } else { + /* should never happen */ + free(n); + return ISO_ASSERT_FAILURE; + } + + /* take a ref to the IsoNode */ + n->node = iso; + iso_node_ref(iso); + + *node = n; + return ISO_SUCCESS; +} + +/** + * Create the low level ISO 9660:1999 tree from the high level ISO tree. + * + * @return + * 1 success, 0 file ignored, < 0 error + */ +static +int create_tree(Ecma119Image *t, IsoNode *iso, Iso1999Node **tree, int pathlen) +{ + int ret, max_path; + Iso1999Node *node = NULL; + char *iso_name = NULL; + + if (t == NULL || iso == NULL || tree == NULL) { + return ISO_NULL_POINTER; + } + + if (iso->hidden & LIBISO_HIDE_ON_1999) { + /* file will be ignored */ + return 0; + } + ret = get_iso1999_name(t, iso->name, &iso_name); + if (ret < 0) { + return ret; + } + + max_path = pathlen + 1 + (iso_name ? strlen(iso_name): 0); + if (!t->allow_longer_paths && max_path > 255) { + free(iso_name); + return iso_msg_submit(t->image->id, ISO_FILE_IMGPATH_WRONG, 0, + "File \"%s\" can't be added to ISO 9660:1999 tree, " + "because its path length is larger than 255", iso->name); + } + + switch (iso->type) { + case LIBISO_FILE: + ret = create_node(t, iso, &node); + break; + case LIBISO_DIR: + { + IsoNode *pos; + IsoDir *dir = (IsoDir*)iso; + ret = create_node(t, iso, &node); + if (ret < 0) { + free(iso_name); + return ret; + } + pos = dir->children; + while (pos) { + int cret; + Iso1999Node *child; + cret = create_tree(t, pos, &child, max_path); + if (cret < 0) { + /* error */ + iso1999_node_free(node); + ret = cret; + break; + } else if (cret == ISO_SUCCESS) { + /* add child to this node */ + int nchildren = node->info.dir->nchildren++; + node->info.dir->children[nchildren] = child; + child->parent = node; + } + pos = pos->next; + } + } + break; + case LIBISO_BOOT: + if (t->eltorito) { + ret = create_node(t, iso, &node); + } else { + /* log and ignore */ + ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, + "El-Torito catalog found on a image without El-Torito.", + iso->name); + } + break; + case LIBISO_SYMLINK: + case LIBISO_SPECIAL: + ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, + "Can't add %s to ISO 9660:1999 tree. This kind of files " + "can only be added to a Rock Ridget tree. Skipping.", + iso->name); + break; + default: + /* should never happen */ + return ISO_ASSERT_FAILURE; + } + if (ret <= 0) { + free(iso_name); + return ret; + } + node->name = iso_name; + *tree = node; + return ISO_SUCCESS; +} + +static int +cmp_node(const void *f1, const void *f2) +{ + Iso1999Node *f = *((Iso1999Node**)f1); + Iso1999Node *g = *((Iso1999Node**)f2); + + /** + * TODO #00027 Follow ISO 9660:1999 specs when sorting files + * strcmp do not does exactly what ISO 9660:1999, 9.3, as characters + * < 0x20 " " are allowed, so name len must be taken into accout + */ + return strcmp(f->name, g->name); +} + +/** + * Sort the entries inside an ISO 9660:1999 directory, according to + * ISO 9660:1999, 9.3 + */ +static +void sort_tree(Iso1999Node *root) +{ + size_t i; + + qsort(root->info.dir->children, root->info.dir->nchildren, + sizeof(void*), cmp_node); + for (i = 0; i < root->info.dir->nchildren; i++) { + Iso1999Node *child = root->info.dir->children[i]; + if (child->type == ISO1999_DIR) + sort_tree(child); + } +} + +static +int mangle_single_dir(Ecma119Image *img, Iso1999Node *dir) +{ + int ret; + int i, nchildren; + Iso1999Node **children; + IsoHTable *table; + int need_sort = 0; + + nchildren = dir->info.dir->nchildren; + children = dir->info.dir->children; + + /* a hash table will temporary hold the names, for fast searching */ + ret = iso_htable_create((nchildren * 100) / 80, iso_str_hash, + (compare_function_t)strcmp, &table); + if (ret < 0) { + return ret; + } + for (i = 0; i < nchildren; ++i) { + char *name = children[i]->name; + ret = iso_htable_add(table, name, name); + if (ret < 0) { + goto mangle_cleanup; + } + } + + for (i = 0; i < nchildren; ++i) { + char *name, *ext; + char full_name[208]; + int max; /* computed max len for name, without extension */ + int j = i; + int digits = 1; /* characters to change per name */ + + /* first, find all child with same name */ + while (j + 1 < nchildren && + !cmp_node(children + i, children + j + 1)) { + ++j; + } + if (j == i) { + /* name is unique */ + continue; + } + + /* + * A max of 7 characters is good enought, it allows handling up to + * 9,999,999 files with same name. + */ + while (digits < 8) { + int ok, k; + char *dot; + int change = 0; /* number to be written */ + + /* copy name to buffer */ + strcpy(full_name, children[i]->name); + + /* compute name and extension */ + dot = strrchr(full_name, '.'); + if (dot != NULL && children[i]->type != ISO1999_DIR) { + + /* + * File (not dir) with extension. + */ + int extlen; + full_name[dot - full_name] = '\0'; + name = full_name; + ext = dot + 1; + + extlen = strlen(ext); + max = 207 - extlen - 1 - digits; + if (max <= 0) { + /* this can happen if extension is too long */ + if (extlen + max > 3) { + /* + * reduce extension len, to give name an extra char + * note that max is negative or 0 + */ + extlen = extlen + max - 1; + ext[extlen] = '\0'; + max = 207 - extlen - 1 - digits; + } else { + /* + * error, we don't support extensions < 3 + * This can't happen with current limit of digits. + */ + ret = ISO_ERROR; + goto mangle_cleanup; + } + } + /* ok, reduce name by digits */ + if (name + max < dot) { + name[max] = '\0'; + } + } else { + /* Directory, or file without extension */ + if (children[i]->type == ISO1999_DIR) { + dot = NULL; /* dots have no meaning in dirs */ + } + max = 207 - digits; + name = full_name; + if (max < strlen(name)) { + name[max] = '\0'; + } + /* let ext be an empty string */ + ext = name + strlen(name); + } + + ok = 1; + /* change name of each file */ + for (k = i; k <= j; ++k) { + char tmp[208]; + char fmt[16]; + if (dot != NULL) { + sprintf(fmt, "%%s%%0%dd.%%s", digits); + } else { + sprintf(fmt, "%%s%%0%dd%%s", digits); + } + while (1) { + sprintf(tmp, fmt, name, change, ext); + ++change; + if (change > int_pow(10, digits)) { + ok = 0; + break; + } + if (!iso_htable_get(table, tmp, NULL)) { + /* the name is unique, so it can be used */ + break; + } + } + if (ok) { + char *new = strdup(tmp); + if (new == NULL) { + ret = ISO_OUT_OF_MEM; + goto mangle_cleanup; + } + iso_msg_debug(img->image->id, "\"%s\" renamed to \"%s\"", + children[k]->name, new); + + iso_htable_remove_ptr(table, children[k]->name, NULL); + free(children[k]->name); + children[k]->name = new; + iso_htable_add(table, new, new); + + /* + * if we change a name we need to sort again children + * at the end + */ + need_sort = 1; + } else { + /* we need to increment digits */ + break; + } + } + if (ok) { + break; + } else { + ++digits; + } + } + if (digits == 8) { + ret = ISO_MANGLE_TOO_MUCH_FILES; + goto mangle_cleanup; + } + i = j; + } + + /* + * If needed, sort again the files inside dir + */ + if (need_sort) { + qsort(children, nchildren, sizeof(void*), cmp_node); + } + + ret = ISO_SUCCESS; + +mangle_cleanup : ; + iso_htable_destroy(table, NULL); + return ret; +} + +static +int mangle_tree(Ecma119Image *t, Iso1999Node *dir) +{ + int ret; + size_t i; + + ret = mangle_single_dir(t, dir); + if (ret < 0) { + return ret; + } + + /* recurse */ + for (i = 0; i < dir->info.dir->nchildren; ++i) { + if (dir->info.dir->children[i]->type == ISO1999_DIR) { + ret = mangle_tree(t, dir->info.dir->children[i]); + if (ret < 0) { + /* error */ + return ret; + } + } + } + return ISO_SUCCESS; +} + +static +int iso1999_tree_create(Ecma119Image *t) +{ + int ret; + Iso1999Node *root; + + if (t == NULL) { + return ISO_NULL_POINTER; + } + + ret = create_tree(t, (IsoNode*)t->image->root, &root, 0); + if (ret <= 0) { + if (ret == 0) { + /* unexpected error, root ignored!! This can't happen */ + ret = ISO_ASSERT_FAILURE; + } + return ret; + } + + /* the ISO 9660:1999 tree is stored in Ecma119Image target */ + t->iso1999_root = root; + + iso_msg_debug(t->image->id, "Sorting the ISO 9660:1999 tree..."); + sort_tree(root); + + iso_msg_debug(t->image->id, "Mangling ISO 9660:1999 names..."); + ret = mangle_tree(t, t->iso1999_root); + if (ret < 0) { + return ret; + } + + return ISO_SUCCESS; +} + +/** + * Compute the size of a directory entry for a single node + */ +static +size_t calc_dirent_len(Ecma119Image *t, Iso1999Node *n) +{ + int ret = n->name ? strlen(n->name) + 33 : 34; + if (ret % 2) + ret++; + return ret; +} + +/** + * Computes the total size of all directory entries of a single dir, as + * stated in ISO 9660:1999, 6.8.1.3 + */ +static +size_t calc_dir_size(Ecma119Image *t, Iso1999Node *dir) +{ + size_t i, len; + + /* size of "." and ".." entries */ + len = 34 + 34; + + for (i = 0; i < dir->info.dir->nchildren; ++i) { + size_t remaining; + Iso1999Node *child = dir->info.dir->children[i]; + size_t dirent_len = calc_dirent_len(t, child); + remaining = BLOCK_SIZE - (len % BLOCK_SIZE); + if (dirent_len > remaining) { + /* child directory entry doesn't fit on block */ + len += remaining + dirent_len; + } else { + len += dirent_len; + } + } + + /* + * The size of a dir is always a multiple of block size, as we must add + * the size of the unused space after the last directory record + * (ISO 9660:1999, 6.8.1.3) + */ + len = ROUND_UP(len, BLOCK_SIZE); + + /* cache the len */ + dir->info.dir->len = len; + return len; +} + +static +void calc_dir_pos(Ecma119Image *t, Iso1999Node *dir) +{ + size_t i, len; + + t->iso1999_ndirs++; + dir->info.dir->block = t->curblock; + len = calc_dir_size(t, dir); + t->curblock += DIV_UP(len, BLOCK_SIZE); + for (i = 0; i < dir->info.dir->nchildren; i++) { + Iso1999Node *child = dir->info.dir->children[i]; + if (child->type == ISO1999_DIR) { + calc_dir_pos(t, child); + } + } +} + +/** + * Compute the length of the path table (ISO 9660:1999, 6.9), in bytes. + */ +static +uint32_t calc_path_table_size(Iso1999Node *dir) +{ + uint32_t size; + size_t i; + + /* size of path table for this entry */ + size = 8; + size += dir->name ? strlen(dir->name) : 2; + size += (size % 2); + + /* and recurse */ + for (i = 0; i < dir->info.dir->nchildren; i++) { + Iso1999Node *child = dir->info.dir->children[i]; + if (child->type == ISO1999_DIR) { + size += calc_path_table_size(child); + } + } + return size; +} + +static +int iso1999_writer_compute_data_blocks(IsoImageWriter *writer) +{ + Ecma119Image *t; + uint32_t path_table_size; + + if (writer == NULL) { + return ISO_OUT_OF_MEM; + } + + t = writer->target; + + /* compute position of directories */ + iso_msg_debug(t->image->id, + "Computing position of ISO 9660:1999 dir structure"); + t->iso1999_ndirs = 0; + calc_dir_pos(t, t->iso1999_root); + + /* compute length of pathlist */ + iso_msg_debug(t->image->id, "Computing length of ISO 9660:1999 pathlist"); + path_table_size = calc_path_table_size(t->iso1999_root); + + /* compute location for path tables */ + t->iso1999_l_path_table_pos = t->curblock; + t->curblock += DIV_UP(path_table_size, BLOCK_SIZE); + t->iso1999_m_path_table_pos = t->curblock; + t->curblock += DIV_UP(path_table_size, BLOCK_SIZE); + t->iso1999_path_table_size = path_table_size; + + return ISO_SUCCESS; +} + +/** + * Write a single directory record (ISO 9660:1999, 9.1). + * + * @param file_id + * if >= 0, we use it instead of the filename (for "." and ".." entries). + * @param len_fi + * Computed length of the file identifier. + */ +static +void write_one_dir_record(Ecma119Image *t, Iso1999Node *node, int file_id, + uint8_t *buf, size_t len_fi) +{ + uint32_t len; + uint32_t block; + uint8_t len_dr; /*< size of dir entry */ + uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id + : (uint8_t*)node->name; + + struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; + + len_dr = 33 + len_fi + (len_fi % 2 ? 0 : 1); + + memcpy(rec->file_id, name, len_fi); + + if (node->type == ISO1999_DIR) { + /* use the cached length */ + len = node->info.dir->len; + block = node->info.dir->block; + } else if (node->type == ISO1999_FILE) { + len = iso_file_src_get_size(node->info.file); + block = node->info.file->block; + } else { + /* + * for nodes other than files and dirs, we set both + * len and block to 0 + */ + len = 0; + block = 0; + } + + /* + * For ".." entry we need to write the parent info! + */ + if (file_id == 1 && node->parent) + node = node->parent; + + rec->len_dr[0] = len_dr; + iso_bb(rec->block, block, 4); + iso_bb(rec->length, len, 4); + iso_datetime_7(rec->recording_time, t->now, t->always_gmt); + rec->flags[0] = (node->type == ISO1999_DIR) ? 2 : 0; + iso_bb(rec->vol_seq_number, 1, 2); + rec->len_fi[0] = len_fi; +} + +/** + * Write the enhanced volume descriptor (ISO/IEC 9660:1999, 8.5) + */ +static +int iso1999_writer_write_vol_desc(IsoImageWriter *writer) +{ + IsoImage *image; + Ecma119Image *t; + + /* The enhanced volume descriptor is like the sup vol desc */ + struct ecma119_sup_vol_desc vol; + + char *vol_id = NULL, *pub_id = NULL, *data_id = NULL; + char *volset_id = NULL, *system_id = NULL, *application_id = NULL; + char *copyright_file_id = NULL, *abstract_file_id = NULL; + char *biblio_file_id = NULL; + + if (writer == NULL) { + return ISO_OUT_OF_MEM; + } + + t = writer->target; + image = t->image; + + iso_msg_debug(image->id, "Write Enhanced Vol Desc (ISO 9660:1999)"); + + memset(&vol, 0, sizeof(struct ecma119_sup_vol_desc)); + + get_iso1999_name(t, image->volume_id, &vol_id); + str2a_char(t->input_charset, image->publisher_id, &pub_id); + str2a_char(t->input_charset, image->data_preparer_id, &data_id); + get_iso1999_name(t, image->volset_id, &volset_id); + + str2a_char(t->input_charset, image->system_id, &system_id); + str2a_char(t->input_charset, image->application_id, &application_id); + get_iso1999_name(t, image->copyright_file_id, ©right_file_id); + get_iso1999_name(t, image->abstract_file_id, &abstract_file_id); + get_iso1999_name(t, image->biblio_file_id, &biblio_file_id); + + vol.vol_desc_type[0] = 2; + memcpy(vol.std_identifier, "CD001", 5); + + /* descriptor version is 2 (ISO/IEC 9660:1999, 8.5.2) */ + vol.vol_desc_version[0] = 2; + strncpy_pad((char*)vol.volume_id, vol_id, 32); + + iso_bb(vol.vol_space_size, t->vol_space_size, 4); + iso_bb(vol.vol_set_size, 1, 2); + iso_bb(vol.vol_seq_number, 1, 2); + iso_bb(vol.block_size, BLOCK_SIZE, 2); + iso_bb(vol.path_table_size, t->iso1999_path_table_size, 4); + iso_lsb(vol.l_path_table_pos, t->iso1999_l_path_table_pos, 4); + iso_msb(vol.m_path_table_pos, t->iso1999_m_path_table_pos, 4); + + write_one_dir_record(t, t->iso1999_root, 0, vol.root_dir_record, 1); + + strncpy_pad((char*)vol.vol_set_id, volset_id, 128); + strncpy_pad((char*)vol.publisher_id, pub_id, 128); + strncpy_pad((char*)vol.data_prep_id, data_id, 128); + + strncpy_pad((char*)vol.system_id, system_id, 32); + + strncpy_pad((char*)vol.application_id, application_id, 128); + strncpy_pad((char*)vol.copyright_file_id, copyright_file_id, 37); + strncpy_pad((char*)vol.abstract_file_id, abstract_file_id, 37); + strncpy_pad((char*)vol.bibliographic_file_id, biblio_file_id, 37); + + iso_datetime_17(vol.vol_creation_time, t->now, t->always_gmt); + iso_datetime_17(vol.vol_modification_time, t->now, t->always_gmt); + iso_datetime_17(vol.vol_effective_time, t->now, t->always_gmt); + vol.file_structure_version[0] = 1; + + free(vol_id); + free(volset_id); + free(pub_id); + free(data_id); + free(system_id); + free(application_id); + free(copyright_file_id); + free(abstract_file_id); + free(biblio_file_id); + + /* Finally write the Volume Descriptor */ + return iso_write(t, &vol, sizeof(struct ecma119_sup_vol_desc)); +} + +static +int write_one_dir(Ecma119Image *t, Iso1999Node *dir) +{ + int ret; + uint8_t buffer[BLOCK_SIZE]; + size_t i; + size_t fi_len, len; + + /* buf will point to current write position on buffer */ + uint8_t *buf = buffer; + + /* initialize buffer with 0s */ + memset(buffer, 0, BLOCK_SIZE); + + /* write the "." and ".." entries first */ + write_one_dir_record(t, dir, 0, buf, 1); + buf += 34; + write_one_dir_record(t, dir, 1, buf, 1); + buf += 34; + + for (i = 0; i < dir->info.dir->nchildren; i++) { + Iso1999Node *child = dir->info.dir->children[i]; + + /* compute len of directory entry */ + fi_len = strlen(child->name); + len = fi_len + 33 + (fi_len % 2 ? 0 : 1); + + if ( (buf + len - buffer) > BLOCK_SIZE) { + /* dir doesn't fit in current block */ + ret = iso_write(t, buffer, BLOCK_SIZE); + if (ret < 0) { + return ret; + } + memset(buffer, 0, BLOCK_SIZE); + buf = buffer; + } + /* write the directory entry in any case */ + write_one_dir_record(t, child, -1, buf, fi_len); + buf += len; + } + + /* write the last block */ + ret = iso_write(t, buffer, BLOCK_SIZE); + return ret; +} + +static +int write_dirs(Ecma119Image *t, Iso1999Node *root) +{ + int ret; + size_t i; + + /* write all directory entries for this dir */ + ret = write_one_dir(t, root); + if (ret < 0) { + return ret; + } + + /* recurse */ + for (i = 0; i < root->info.dir->nchildren; i++) { + Iso1999Node *child = root->info.dir->children[i]; + if (child->type == ISO1999_DIR) { + ret = write_dirs(t, child); + if (ret < 0) { + return ret; + } + } + } + return ISO_SUCCESS; +} + +static +int write_path_table(Ecma119Image *t, Iso1999Node **pathlist, int l_type) +{ + size_t i, len; + uint8_t buf[256]; /* 256 is just a convenient size larger enought */ + struct ecma119_path_table_record *rec; + void (*write_int)(uint8_t*, uint32_t, int); + Iso1999Node *dir; + uint32_t path_table_size; + int parent = 0; + int ret= ISO_SUCCESS; + + path_table_size = 0; + write_int = l_type ? iso_lsb : iso_msb; + + for (i = 0; i < t->iso1999_ndirs; i++) { + dir = pathlist[i]; + + /* find the index of the parent in the table */ + while ((i) && pathlist[parent] != dir->parent) { + parent++; + } + + /* write the Path Table Record (ECMA-119, 9.4) */ + memset(buf, 0, 256); + rec = (struct ecma119_path_table_record*) buf; + rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->name) : 1; + rec->len_xa[0] = 0; + write_int(rec->block, dir->info.dir->block, 4); + write_int(rec->parent, parent + 1, 2); + if (dir->parent) { + memcpy(rec->dir_id, dir->name, rec->len_di[0]); + } + len = 8 + rec->len_di[0] + (rec->len_di[0] % 2); + ret = iso_write(t, buf, len); + if (ret < 0) { + /* error */ + return ret; + } + path_table_size += len; + } + + /* we need to fill the last block with zeros */ + path_table_size %= BLOCK_SIZE; + if (path_table_size) { + uint8_t zeros[BLOCK_SIZE]; + len = BLOCK_SIZE - path_table_size; + memset(zeros, 0, len); + ret = iso_write(t, zeros, len); + } + return ret; +} + +static +int write_path_tables(Ecma119Image *t) +{ + int ret; + size_t i, j, cur; + Iso1999Node **pathlist; + + iso_msg_debug(t->image->id, "Writing ISO 9660:1999 Path tables"); + + /* allocate temporal pathlist */ + pathlist = malloc(sizeof(void*) * t->iso1999_ndirs); + if (pathlist == NULL) { + return ISO_OUT_OF_MEM; + } + pathlist[0] = t->iso1999_root; + cur = 1; + + for (i = 0; i < t->iso1999_ndirs; i++) { + Iso1999Node *dir = pathlist[i]; + for (j = 0; j < dir->info.dir->nchildren; j++) { + Iso1999Node *child = dir->info.dir->children[j]; + if (child->type == ISO1999_DIR) { + pathlist[cur++] = child; + } + } + } + + /* Write L Path Table */ + ret = write_path_table(t, pathlist, 1); + if (ret < 0) { + goto write_path_tables_exit; + } + + /* Write L Path Table */ + ret = write_path_table(t, pathlist, 0); + + write_path_tables_exit: ; + free(pathlist); + return ret; +} + +static +int iso1999_writer_write_data(IsoImageWriter *writer) +{ + int ret; + Ecma119Image *t; + + if (writer == NULL) { + return ISO_NULL_POINTER; + } + t = writer->target; + + /* first of all, we write the directory structure */ + ret = write_dirs(t, t->iso1999_root); + if (ret < 0) { + return ret; + } + + /* and write the path tables */ + ret = write_path_tables(t); + + return ret; +} + +static +int iso1999_writer_free_data(IsoImageWriter *writer) +{ + /* free the ISO 9660:1999 tree */ + Ecma119Image *t = writer->target; + iso1999_node_free(t->iso1999_root); + return ISO_SUCCESS; +} + +int iso1999_writer_create(Ecma119Image *target) +{ + int ret; + IsoImageWriter *writer; + + writer = malloc(sizeof(IsoImageWriter)); + if (writer == NULL) { + return ISO_OUT_OF_MEM; + } + + writer->compute_data_blocks = iso1999_writer_compute_data_blocks; + writer->write_vol_desc = iso1999_writer_write_vol_desc; + writer->write_data = iso1999_writer_write_data; + writer->free_data = iso1999_writer_free_data; + writer->data = NULL; + writer->target = target; + + iso_msg_debug(target->image->id, + "Creating low level ISO 9660:1999 tree..."); + ret = iso1999_tree_create(target); + if (ret < 0) { + return ret; + } + + /* add this writer to image */ + target->writers[target->nwriters++] = writer; + + /* we need the volume descriptor */ + target->curblock++; + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/iso1999.h b/libisofs/branches/thomas/libisofs/iso1999.h new file mode 100644 index 00000000..5c986e7d --- /dev/null +++ b/libisofs/branches/thomas/libisofs/iso1999.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/** + * Structures related to ISO/IEC 9660:1999, that is version 2 of ISO-9660 + * "See doc/devel/cookbook/ISO 9660-1999" and + * ISO/IEC DIS 9660:1999(E) "Information processing. Volume and file structure + * of CD­-ROM for Information Interchange" + * for further details. + */ + +#ifndef LIBISO_ISO1999_H +#define LIBISO_ISO1999_H + +#include "libisofs.h" +#include "ecma119.h" + +enum iso1999_node_type { + ISO1999_FILE, + ISO1999_DIR +}; + +struct iso1999_dir_info { + Iso1999Node **children; + size_t nchildren; + size_t len; + size_t block; +}; + +struct iso1999_node +{ + char *name; /**< Name chosen output charset. */ + + Iso1999Node *parent; + + IsoNode *node; /*< reference to the iso node */ + + enum iso1999_node_type type; + union { + IsoFileSrc *file; + struct iso1999_dir_info *dir; + } info; +}; + +/** + * Create a IsoWriter to deal with ISO 9660:1999 estructures, and add it to + * the given target. + * + * @return + * 1 on success, < 0 on error + */ +int iso1999_writer_create(Ecma119Image *target); + +#endif /* LIBISO_ISO1999_H */ diff --git a/libisofs/branches/thomas/libisofs/joliet.c b/libisofs/branches/thomas/libisofs/joliet.c new file mode 100644 index 00000000..0b86f031 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/joliet.c @@ -0,0 +1,1081 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * Copyright (c) 2007 Mario Danic + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "joliet.h" +#include "messages.h" +#include "writer.h" +#include "image.h" +#include "filesrc.h" +#include "eltorito.h" + +#include +#include +#include + +static +int get_joliet_name(Ecma119Image *t, IsoNode *iso, uint16_t **name) +{ + int ret; + uint16_t *ucs_name; + uint16_t *jname = NULL; + + if (iso->name == NULL) { + /* it is not necessarily an error, it can be the root */ + return ISO_SUCCESS; + } + + ret = str2ucs(t->input_charset, iso->name, &ucs_name); + if (ret < 0) { + iso_msg_debug(t->image->id, "Can't convert %s", iso->name); + return ret; + } + + /* TODO #00022 : support relaxed constraints in joliet filenames */ + if (iso->type == LIBISO_DIR) { + jname = iso_j_dir_id(ucs_name); + } else { + jname = iso_j_file_id(ucs_name); + } + free(ucs_name); + if (jname != NULL) { + *name = jname; + return ISO_SUCCESS; + } else { + /* + * only possible if mem error, as check for empty names is done + * in public tree + */ + return ISO_OUT_OF_MEM; + } +} + +static +void joliet_node_free(JolietNode *node) +{ + if (node == NULL) { + return; + } + if (node->type == JOLIET_DIR) { + int i; + for (i = 0; i < node->info.dir->nchildren; i++) { + joliet_node_free(node->info.dir->children[i]); + } + free(node->info.dir->children); + free(node->info.dir); + } + iso_node_unref(node->node); + free(node->name); + free(node); +} + +/** + * Create a low level Joliet node + * @return + * 1 success, 0 ignored, < 0 error + */ +static +int create_node(Ecma119Image *t, IsoNode *iso, JolietNode **node) +{ + int ret; + JolietNode *joliet; + + joliet = calloc(1, sizeof(JolietNode)); + if (joliet == NULL) { + return ISO_OUT_OF_MEM; + } + + if (iso->type == LIBISO_DIR) { + IsoDir *dir = (IsoDir*) iso; + joliet->info.dir = calloc(1, sizeof(struct joliet_dir_info)); + if (joliet->info.dir == NULL) { + free(joliet); + return ISO_OUT_OF_MEM; + } + joliet->info.dir->children = calloc(sizeof(void*), dir->nchildren); + if (joliet->info.dir->children == NULL) { + free(joliet->info.dir); + free(joliet); + return ISO_OUT_OF_MEM; + } + joliet->type = JOLIET_DIR; + } else if (iso->type == LIBISO_FILE) { + /* it's a file */ + off_t size; + IsoFileSrc *src; + IsoFile *file = (IsoFile*) iso; + + size = iso_stream_get_size(file->stream); + if (size > (off_t)0xffffffff) { + free(joliet); + return iso_msg_submit(t->image->id, ISO_FILE_TOO_BIG, 0, + "File \"%s\" can't be added to image because is " + "greater than 4GB", iso->name); + } + + ret = iso_file_src_create(t, file, &src); + if (ret < 0) { + free(joliet); + return ret; + } + joliet->info.file = src; + joliet->type = JOLIET_FILE; + } else if (iso->type == LIBISO_BOOT) { + /* it's a el-torito boot catalog, that we write as a file */ + IsoFileSrc *src; + + ret = el_torito_catalog_file_src_create(t, &src); + if (ret < 0) { + free(joliet); + return ret; + } + joliet->info.file = src; + joliet->type = JOLIET_FILE; + } else { + /* should never happen */ + free(joliet); + return ISO_ASSERT_FAILURE; + } + + /* take a ref to the IsoNode */ + joliet->node = iso; + iso_node_ref(iso); + + *node = joliet; + return ISO_SUCCESS; +} + +/** + * Create the low level Joliet tree from the high level ISO tree. + * + * @return + * 1 success, 0 file ignored, < 0 error + */ +static +int create_tree(Ecma119Image *t, IsoNode *iso, JolietNode **tree, int pathlen) +{ + int ret, max_path; + JolietNode *node = NULL; + uint16_t *jname = NULL; + + if (t == NULL || iso == NULL || tree == NULL) { + return ISO_NULL_POINTER; + } + + if (iso->hidden & LIBISO_HIDE_ON_JOLIET) { + /* file will be ignored */ + return 0; + } + ret = get_joliet_name(t, iso, &jname); + if (ret < 0) { + return ret; + } + max_path = pathlen + 1 + (jname ? ucslen(jname) * 2 : 0); + if (!t->joliet_longer_paths && max_path > 240) { + free(jname); + /* + * Wow!! Joliet is even more restrictive than plain ISO-9660, + * that allows up to 255 bytes!! + */ + return iso_msg_submit(t->image->id, ISO_FILE_IMGPATH_WRONG, 0, + "File \"%s\" can't be added to Joliet tree, because " + "its path length is larger than 240", iso->name); + } + + switch (iso->type) { + case LIBISO_FILE: + ret = create_node(t, iso, &node); + break; + case LIBISO_DIR: + { + IsoNode *pos; + IsoDir *dir = (IsoDir*)iso; + ret = create_node(t, iso, &node); + if (ret < 0) { + free(jname); + return ret; + } + pos = dir->children; + while (pos) { + int cret; + JolietNode *child; + cret = create_tree(t, pos, &child, max_path); + if (cret < 0) { + /* error */ + joliet_node_free(node); + ret = cret; + break; + } else if (cret == ISO_SUCCESS) { + /* add child to this node */ + int nchildren = node->info.dir->nchildren++; + node->info.dir->children[nchildren] = child; + child->parent = node; + } + pos = pos->next; + } + } + break; + case LIBISO_BOOT: + if (t->eltorito) { + ret = create_node(t, iso, &node); + } else { + /* log and ignore */ + ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, + "El-Torito catalog found on a image without El-Torito.", + iso->name); + } + break; + case LIBISO_SYMLINK: + case LIBISO_SPECIAL: + ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, + "Can't add %s to Joliet tree. This kind of files can only" + " be added to a Rock Ridget tree. Skipping.", iso->name); + break; + default: + /* should never happen */ + return ISO_ASSERT_FAILURE; + } + if (ret <= 0) { + free(jname); + return ret; + } + node->name = jname; + *tree = node; + return ISO_SUCCESS; +} + +static int +cmp_node(const void *f1, const void *f2) +{ + JolietNode *f = *((JolietNode**)f1); + JolietNode *g = *((JolietNode**)f2); + return ucscmp(f->name, g->name); +} + +static +void sort_tree(JolietNode *root) +{ + size_t i; + + qsort(root->info.dir->children, root->info.dir->nchildren, + sizeof(void*), cmp_node); + for (i = 0; i < root->info.dir->nchildren; i++) { + JolietNode *child = root->info.dir->children[i]; + if (child->type == JOLIET_DIR) + sort_tree(child); + } +} + +static +int cmp_node_name(const void *f1, const void *f2) +{ + JolietNode *f = *((JolietNode**)f1); + JolietNode *g = *((JolietNode**)f2); + return ucscmp(f->name, g->name); +} + +static +int joliet_create_mangled_name(uint16_t *dest, uint16_t *src, int digits, + int number, uint16_t *ext) +{ + int ret, pos; + uint16_t *ucsnumber; + char fmt[16]; + char *nstr = alloca(digits + 1); + + sprintf(fmt, "%%0%dd", digits); + sprintf(nstr, fmt, number); + + ret = str2ucs("ASCII", nstr, &ucsnumber); + if (ret < 0) { + return ret; + } + + /* copy name */ + pos = ucslen(src); + ucsncpy(dest, src, pos); + + /* copy number */ + ucsncpy(dest + pos, ucsnumber, digits); + pos += digits; + + if (ext[0] != (uint16_t)0) { + size_t extlen = ucslen(ext); + dest[pos++] = (uint16_t)0x2E00; /* '.' in big endian UCS */ + ucsncpy(dest + pos, ext, extlen); + pos += extlen; + } + dest[pos] = (uint16_t)0; + free(ucsnumber); + return ISO_SUCCESS; +} + +static +int mangle_single_dir(Ecma119Image *t, JolietNode *dir) +{ + int ret; + int i, nchildren; + JolietNode **children; + IsoHTable *table; + int need_sort = 0; + + nchildren = dir->info.dir->nchildren; + children = dir->info.dir->children; + + /* a hash table will temporary hold the names, for fast searching */ + ret = iso_htable_create((nchildren * 100) / 80, iso_str_hash, + (compare_function_t)ucscmp, &table); + if (ret < 0) { + return ret; + } + for (i = 0; i < nchildren; ++i) { + uint16_t *name = children[i]->name; + ret = iso_htable_add(table, name, name); + if (ret < 0) { + goto mangle_cleanup; + } + } + + for (i = 0; i < nchildren; ++i) { + uint16_t *name, *ext; + uint16_t full_name[66]; + int max; /* computed max len for name, without extension */ + int j = i; + int digits = 1; /* characters to change per name */ + + /* first, find all child with same name */ + while (j + 1 < nchildren && + !cmp_node_name(children + i, children + j + 1)) { + ++j; + } + if (j == i) { + /* name is unique */ + continue; + } + + /* + * A max of 7 characters is good enought, it allows handling up to + * 9,999,999 files with same name. + */ + while (digits < 8) { + int ok, k; + uint16_t *dot; + int change = 0; /* number to be written */ + + /* copy name to buffer */ + ucscpy(full_name, children[i]->name); + + /* compute name and extension */ + dot = ucsrchr(full_name, '.'); + if (dot != NULL && children[i]->type != JOLIET_DIR) { + + /* + * File (not dir) with extension + */ + int extlen; + full_name[dot - full_name] = 0; + name = full_name; + ext = dot + 1; + + extlen = ucslen(ext); + max = 65 - extlen - 1 - digits; + if (max <= 0) { + /* this can happen if extension is too long */ + if (extlen + max > 3) { + /* + * reduce extension len, to give name an extra char + * note that max is negative or 0 + */ + extlen = extlen + max - 1; + ext[extlen] = 0; + max = 66 - extlen - 1 - digits; + } else { + /* + * error, we don't support extensions < 3 + * This can't happen with current limit of digits. + */ + ret = ISO_ERROR; + goto mangle_cleanup; + } + } + /* ok, reduce name by digits */ + if (name + max < dot) { + name[max] = 0; + } + } else { + /* Directory, or file without extension */ + if (children[i]->type == JOLIET_DIR) { + max = 65 - digits; + dot = NULL; /* dots have no meaning in dirs */ + } else { + max = 65 - digits; + } + name = full_name; + if (max < ucslen(name)) { + name[max] = 0; + } + /* let ext be an empty string */ + ext = name + ucslen(name); + } + + ok = 1; + /* change name of each file */ + for (k = i; k <= j; ++k) { + uint16_t tmp[66]; + while (1) { + ret = joliet_create_mangled_name(tmp, name, digits, + change, ext); + if (ret < 0) { + goto mangle_cleanup; + } + ++change; + if (change > int_pow(10, digits)) { + ok = 0; + break; + } + if (!iso_htable_get(table, tmp, NULL)) { + /* the name is unique, so it can be used */ + break; + } + } + if (ok) { + uint16_t *new = ucsdup(tmp); + if (new == NULL) { + ret = ISO_OUT_OF_MEM; + goto mangle_cleanup; + } + + iso_htable_remove_ptr(table, children[k]->name, NULL); + free(children[k]->name); + children[k]->name = new; + iso_htable_add(table, new, new); + + /* + * if we change a name we need to sort again children + * at the end + */ + need_sort = 1; + } else { + /* we need to increment digits */ + break; + } + } + if (ok) { + break; + } else { + ++digits; + } + } + if (digits == 8) { + ret = ISO_MANGLE_TOO_MUCH_FILES; + goto mangle_cleanup; + } + i = j; + } + + /* + * If needed, sort again the files inside dir + */ + if (need_sort) { + qsort(children, nchildren, sizeof(void*), cmp_node_name); + } + + ret = ISO_SUCCESS; + +mangle_cleanup : ; + iso_htable_destroy(table, NULL); + return ret; +} + +static +int mangle_tree(Ecma119Image *t, JolietNode *dir) +{ + int ret; + size_t i; + + ret = mangle_single_dir(t, dir); + if (ret < 0) { + return ret; + } + + /* recurse */ + for (i = 0; i < dir->info.dir->nchildren; ++i) { + if (dir->info.dir->children[i]->type == JOLIET_DIR) { + ret = mangle_tree(t, dir->info.dir->children[i]); + if (ret < 0) { + /* error */ + return ret; + } + } + } + return ISO_SUCCESS; +} + +static +int joliet_tree_create(Ecma119Image *t) +{ + int ret; + JolietNode *root; + + if (t == NULL) { + return ISO_NULL_POINTER; + } + + ret = create_tree(t, (IsoNode*)t->image->root, &root, 0); + if (ret <= 0) { + if (ret == 0) { + /* unexpected error, root ignored!! This can't happen */ + ret = ISO_ASSERT_FAILURE; + } + return ret; + } + + /* the Joliet tree is stored in Ecma119Image target */ + t->joliet_root = root; + + iso_msg_debug(t->image->id, "Sorting the Joliet tree..."); + sort_tree(root); + + iso_msg_debug(t->image->id, "Mangling Joliet names..."); + ret = mangle_tree(t, t->joliet_root); + if (ret < 0) { + return ret; + } + + return ISO_SUCCESS; +} + +/** + * Compute the size of a directory entry for a single node + */ +static +size_t calc_dirent_len(Ecma119Image *t, JolietNode *n) +{ + /* note than name len is always even, so we always need the pad byte */ + int ret = n->name ? ucslen(n->name) * 2 + 34 : 34; + if (n->type == JOLIET_FILE && !t->omit_version_numbers) { + /* take into account version numbers */ + ret += 4; + } + return ret; +} + +/** + * Computes the total size of all directory entries of a single joliet dir. + * This is like ECMA-119 6.8.1.1, but taking care that names are stored in + * UCS. + */ +static +size_t calc_dir_size(Ecma119Image *t, JolietNode *dir) +{ + size_t i, len; + + /* size of "." and ".." entries */ + len = 34 + 34; + + for (i = 0; i < dir->info.dir->nchildren; ++i) { + size_t remaining; + JolietNode *child = dir->info.dir->children[i]; + size_t dirent_len = calc_dirent_len(t, child); + remaining = BLOCK_SIZE - (len % BLOCK_SIZE); + if (dirent_len > remaining) { + /* child directory entry doesn't fit on block */ + len += remaining + dirent_len; + } else { + len += dirent_len; + } + } + + /* + * The size of a dir is always a multiple of block size, as we must add + * the size of the unused space after the last directory record + * (ECMA-119, 6.8.1.3) + */ + len = ROUND_UP(len, BLOCK_SIZE); + + /* cache the len */ + dir->info.dir->len = len; + return len; +} + +static +void calc_dir_pos(Ecma119Image *t, JolietNode *dir) +{ + size_t i, len; + + t->joliet_ndirs++; + dir->info.dir->block = t->curblock; + len = calc_dir_size(t, dir); + t->curblock += DIV_UP(len, BLOCK_SIZE); + for (i = 0; i < dir->info.dir->nchildren; i++) { + JolietNode *child = dir->info.dir->children[i]; + if (child->type == JOLIET_DIR) { + calc_dir_pos(t, child); + } + } +} + +/** + * Compute the length of the joliet path table, in bytes. + */ +static +uint32_t calc_path_table_size(JolietNode *dir) +{ + uint32_t size; + size_t i; + + /* size of path table for this entry */ + size = 8; + size += dir->name ? ucslen(dir->name) * 2 : 2; + + /* and recurse */ + for (i = 0; i < dir->info.dir->nchildren; i++) { + JolietNode *child = dir->info.dir->children[i]; + if (child->type == JOLIET_DIR) { + size += calc_path_table_size(child); + } + } + return size; +} + +static +int joliet_writer_compute_data_blocks(IsoImageWriter *writer) +{ + Ecma119Image *t; + uint32_t path_table_size; + + if (writer == NULL) { + return ISO_OUT_OF_MEM; + } + + t = writer->target; + + /* compute position of directories */ + iso_msg_debug(t->image->id, "Computing position of Joliet dir structure"); + t->joliet_ndirs = 0; + calc_dir_pos(t, t->joliet_root); + + /* compute length of pathlist */ + iso_msg_debug(t->image->id, "Computing length of Joliet pathlist"); + path_table_size = calc_path_table_size(t->joliet_root); + + /* compute location for path tables */ + t->joliet_l_path_table_pos = t->curblock; + t->curblock += DIV_UP(path_table_size, BLOCK_SIZE); + t->joliet_m_path_table_pos = t->curblock; + t->curblock += DIV_UP(path_table_size, BLOCK_SIZE); + t->joliet_path_table_size = path_table_size; + + return ISO_SUCCESS; +} + +/** + * Write a single directory record for Joliet. It is like (ECMA-119, 9.1), + * but file identifier is stored in UCS. + * + * @param file_id + * if >= 0, we use it instead of the filename (for "." and ".." entries). + * @param len_fi + * Computed length of the file identifier. Total size of the directory + * entry will be len + 34 (ECMA-119, 9.1.12), as padding is always needed + */ +static +void write_one_dir_record(Ecma119Image *t, JolietNode *node, int file_id, + uint8_t *buf, size_t len_fi) +{ + uint32_t len; + uint32_t block; + uint8_t len_dr; /*< size of dir entry */ + uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id + : (uint8_t*)node->name; + + struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; + + len_dr = 33 + len_fi + (len_fi % 2 ? 0 : 1); + + memcpy(rec->file_id, name, len_fi); + + if (node->type == JOLIET_FILE && !t->omit_version_numbers) { + len_dr += 4; + rec->file_id[len_fi++] = 0; + rec->file_id[len_fi++] = ';'; + rec->file_id[len_fi++] = 0; + rec->file_id[len_fi++] = '1'; + } + + if (node->type == JOLIET_DIR) { + /* use the cached length */ + len = node->info.dir->len; + block = node->info.dir->block; + } else if (node->type == JOLIET_FILE) { + len = iso_file_src_get_size(node->info.file); + block = node->info.file->block; + } else { + /* + * for nodes other than files and dirs, we set both + * len and block to 0 + */ + len = 0; + block = 0; + } + + /* + * For ".." entry we need to write the parent info! + */ + if (file_id == 1 && node->parent) + node = node->parent; + + rec->len_dr[0] = len_dr; + iso_bb(rec->block, block, 4); + iso_bb(rec->length, len, 4); + iso_datetime_7(rec->recording_time, t->now, t->always_gmt); + rec->flags[0] = (node->type == JOLIET_DIR) ? 2 : 0; + iso_bb(rec->vol_seq_number, 1, 2); + rec->len_fi[0] = len_fi; +} + +/** + * Copy up to \p max characters from \p src to \p dest. If \p src has less than + * \p max characters, we pad dest with " " characters. + */ +static +void ucsncpy_pad(uint16_t *dest, const uint16_t *src, size_t max) +{ + char *cdest, *csrc; + size_t len, i; + + cdest = (char*)dest; + csrc = (char*)src; + + if (src != NULL) { + len = MIN(ucslen(src) * 2, max); + } else { + len = 0; + } + + for (i = 0; i < len; ++i) + cdest[i] = csrc[i]; + + for (i = len; i < max; i += 2) { + cdest[i] = '\0'; + cdest[i + 1] = ' '; + } +} + +static +int joliet_writer_write_vol_desc(IsoImageWriter *writer) +{ + IsoImage *image; + Ecma119Image *t; + struct ecma119_sup_vol_desc vol; + + uint16_t *vol_id = NULL, *pub_id = NULL, *data_id = NULL; + uint16_t *volset_id = NULL, *system_id = NULL, *application_id = NULL; + uint16_t *copyright_file_id = NULL, *abstract_file_id = NULL; + uint16_t *biblio_file_id = NULL; + + if (writer == NULL) { + return ISO_OUT_OF_MEM; + } + + t = writer->target; + image = t->image; + + iso_msg_debug(image->id, "Write SVD for Joliet"); + + memset(&vol, 0, sizeof(struct ecma119_sup_vol_desc)); + + str2ucs(t->input_charset, image->volume_id, &vol_id); + str2ucs(t->input_charset, image->publisher_id, &pub_id); + str2ucs(t->input_charset, image->data_preparer_id, &data_id); + str2ucs(t->input_charset, image->volset_id, &volset_id); + + str2ucs(t->input_charset, image->system_id, &system_id); + str2ucs(t->input_charset, image->application_id, &application_id); + str2ucs(t->input_charset, image->copyright_file_id, ©right_file_id); + str2ucs(t->input_charset, image->abstract_file_id, &abstract_file_id); + str2ucs(t->input_charset, image->biblio_file_id, &biblio_file_id); + + vol.vol_desc_type[0] = 2; + memcpy(vol.std_identifier, "CD001", 5); + vol.vol_desc_version[0] = 1; + ucsncpy_pad((uint16_t*)vol.volume_id, vol_id, 32); + + /* make use of UCS-2 Level 3 */ + memcpy(vol.esc_sequences, "%/E", 3); + + iso_bb(vol.vol_space_size, t->vol_space_size, 4); + iso_bb(vol.vol_set_size, 1, 2); + iso_bb(vol.vol_seq_number, 1, 2); + iso_bb(vol.block_size, BLOCK_SIZE, 2); + iso_bb(vol.path_table_size, t->joliet_path_table_size, 4); + iso_lsb(vol.l_path_table_pos, t->joliet_l_path_table_pos, 4); + iso_msb(vol.m_path_table_pos, t->joliet_m_path_table_pos, 4); + + write_one_dir_record(t, t->joliet_root, 0, vol.root_dir_record, 1); + + ucsncpy_pad((uint16_t*)vol.vol_set_id, volset_id, 128); + ucsncpy_pad((uint16_t*)vol.publisher_id, pub_id, 128); + ucsncpy_pad((uint16_t*)vol.data_prep_id, data_id, 128); + + ucsncpy_pad((uint16_t*)vol.system_id, system_id, 32); + + ucsncpy_pad((uint16_t*)vol.application_id, application_id, 128); + ucsncpy_pad((uint16_t*)vol.copyright_file_id, copyright_file_id, 37); + ucsncpy_pad((uint16_t*)vol.abstract_file_id, abstract_file_id, 37); + ucsncpy_pad((uint16_t*)vol.bibliographic_file_id, biblio_file_id, 37); + + iso_datetime_17(vol.vol_creation_time, t->now, t->always_gmt); + iso_datetime_17(vol.vol_modification_time, t->now, t->always_gmt); + iso_datetime_17(vol.vol_effective_time, t->now, t->always_gmt); + vol.file_structure_version[0] = 1; + + free(vol_id); + free(volset_id); + free(pub_id); + free(data_id); + free(system_id); + free(application_id); + free(copyright_file_id); + free(abstract_file_id); + free(biblio_file_id); + + /* Finally write the Volume Descriptor */ + return iso_write(t, &vol, sizeof(struct ecma119_sup_vol_desc)); +} + +static +int write_one_dir(Ecma119Image *t, JolietNode *dir) +{ + int ret; + uint8_t buffer[BLOCK_SIZE]; + size_t i; + size_t fi_len, len; + + /* buf will point to current write position on buffer */ + uint8_t *buf = buffer; + + /* initialize buffer with 0s */ + memset(buffer, 0, BLOCK_SIZE); + + /* write the "." and ".." entries first */ + write_one_dir_record(t, dir, 0, buf, 1); + buf += 34; + write_one_dir_record(t, dir, 1, buf, 1); + buf += 34; + + for (i = 0; i < dir->info.dir->nchildren; i++) { + JolietNode *child = dir->info.dir->children[i]; + + /* compute len of directory entry */ + fi_len = ucslen(child->name) * 2; + len = fi_len + 34; + if (child->type == JOLIET_FILE && !t->omit_version_numbers) { + len += 4; + } + + if ( (buf + len - buffer) > BLOCK_SIZE) { + /* dir doesn't fit in current block */ + ret = iso_write(t, buffer, BLOCK_SIZE); + if (ret < 0) { + return ret; + } + memset(buffer, 0, BLOCK_SIZE); + buf = buffer; + } + /* write the directory entry in any case */ + write_one_dir_record(t, child, -1, buf, fi_len); + buf += len; + } + + /* write the last block */ + ret = iso_write(t, buffer, BLOCK_SIZE); + return ret; +} + +static +int write_dirs(Ecma119Image *t, JolietNode *root) +{ + int ret; + size_t i; + + /* write all directory entries for this dir */ + ret = write_one_dir(t, root); + if (ret < 0) { + return ret; + } + + /* recurse */ + for (i = 0; i < root->info.dir->nchildren; i++) { + JolietNode *child = root->info.dir->children[i]; + if (child->type == JOLIET_DIR) { + ret = write_dirs(t, child); + if (ret < 0) { + return ret; + } + } + } + return ISO_SUCCESS; +} + +static +int write_path_table(Ecma119Image *t, JolietNode **pathlist, int l_type) +{ + size_t i, len; + uint8_t buf[256]; /* 256 is just a convenient size larger enought */ + struct ecma119_path_table_record *rec; + void (*write_int)(uint8_t*, uint32_t, int); + JolietNode *dir; + uint32_t path_table_size; + int parent = 0; + int ret= ISO_SUCCESS; + + path_table_size = 0; + write_int = l_type ? iso_lsb : iso_msb; + + for (i = 0; i < t->joliet_ndirs; i++) { + dir = pathlist[i]; + + /* find the index of the parent in the table */ + while ((i) && pathlist[parent] != dir->parent) { + parent++; + } + + /* write the Path Table Record (ECMA-119, 9.4) */ + memset(buf, 0, 256); + rec = (struct ecma119_path_table_record*) buf; + rec->len_di[0] = dir->parent ? (uint8_t) ucslen(dir->name) * 2 : 1; + rec->len_xa[0] = 0; + write_int(rec->block, dir->info.dir->block, 4); + write_int(rec->parent, parent + 1, 2); + if (dir->parent) { + memcpy(rec->dir_id, dir->name, rec->len_di[0]); + } + len = 8 + rec->len_di[0] + (rec->len_di[0] % 2); + ret = iso_write(t, buf, len); + if (ret < 0) { + /* error */ + return ret; + } + path_table_size += len; + } + + /* we need to fill the last block with zeros */ + path_table_size %= BLOCK_SIZE; + if (path_table_size) { + uint8_t zeros[BLOCK_SIZE]; + len = BLOCK_SIZE - path_table_size; + memset(zeros, 0, len); + ret = iso_write(t, zeros, len); + } + return ret; +} + +static +int write_path_tables(Ecma119Image *t) +{ + int ret; + size_t i, j, cur; + JolietNode **pathlist; + + iso_msg_debug(t->image->id, "Writing Joliet Path tables"); + + /* allocate temporal pathlist */ + pathlist = malloc(sizeof(void*) * t->joliet_ndirs); + if (pathlist == NULL) { + return ISO_OUT_OF_MEM; + } + pathlist[0] = t->joliet_root; + cur = 1; + + for (i = 0; i < t->joliet_ndirs; i++) { + JolietNode *dir = pathlist[i]; + for (j = 0; j < dir->info.dir->nchildren; j++) { + JolietNode *child = dir->info.dir->children[j]; + if (child->type == JOLIET_DIR) { + pathlist[cur++] = child; + } + } + } + + /* Write L Path Table */ + ret = write_path_table(t, pathlist, 1); + if (ret < 0) { + goto write_path_tables_exit; + } + + /* Write L Path Table */ + ret = write_path_table(t, pathlist, 0); + + write_path_tables_exit: ; + free(pathlist); + return ret; +} + +static +int joliet_writer_write_data(IsoImageWriter *writer) +{ + int ret; + Ecma119Image *t; + + if (writer == NULL) { + return ISO_NULL_POINTER; + } + t = writer->target; + + /* first of all, we write the directory structure */ + ret = write_dirs(t, t->joliet_root); + if (ret < 0) { + return ret; + } + + /* and write the path tables */ + ret = write_path_tables(t); + + return ret; +} + +static +int joliet_writer_free_data(IsoImageWriter *writer) +{ + /* free the Joliet tree */ + Ecma119Image *t = writer->target; + joliet_node_free(t->joliet_root); + return ISO_SUCCESS; +} + +int joliet_writer_create(Ecma119Image *target) +{ + int ret; + IsoImageWriter *writer; + + writer = malloc(sizeof(IsoImageWriter)); + if (writer == NULL) { + return ISO_OUT_OF_MEM; + } + + writer->compute_data_blocks = joliet_writer_compute_data_blocks; + writer->write_vol_desc = joliet_writer_write_vol_desc; + writer->write_data = joliet_writer_write_data; + writer->free_data = joliet_writer_free_data; + writer->data = NULL; + writer->target = target; + + iso_msg_debug(target->image->id, "Creating low level Joliet tree..."); + ret = joliet_tree_create(target); + if (ret < 0) { + return ret; + } + + /* add this writer to image */ + target->writers[target->nwriters++] = writer; + + /* we need the volume descriptor */ + target->curblock++; + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/joliet.h b/libisofs/branches/thomas/libisofs/joliet.h new file mode 100644 index 00000000..a2db1890 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/joliet.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * Copyright (c) 2007 Mario Danic + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/** + * Declare Joliet related structures. + */ + +#ifndef LIBISO_JOLIET_H +#define LIBISO_JOLIET_H + +#include "libisofs.h" +#include "ecma119.h" + +enum joliet_node_type { + JOLIET_FILE, + JOLIET_DIR +}; + +struct joliet_dir_info { + JolietNode **children; + size_t nchildren; + size_t len; + size_t block; +}; + +struct joliet_node +{ + uint16_t *name; /**< Name in UCS-2BE. */ + + JolietNode *parent; + + IsoNode *node; /*< reference to the iso node */ + + enum joliet_node_type type; + union { + IsoFileSrc *file; + struct joliet_dir_info *dir; + } info; +}; + +/** + * Create a IsoWriter to deal with Joliet estructures, and add it to the given + * target. + * + * @return + * 1 on success, < 0 on error + */ +int joliet_writer_create(Ecma119Image *target); + +#endif /* LIBISO_JOLIET_H */ diff --git a/libisofs/branches/thomas/libisofs/libiso_msgs.c b/libisofs/branches/thomas/libisofs/libiso_msgs.c new file mode 100644 index 00000000..6a60429d --- /dev/null +++ b/libisofs/branches/thomas/libisofs/libiso_msgs.c @@ -0,0 +1,439 @@ + +/* libiso_msgs (generated from libdax_msgs : Fri Feb 22 19:42:52 CET 2008) + Message handling facility of libisofs. + Copyright (C) 2006 - 2008 Thomas Schmitt , + provided under GPL version 2 +*/ + +#include +#include +#include +#include +#include +#include +#include + +/* Only this single source module is entitled to do this */ +#define LIBISO_MSGS_H_INTERNAL 1 + +/* All participants in the messaging system must do this */ +#include "libiso_msgs.h" + + +/* ----------------------------- libiso_msgs_item ------------------------- */ + + +static int libiso_msgs_item_new(struct libiso_msgs_item **item, + struct libiso_msgs_item *link, int flag) +{ + int ret; + struct libiso_msgs_item *o; + struct timeval tv; + struct timezone tz; + + (*item)= o= + (struct libiso_msgs_item *) malloc(sizeof(struct libiso_msgs_item)); + if(o==NULL) + return(-1); + o->timestamp= 0.0; + ret= gettimeofday(&tv,&tz); + if(ret==0) + o->timestamp= tv.tv_sec+0.000001*tv.tv_usec; + o->process_id= getpid(); + o->origin= -1; + o->severity= LIBISO_MSGS_SEV_ALL; + o->priority= LIBISO_MSGS_PRIO_ZERO; + o->error_code= 0; + o->msg_text= NULL; + o->os_errno= 0; + o->prev= link; + o->next= NULL; + if(link!=NULL) { + if(link->next!=NULL) { + link->next->prev= o; + o->next= link->next; + } + link->next= o; + } + return(1); +} + + +/** Detaches item from its queue and eventually readjusts start, end pointers + of the queue */ +int libiso_msgs_item_unlink(struct libiso_msgs_item *o, + struct libiso_msgs_item **chain_start, + struct libiso_msgs_item **chain_end, int flag) +{ + if(o->prev!=NULL) + o->prev->next= o->next; + if(o->next!=NULL) + o->next->prev= o->prev; + if(chain_start!=NULL) + if(*chain_start == o) + *chain_start= o->next; + if(chain_end!=NULL) + if(*chain_end == o) + *chain_end= o->prev; + o->next= o->prev= NULL; + return(1); +} + + +int libiso_msgs_item_destroy(struct libiso_msgs_item **item, + int flag) +{ + struct libiso_msgs_item *o; + + o= *item; + if(o==NULL) + return(0); + libiso_msgs_item_unlink(o,NULL,NULL,0); + if(o->msg_text!=NULL) + free((char *) o->msg_text); + free((char *) o); + *item= NULL; + return(1); +} + + +int libiso_msgs_item_get_msg(struct libiso_msgs_item *item, + int *error_code, char **msg_text, int *os_errno, + int flag) +{ + *error_code= item->error_code; + *msg_text= item->msg_text; + *os_errno= item->os_errno; + return(1); +} + + +int libiso_msgs_item_get_origin(struct libiso_msgs_item *item, + double *timestamp, pid_t *process_id, int *origin, + int flag) +{ + *timestamp= item->timestamp; + *process_id= item->process_id; + *origin= item->origin; + return(1); +} + + +int libiso_msgs_item_get_rank(struct libiso_msgs_item *item, + int *severity, int *priority, int flag) +{ + *severity= item->severity; + *priority= item->priority; + return(1); +} + + +/* ------------------------------- libiso_msgs ---------------------------- */ + + +int libiso_msgs_new(struct libiso_msgs **m, int flag) +{ + struct libiso_msgs *o; + + (*m)= o= (struct libiso_msgs *) malloc(sizeof(struct libiso_msgs)); + if(o==NULL) + return(-1); + o->refcount= 1; + o->oldest= NULL; + o->youngest= NULL; + o->count= 0; + o->queue_severity= LIBISO_MSGS_SEV_ALL; + o->print_severity= LIBISO_MSGS_SEV_NEVER; + strcpy(o->print_id,"libiso: "); + +#ifndef LIBISO_MSGS_SINGLE_THREADED + pthread_mutex_init(&(o->lock_mutex),NULL); +#endif + + return(1); +} + + +static int libiso_msgs_lock(struct libiso_msgs *m, int flag) +{ + +#ifndef LIBISO_MSGS_SINGLE_THREADED + int ret; + + ret= pthread_mutex_lock(&(m->lock_mutex)); + if(ret!=0) + return(0); +#endif + + return(1); +} + + +static int libiso_msgs_unlock(struct libiso_msgs *m, int flag) +{ + +#ifndef LIBISO_MSGS_SINGLE_THREADED + int ret; + + ret= pthread_mutex_unlock(&(m->lock_mutex)); + if(ret!=0) + return(0); +#endif + + return(1); +} + + +int libiso_msgs_destroy(struct libiso_msgs **m, int flag) +{ + struct libiso_msgs *o; + struct libiso_msgs_item *item, *next_item; + + o= *m; + if(o==NULL) + return(0); + if(o->refcount > 1) { + if(libiso_msgs_lock(*m,0)<=0) + return(-1); + o->refcount--; + libiso_msgs_unlock(*m,0); + *m= NULL; + return(1); + } + +#ifndef LIBISO_MSGS_SINGLE_THREADED + if(pthread_mutex_destroy(&(o->lock_mutex))!=0) { + pthread_mutex_unlock(&(o->lock_mutex)); + pthread_mutex_destroy(&(o->lock_mutex)); + } +#endif + + for(item= o->oldest; item!=NULL; item= next_item) { + next_item= item->next; + libiso_msgs_item_destroy(&item,0); + } + free((char *) o); + *m= NULL; + return(1); +} + + +int libiso_msgs_refer(struct libiso_msgs **pt, struct libiso_msgs *m, int flag) +{ + if(libiso_msgs_lock(m,0)<=0) + return(0); + m->refcount++; + *pt= m; + libiso_msgs_unlock(m,0); + return(1); +} + + +int libiso_msgs_set_severities(struct libiso_msgs *m, int queue_severity, + int print_severity, char *print_id, int flag) +{ + if(libiso_msgs_lock(m,0)<=0) + return(0); + m->queue_severity= queue_severity; + m->print_severity= print_severity; + strncpy(m->print_id,print_id,80); + m->print_id[80]= 0; + libiso_msgs_unlock(m,0); + return(1); +} + + +int libiso_msgs__text_to_sev(char *severity_name, int *severity, + int flag) +{ + if(strncmp(severity_name,"NEVER",5)==0) + *severity= LIBISO_MSGS_SEV_NEVER; + else if(strncmp(severity_name,"ABORT",5)==0) + *severity= LIBISO_MSGS_SEV_ABORT; + else if(strncmp(severity_name,"FATAL",5)==0) + *severity= LIBISO_MSGS_SEV_FATAL; + else if(strncmp(severity_name,"FAILURE",7)==0) + *severity= LIBISO_MSGS_SEV_FAILURE; + else if(strncmp(severity_name,"MISHAP",6)==0) + *severity= LIBISO_MSGS_SEV_MISHAP; + else if(strncmp(severity_name,"SORRY",5)==0) + *severity= LIBISO_MSGS_SEV_SORRY; + else if(strncmp(severity_name,"WARNING",7)==0) + *severity= LIBISO_MSGS_SEV_WARNING; + else if(strncmp(severity_name,"HINT",4)==0) + *severity= LIBISO_MSGS_SEV_HINT; + else if(strncmp(severity_name,"NOTE",4)==0) + *severity= LIBISO_MSGS_SEV_NOTE; + else if(strncmp(severity_name,"UPDATE",6)==0) + *severity= LIBISO_MSGS_SEV_UPDATE; + else if(strncmp(severity_name,"DEBUG",5)==0) + *severity= LIBISO_MSGS_SEV_DEBUG; + else if(strncmp(severity_name,"ERRFILE",7)==0) + *severity= LIBISO_MSGS_SEV_ERRFILE; + else if(strncmp(severity_name,"ALL",3)==0) + *severity= LIBISO_MSGS_SEV_ALL; + else { + *severity= LIBISO_MSGS_SEV_ALL; + return(0); + } + return(1); +} + + +int libiso_msgs__sev_to_text(int severity, char **severity_name, + int flag) +{ + if(flag&1) { + *severity_name= "NEVER\nABORT\nFATAL\nFAILURE\nMISHAP\nSORRY\nWARNING\nHINT\nNOTE\nUPDATE\nDEBUG\nERRFILE\nALL"; + return(1); + } + *severity_name= ""; + if(severity>=LIBISO_MSGS_SEV_NEVER) + *severity_name= "NEVER"; + else if(severity>=LIBISO_MSGS_SEV_ABORT) + *severity_name= "ABORT"; + else if(severity>=LIBISO_MSGS_SEV_FATAL) + *severity_name= "FATAL"; + else if(severity>=LIBISO_MSGS_SEV_FAILURE) + *severity_name= "FAILURE"; + else if(severity>=LIBISO_MSGS_SEV_MISHAP) + *severity_name= "MISHAP"; + else if(severity>=LIBISO_MSGS_SEV_SORRY) + *severity_name= "SORRY"; + else if(severity>=LIBISO_MSGS_SEV_WARNING) + *severity_name= "WARNING"; + else if(severity>=LIBISO_MSGS_SEV_HINT) + *severity_name= "HINT"; + else if(severity>=LIBISO_MSGS_SEV_NOTE) + *severity_name= "NOTE"; + else if(severity>=LIBISO_MSGS_SEV_UPDATE) + *severity_name= "UPDATE"; + else if(severity>=LIBISO_MSGS_SEV_DEBUG) + *severity_name= "DEBUG"; + else if(severity>=LIBISO_MSGS_SEV_ERRFILE) + *severity_name= "ERRFILE"; + else if(severity>=LIBISO_MSGS_SEV_ALL) + *severity_name= "ALL"; + else { + *severity_name= ""; + return(0); + } + return(1); +} + + +int libiso_msgs_submit(struct libiso_msgs *m, int origin, int error_code, + int severity, int priority, char *msg_text, + int os_errno, int flag) +{ + int ret; + char *textpt,*sev_name,sev_text[81]; + struct libiso_msgs_item *item= NULL; + + if(severity >= m->print_severity) { + if(msg_text==NULL) + textpt= ""; + else + textpt= msg_text; + sev_text[0]= 0; + ret= libiso_msgs__sev_to_text(severity,&sev_name,0); + if(ret>0) + sprintf(sev_text,"%s : ",sev_name); + + fprintf(stderr,"%s%s%s\n",m->print_id,sev_text,textpt); + if(os_errno!=0) { + ret= libiso_msgs_lock(m,0); + if(ret<=0) + return(-1); + fprintf(stderr,"%s( Most recent system error: %d '%s' )\n", + m->print_id,os_errno,strerror(os_errno)); + libiso_msgs_unlock(m,0); + } + + } + if(severity < m->queue_severity) + return(0); + + ret= libiso_msgs_lock(m,0); + if(ret<=0) + return(-1); + ret= libiso_msgs_item_new(&item,m->youngest,0); + if(ret<=0) + goto failed; + item->origin= origin; + item->error_code= error_code; + item->severity= severity; + item->priority= priority; + if(msg_text!=NULL) { + item->msg_text= malloc(strlen(msg_text)+1); + if(item->msg_text==NULL) + goto failed; + strcpy(item->msg_text,msg_text); + } + item->os_errno= os_errno; + if(m->oldest==NULL) + m->oldest= item; + m->youngest= item; + m->count++; + libiso_msgs_unlock(m,0); + +/* +fprintf(stderr,"libiso_experimental: message submitted to queue (now %d)\n", + m->count); +*/ + + return(1); +failed:; + libiso_msgs_item_destroy(&item,0); + libiso_msgs_unlock(m,0); + return(-1); +} + + +int libiso_msgs_obtain(struct libiso_msgs *m, struct libiso_msgs_item **item, + int severity, int priority, int flag) +{ + int ret; + struct libiso_msgs_item *im, *next_im= NULL; + + *item= NULL; + ret= libiso_msgs_lock(m,0); + if(ret<=0) + return(-1); + for(im= m->oldest; im!=NULL; im= next_im) { + for(; im!=NULL; im= next_im) { + next_im= im->next; + if(im->severity>=severity) + break; + libiso_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0); + libiso_msgs_item_destroy(&im,0); /* severity too low: delete */ + } + if(im==NULL) + break; + if(im->priority>=priority) + break; + } + if(im==NULL) + {ret= 0; goto ex;} + libiso_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0); + *item= im; + ret= 1; +ex:; + libiso_msgs_unlock(m,0); + return(ret); +} + + +int libiso_msgs_destroy_item(struct libiso_msgs *m, + struct libiso_msgs_item **item, int flag) +{ + int ret; + + ret= libiso_msgs_lock(m,0); + if(ret<=0) + return(-1); + ret= libiso_msgs_item_destroy(item,0); + libiso_msgs_unlock(m,0); + return(ret); +} + diff --git a/libisofs/branches/thomas/libisofs/libiso_msgs.h b/libisofs/branches/thomas/libisofs/libiso_msgs.h new file mode 100644 index 00000000..17e8f9e5 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/libiso_msgs.h @@ -0,0 +1,682 @@ + +/* libiso_msgs (generated from libdax_msgs : Fri Feb 22 19:42:52 CET 2008) + Message handling facility of libisofs. + Copyright (C) 2006-2008 Thomas Schmitt , + provided under GPL version 2 +*/ + + +/* + *Never* set this macro outside libiso_msgs.c ! + The entrails of the message handling facility are not to be seen by + the other library components or the applications. +*/ +#ifdef LIBISO_MSGS_H_INTERNAL + + +#ifndef LIBISO_MSGS_SINGLE_THREADED +#include +#endif + + +struct libiso_msgs_item { + + double timestamp; + pid_t process_id; + int origin; + + int severity; + int priority; + + /* Apply for your developer's error code range at + libburn-hackers@pykix.org + Report introduced codes in the list below. */ + int error_code; + + char *msg_text; + int os_errno; + + struct libiso_msgs_item *prev,*next; + +}; + + +struct libiso_msgs { + + int refcount; + + struct libiso_msgs_item *oldest; + struct libiso_msgs_item *youngest; + int count; + + int queue_severity; + int print_severity; + char print_id[81]; + +#ifndef LIBISO_MSGS_SINGLE_THREADED + pthread_mutex_t lock_mutex; +#endif + + +}; + +#endif /* LIBISO_MSGS_H_INTERNAL */ + + +#ifndef LIBISO_MSGS_H_INCLUDED +#define LIBISO_MSGS_H_INCLUDED 1 + + +#ifndef LIBISO_MSGS_H_INTERNAL + + + /* Architectural aspects */ +/* + libdax_msgs is designed to serve in libraries which want to offer their + applications a way to control the output of library messages. It shall be + incorporated by an owner, i.e. a software entity which encloses the code + of the .c file. + + Owner of libdax_msgs is libburn. A fully compatible variant named libiso_msgs + is owned by libisofs and can get generated by a script of the libburn + project: libburn/libiso_msgs_to_xyz_msgs.sh . + + Reason: One cannot link two owners of the same variant together because + both would offer the same functions to the linker. For that situation one + has to create a compatible variant as it is done for libisofs. + + Compatible variants may get plugged together by call combinations like + burn_set_messenger(iso_get_messenger()); + A new variant would demand a _set_messenger() function if it has to work + with libisofs. If only libburn is planned as link partner then a simple + _get_messenger() does suffice. + Take care to shutdown libburn before its provider of the *_msgs object + gets shut down. + +*/ + + /* Public Opaque Handles */ + +/** A pointer to this is a opaque handle to a message handling facility */ +struct libiso_msgs; + +/** A pointer to this is a opaque handle to a single message item */ +struct libiso_msgs_item; + +#endif /* ! LIBISO_MSGS_H_INTERNAL */ + + + /* Public Macros */ + + +/* Registered Severities */ + +/* It is well advisable to let applications select severities via strings and + forwarded functions libiso_msgs__text_to_sev(), libiso_msgs__sev_to_text(). + These macros are for use by the owner of libiso_msgs. +*/ + +/** Use this to get messages of any severity. Do not use for submitting. +*/ +#define LIBISO_MSGS_SEV_ALL 0x00000000 + + +/** Messages of this severity shall transport plain disk file paths + whenever an event of severity SORRY or above is related with an + individual disk file. + No message text shall be added to the file path. The ERRFILE message + shall be issued before the human readable message which carries the + true event severity. That message should contain the file path so it + can be found by strstr(message, path)!=NULL. + The error code shall be the same as with the human readable message. +*/ +#define LIBISO_MSGS_SEV_ERRFILE 0x08000000 + + +/** Debugging messages not to be visible to normal users by default +*/ +#define LIBISO_MSGS_SEV_DEBUG 0x10000000 + +/** Update of a progress report about long running actions +*/ +#define LIBISO_MSGS_SEV_UPDATE 0x20000000 + +/** Not so usual events which were gracefully handled +*/ +#define LIBISO_MSGS_SEV_NOTE 0x30000000 + +/** Possibilities to achieve a better result +*/ +#define LIBISO_MSGS_SEV_HINT 0x40000000 + +/** Warnings about problems which could not be handled optimally +*/ +#define LIBISO_MSGS_SEV_WARNING 0x50000000 + + +/** Non-fatal error messages indicating that parts of an action failed but + processing may go on if one accepts deviations from the desired result. + + SORRY may also be the severity for incidents which are severe enough + for FAILURE but happen within already started irrevocable actions, + like ISO image generation. A precondition for such a severity ease is + that the action can be continued after the incident. + See below MISHAP for what xorriso would need instead of this kind of SORRY + and generates for itself in case of libisofs image generation. + + E.g.: A pattern yields no result. + A speed setting cannot be made. + A libisofs input file is inaccessible during image generation. + + After SORRY a function should try to go on if that makes any sense + and if no threshold prescribes abort on SORRY. The function should + nevertheless indicate some failure in its return value. + It should - but it does not have to. +*/ +#define LIBISO_MSGS_SEV_SORRY 0x60000000 + + +/** A FAILURE (see below) which can be tolerated during long lasting + operations just because they cannot simply be stopped or revoked. + + xorriso converts libisofs SORRY messages issued during image generation + into MISHAP messages in order to allow its evaluators to distinguish + image generation problems from minor image composition problems. + E.g.: + A libisofs input file is inaccessible during image generation. + + After a MISHAP a function should behave like after SORRY. +*/ +#define LIBISO_MSGS_SEV_MISHAP 0x64000000 + + +/** Non-fatal error indicating that an important part of an action failed and + that only a new setup of preconditions will give hope for sufficient + success. + + E.g.: No media is inserted in the output drive. + No write mode can be found for inserted media. + A libisofs input file is inaccessible during grafting. + + After FAILURE a function should end with a return value indicating failure. + It is at the discretion of the function whether it ends immediately in any + case or whether it tries to go on if the eventual threshold allows. +*/ +#define LIBISO_MSGS_SEV_FAILURE 0x68000000 + + +/** An error message which puts the whole operation of the program in question + + E.g.: Not enough memory for essential temporary objects. + Irregular errors from resources. + Programming errors (soft assert). + + After FATAL a function should end very soon with a return value + indicating severe failure. +*/ +#define LIBISO_MSGS_SEV_FATAL 0x70000000 + + +/** A message from an abort handler which will finally finish libburn +*/ +#define LIBISO_MSGS_SEV_ABORT 0x71000000 + +/** A severity to exclude resp. discard any possible message. + Do not use this severity for submitting. +*/ +#define LIBISO_MSGS_SEV_NEVER 0x7fffffff + + +/* Registered Priorities */ + +/* Priorities are to be selected by the programmers and not by the user. */ + +#define LIBISO_MSGS_PRIO_ZERO 0x00000000 +#define LIBISO_MSGS_PRIO_LOW 0x10000000 +#define LIBISO_MSGS_PRIO_MEDIUM 0x20000000 +#define LIBISO_MSGS_PRIO_HIGH 0x30000000 +#define LIBISO_MSGS_PRIO_TOP 0x7ffffffe + +/* Do not use this priority for submitting */ +#define LIBISO_MSGS_PRIO_NEVER 0x7fffffff + + +/* Origin numbers of libburn drives may range from 0 to 1048575 */ +#define LIBISO_MSGS_ORIGIN_DRIVE_BASE 0 +#define LIBISO_MSGS_ORIGIN_DRIVE_TOP 0xfffff + +/* Origin numbers of libisofs images may range from 1048575 to 2097152 */ +#define LIBISO_MSGS_ORIGIN_IMAGE_BASE 0x100000 +#define LIBISO_MSGS_ORIGIN_IMAGE_TOP 0x1fffff + + + + /* Public Functions */ + + /* Calls initiated from inside the direct owner (e.g. from libburn) */ + + +/** Create new empty message handling facility with queue and issue a first + official reference to it. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure +*/ +int libiso_msgs_new(struct libiso_msgs **m, int flag); + + +/** Destroy a message handling facility and all its eventual messages. + The submitted pointer gets set to NULL. + Actually only the last destroy call of all offical references to the object + will really dispose it. All others just decrement the reference counter. + Call this function only with official reference pointers obtained by + libiso_msgs_new() or libiso_msgs_refer(), and only once per such pointer. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 for success, 0 for pointer to NULL, -1 for fatal error +*/ +int libiso_msgs_destroy(struct libiso_msgs **m, int flag); + + +/** Create an official reference to an existing libiso_msgs object. The + references keep the object alive at least until it is released by + a matching number of destroy calls. So each reference MUST be revoked + by exactly one call to libiso_msgs_destroy(). + @param pt The pointer to be set and registered + @param m A pointer to the existing object + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 for success, 0 for failure +*/ +int libiso_msgs_refer(struct libiso_msgs **pt, struct libiso_msgs *o, int flag); + + +/** Submit a message to a message handling facility. + @param origin program specific identification number of the originator of + a message. E.g. drive number. Programs should have an own + range of origin numbers. See above LIBISO_MSGS_ORIGIN_*_BASE + Use -1 if no number is known. + @param error_code Unique error code. Use only registered codes. See below. + The same unique error_code may be issued at different + occasions but those should be equivalent out of the view + of a libiso_msgs application. (E.g. "cannot open ATA drive" + versus "cannot open SCSI drive" would be equivalent.) + @param severity The LIBISO_MSGS_SEV_* of the event. + @param priority The LIBISO_MSGS_PRIO_* number of the event. + @param msg_text Printable and human readable message text. + @param os_errno Eventual error code from operating system (0 if none) + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 on success, 0 on rejection, <0 for severe errors +*/ +int libiso_msgs_submit(struct libiso_msgs *m, int origin, int error_code, + int severity, int priority, char *msg_text, + int os_errno, int flag); + + + + /* Calls from applications (to be forwarded by direct owner) */ + + +/** Convert a registered severity number into a severity name + @param flag Bitfield for control purposes: + bit0= list all severity names in a newline separated string + @return >0 success, <=0 failure +*/ +int libiso_msgs__sev_to_text(int severity, char **severity_name, + int flag); + + +/** Convert a severity name into a severity number, + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure +*/ +int libiso_msgs__text_to_sev(char *severity_name, int *severity, + int flag); + + +/** Set minimum severity for messages to be queued (default + LIBISO_MSGS_SEV_ALL) and for messages to be printed directly to stderr + (default LIBISO_MSGS_SEV_NEVER). + @param print_id A text of at most 80 characters to be printed before + any eventually printed message (default is "libiso: "). + @param flag Bitfield for control purposes (unused yet, submit 0) + @return always 1 for now +*/ +int libiso_msgs_set_severities(struct libiso_msgs *m, int queue_severity, + int print_severity, char *print_id, int flag); + + +/** Obtain a message item that has at least the given severity and priority. + Usually all older messages of lower severity are discarded then. If no + item of sufficient severity was found, all others are discarded from the + queue. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 if a matching item was found, 0 if not, <0 for severe errors +*/ +int libiso_msgs_obtain(struct libiso_msgs *m, struct libiso_msgs_item **item, + int severity, int priority, int flag); + + +/** Destroy a message item obtained by libiso_msgs_obtain(). The submitted + pointer gets set to NULL. + Caution: Copy eventually obtained msg_text before destroying the item, + if you want to use it further. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 for success, 0 for pointer to NULL, <0 for severe errors +*/ +int libiso_msgs_destroy_item(struct libiso_msgs *m, + struct libiso_msgs_item **item, int flag); + + +/** Obtain from a message item the three application oriented components as + submitted with the originating call of libiso_msgs_submit(). + Caution: msg_text becomes a pointer into item, not a copy. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 on success, 0 on invalid item, <0 for servere errors +*/ +int libiso_msgs_item_get_msg(struct libiso_msgs_item *item, + int *error_code, char **msg_text, int *os_errno, + int flag); + + +/** Obtain from a message item the submitter identification submitted + with the originating call of libiso_msgs_submit(). + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 on success, 0 on invalid item, <0 for servere errors +*/ +int libiso_msgs_item_get_origin(struct libiso_msgs_item *item, + double *timestamp, pid_t *process_id, int *origin, + int flag); + + +/** Obtain from a message item severity and priority as submitted + with the originating call of libiso_msgs_submit(). + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 on success, 0 on invalid item, <0 for servere errors +*/ +int libiso_msgs_item_get_rank(struct libiso_msgs_item *item, + int *severity, int *priority, int flag); + + +#ifdef LIDBAX_MSGS_________________ + + + /* Registered Error Codes */ + + +Format: error_code (LIBISO_MSGS_SEV_*,LIBISO_MSGS_PRIO_*) = explanation +If no severity or priority are fixely associated, use "(,)". + +------------------------------------------------------------------------------ +Range "libiso_msgs" : 0x00000000 to 0x0000ffff + + 0x00000000 (ALL,ZERO) = Initial setting in new libiso_msgs_item + 0x00000001 (DEBUG,ZERO) = Test error message + 0x00000002 (DEBUG,ZERO) = Debugging message + 0x00000003 (FATAL,HIGH) = Out of virtual memory + + +------------------------------------------------------------------------------ +Range "elmom" : 0x00010000 to 0x0001ffff + + + +------------------------------------------------------------------------------ +Range "scdbackup" : 0x00020000 to 0x0002ffff + + Acessing and defending drives: + + 0x00020001 (SORRY,LOW) = Cannot open busy device + 0x00020002 (SORRY,HIGH) = Encountered error when closing drive + 0x00020003 (SORRY,HIGH) = Could not grab drive + 0x00020004 (NOTE,HIGH) = Opened O_EXCL scsi sibling + 0x00020005 (SORRY,HIGH) = Failed to open device + 0x00020006 (FATAL,HIGH) = Too many scsi siblings + 0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings + 0x00020008 (SORRY,HIGH) = Device busy. Failed to fcntl-lock + 0x00020009 (SORRY,HIGH) = Neither stdio-path nor its directory exist + + General library operations: + + 0x00020101 (WARNING,HIGH) = Cannot find given worker item + 0x00020102 (SORRY,HIGH) = A drive operation is still going on + 0x00020103 (WARNING,HIGH) = After scan a drive operation is still going on + 0x00020104 (SORRY,HIGH) = NULL pointer caught + 0x00020105 (SORRY,HIGH) = Drive is already released + 0x00020106 (SORRY,HIGH) = Drive is busy on attempt to close + 0x00020107 (WARNING,HIGH) = A drive is still busy on shutdown of library + 0x00020108 (SORRY,HIGH) = Drive is not grabbed on disc status inquiry + 0x00020108 (FATAL,HIGH) = Could not allocate new drive object + 0x00020109 (FATAL,HIGH) = Library not running + 0x0002010a (FATAL,HIGH) = Unsuitable track mode + 0x0002010b (FATAL,HIGH) = Burn run failed + 0x0002010c (FATAL,HIGH) = Failed to transfer command to drive + 0x0002010d (DEBUG,HIGH) = Could not inquire TOC + 0x0002010e (FATAL,HIGH) = Attempt to read ATIP from ungrabbed drive + 0x0002010f (DEBUG,HIGH) = SCSI error condition on command + 0x00020110 (FATAL,HIGH) = Persistent drive address too long + 0x00020111 (FATAL,HIGH) = Could not allocate new auxiliary object + 0x00020112 (SORRY,HIGH) = Bad combination of write_type and block_type + 0x00020113 (FATAL,HIGH) = Drive capabilities not inquired yet + 0x00020114 (SORRY,HIGH) = Attempt to set ISRC with bad data + 0x00020115 (SORRY,HIGH) = Attempt to set track mode to unusable value + 0x00020116 (FATAL,HIGH) = Track mode has unusable value + 0x00020117 (FATAL,HIGH) = toc_entry of drive is already in use + 0x00020118 (DEBUG,HIGH) = Closing track + 0x00020119 (DEBUG,HIGH) = Closing session + 0x0002011a (NOTE,HIGH) = Padding up track to minimum size + 0x0002011b (FATAL,HIGH) = Attempt to read track info from ungrabbed drive + 0x0002011c (FATAL,HIGH) = Attempt to read track info from busy drive + 0x0002011d (FATAL,HIGH) = SCSI error on write + 0x0002011e (SORRY,HIGH) = Unsuitable media detected + 0x0002011f (SORRY,HIGH) = Burning is restricted to a single track + 0x00020120 (NOTE,HIGH) = FORMAT UNIT ignored + 0x00020121 (FATAL,HIGH) = Write preparation setup failed + 0x00020122 (FATAL,HIGH) = SCSI error on format_unit + 0x00020123 (SORRY,HIGH) = DVD Media are unsuitable for desired track type + 0x00020124 (SORRY,HIGH) = SCSI error on set_streaming + 0x00020125 (SORRY,HIGH) = Write start address not supported + 0x00020126 (SORRY,HIGH) = Write start address not properly aligned + 0x00020127 (NOTE,HIGH) = Write start address is ... + 0x00020128 (FATAL,HIGH) = Unsupported inquiry_type with mmc_get_performance + 0x00020129 (SORRY,HIGH) = Will not format media type + 0x0002012a (FATAL,HIGH) = Cannot inquire write mode capabilities + 0x0002012b (FATAL,HIGH) = Drive offers no suitable write mode with this job + 0x0002012c (SORRY,HIGH) = Too many logical tracks recorded + 0x0002012d (FATAL,HIGH) = Exceeding range of permissible write addresses + 0x0002012e (NOTE,HIGH) = Activated track default size + 0x0002012f (SORRY,HIGH) = SAO is restricted to single fixed size session + 0x00020130 (SORRY,HIGH) = Drive and media state unsuitable for blanking + 0x00020131 (SORRY,HIGH) = No suitable formatting type offered by drive + 0x00020132 (SORRY,HIGH) = Selected format is not suitable for libburn + 0x00020133 (SORRY,HIGH) = Cannot mix data and audio in SAO mode + 0x00020134 (NOTE,HIGH) = Defaulted TAO to DAO + 0x00020135 (SORRY,HIGH) = Cannot perform TAO, job unsuitable for DAO + 0x00020136 (SORRY,HIGH) = DAO burning restricted to single fixed size track + 0x00020137 (HINT,HIGH) = TAO would be possible + 0x00020138 (FATAL,HIGH) = Cannot reserve track + 0x00020139 (SORRY,HIGH) = Write job parameters are unsuitable + 0x0002013a (FATAL,HIGH) = No suitable media detected + 0x0002013b (DEBUG,HIGH) = SCSI command indicates host or driver error + 0x0002013c (SORRY,HIGH) = Malformed capabilities page 2Ah received + 0x0002013d (DEBUG,LOW) = Waiting for free buffer space takes long time + 0x0002013e (SORRY,HIGH) = Timeout with waiting for free buffer. Now disabled + 0x0002013f (DEBUG,LOW) = Reporting total time spent with waiting for buffer + 0x00020140 (FATAL,HIGH) = Drive is busy on attempt to write random access + 0x00020141 (SORRY,HIGH) = Write data count not properly aligned + 0x00020142 (FATAL,HIGH) = Drive is not grabbed on random access write + 0x00020143 (SORRY,HIGH) = Read start address not properly aligned + 0x00020144 (SORRY,HIGH) = SCSI error on read + 0x00020145 (FATAL,HIGH) = Drive is busy on attempt to read data + 0x00020146 (FATAL,HIGH) = Drive is a virtual placeholder + 0x00020147 (SORRY,HIGH) = Cannot address start byte + 0x00020148 (SORRY,HIGH) = Cannot write desired amount of data + 0x00020149 (SORRY,HIGH) = Unsuitable filetype for pseudo-drive + 0x0002014a (SORRY,HIGH) = Cannot read desired amount of data + 0x0002014b (SORRY,HIGH) = Drive is already registered resp. scanned + 0x0002014c (FATAL,HIGH) = Emulated drive caught in SCSI function + 0x0002014d (SORRY,HIGH) = Asynchromous SCSI error + 0x0002014f (SORRY,HIGH) = Timeout with asynchromous SCSI command + 0x00020150 (DEBUG,LOW) = Reporting asynchronous waiting time + 0x00020151 (FATAL,HIGH) = Read attempt on write-only drive + 0x00020152 (FATAL,HIGH) = Cannot start fifo thread + 0x00020153 (SORRY,HIGH) = Read error on fifo input + 0x00020154 (NOTE,HIGH) = Forwarded input error ends output + 0x00020155 (SORRY,HIGH) = Desired fifo buffer too large + 0x00020156 (SORRY,HIGH) = Desired fifo buffer too small + 0x00020157 (FATAL,HIGH) = burn_source is not a fifo object + 0x00020158 (DEBUG,LOW) = Reporting thread disposal precautions + 0x00020159 (DEBUG,HIGH) = TOC Format 0 returns inconsistent data + + libiso_audioxtr: + 0x00020200 (SORRY,HIGH) = Cannot open audio source file + 0x00020201 (SORRY,HIGH) = Audio source file has unsuitable format + 0x00020202 (SORRY,HIGH) = Failed to prepare reading of audio data + + + +------------------------------------------------------------------------------ +Range "vreixo" : 0x00030000 to 0x0003ffff + + 0x0003ffff (FAILURE,HIGH) = Operation canceled + 0x0003fffe (FATAL,HIGH) = Unknown or unexpected fatal error + 0x0003fffd (FAILURE,HIGH) = Unknown or unexpected error + 0x0003fffc (FATAL,HIGH) = Internal programming error + 0x0003fffb (FAILURE,HIGH) = NULL pointer where NULL not allowed + 0x0003fffa (FATAL,HIGH) = Memory allocation error + 0x0003fff9 (FATAL,HIGH) = Interrupted by a signal + 0x0003fff8 (FAILURE,HIGH) = Invalid parameter value + 0x0003fff7 (FATAL,HIGH) = Cannot create a needed thread + 0x0003fff6 (FAILURE,HIGH) = Write error + 0x0003fff5 (FAILURE,HIGH) = Buffer read error + 0x0003ffc0 (FAILURE,HIGH) = Trying to add a node already added to another dir + 0x0003ffbf (FAILURE,HIGH) = Node with same name already exist + 0x0003ffbe (FAILURE,HIGH) = Trying to remove a node that was not added to dir + 0x0003ffbd (FAILURE,HIGH) = A requested node does not exist + 0x0003ffbc (FAILURE,HIGH) = Image already bootable + 0x0003ffbb (FAILURE,HIGH) = Trying to use an invalid file as boot image + 0x0003ff80 (FAILURE,HIGH) = Error on file operation + 0x0003ff7f (FAILURE,HIGH) = Trying to open an already openned file + 0x0003ff7e (FAILURE,HIGH) = Access to file is not allowed + 0x0003ff7d (FAILURE,HIGH) = Incorrect path to file + 0x0003ff7c (FAILURE,HIGH) = The file does not exist in the filesystem + 0x0003ff7b (FAILURE,HIGH) = Trying to read or close a file not openned + 0x0003ff7a (FAILURE,HIGH) = Directory used where no dir is expected + 0x0003ff79 (FAILURE,HIGH) = File read error + 0x0003ff78 (FAILURE,HIGH) = Not dir used where a dir is expected + 0x0003ff77 (FAILURE,HIGH) = Not symlink used where a symlink is expected + 0x0003ff76 (FAILURE,HIGH) = Cannot seek to specified location + 0x0003ff75 (HINT,MEDIUM) = File not supported in ECMA-119 tree and ignored + 0x0003ff74 (HINT,MEDIUM) = File bigger than supported by used standard + 0x0003ff73 (MISHAP,HIGH) = File read error during image creation + 0x0003ff72 (HINT,MEDIUM) = Cannot convert filename to requested charset + 0x0003ff71 (SORRY,HIGH) = File cannot be added to the tree + 0x0003ff70 (HINT,MEDIUM) = File path breaks specification constraints + 0x0003ff00 (FAILURE,HIGH) = Charset conversion error + 0x0003feff (FAILURE,HIGH) = Too much files to mangle + 0x0003fec0 (FAILURE,HIGH) = Wrong or damaged Primary Volume Descriptor + 0x0003febf (SORRY,HIGH) = Wrong or damaged RR entry + 0x0003febe (SORRY,HIGH) = Unsupported RR feature + 0x0003febd (FAILURE,HIGH) = Wrong or damaged ECMA-119 + 0x0003febc (FAILURE,HIGH) = Unsupported ECMA-119 feature + 0x0003febb (SORRY,HIGH) = Wrong or damaged El-Torito catalog + 0x0003feba (SORRY,HIGH) = Unsupported El-Torito feature + 0x0003feb9 (SORRY,HIGH) = Cannot patch isolinux boot image + 0x0003feb8 (SORRY,HIGH) = Unsupported SUSP feature + 0x0003feb7 (WARNING,HIGH) = Error on a RR entry that can be ignored + 0x0003feb6 (HINT,MEDIUM) = Error on a RR entry that can be ignored + 0x0003feb5 (WARNING,HIGH) = Multiple ER SUSP entries found + 0x0003feb4 (HINT,MEDIUM) = Unsupported volume descriptor found + 0x0003feb3 (WARNING,HIGH) = El-Torito related warning + 0x0003feb2 (MISHAP,HIGH) = Image write cancelled + 0x0003feb1 (WARNING,HIGH) = El-Torito image is hidden + +Outdated codes which may not be re-used for other purposes than +re-instating them, if ever: + +X 0x00031001 (SORRY,HIGH) = Cannot read file (ignored) +X 0x00031002 (FATAL,HIGH) = Cannot read file (operation canceled) +X 0x00031000 (FATAL,HIGH) = Unsupported ISO-9660 image +X 0x00031001 (HINT,MEDIUM) = Unsupported Vol Desc that will be ignored +X 0x00031002 (FATAL,HIGH) = Damaged ISO-9660 image +X 0x00031003 (SORRY,HIGH) = Cannot read previous image file +X 0x00030101 (HINT,MEDIUM) = Unsupported SUSP entry that will be ignored +X 0x00030102 (SORRY,HIGH) = Wrong/damaged SUSP entry +X 0x00030103 (WARNING,MEDIUM)= Multiple SUSP ER entries where found +X 0x00030111 (SORRY,HIGH) = Unsupported RR feature +X 0x00030112 (SORRY,HIGH) = Error in a Rock Ridge entry +X 0x00030201 (HINT,MEDIUM) = Unsupported Boot Vol Desc that will be ignored +X 0x00030202 (SORRY,HIGH) = Wrong El-Torito catalog +X 0x00030203 (HINT,MEDIUM) = Unsupported El-Torito feature +X 0x00030204 (SORRY,HIGH) = Invalid file to be an El-Torito image +X 0x00030205 (WARNING,MEDIUM)= Cannot properly patch isolinux image +X 0x00030206 (WARNING,MEDIUM)= Copying El-Torito from a previous image without +X enought info about it +X 0x00030301 (NOTE,MEDIUM) = Unsupported file type for Joliet tree + + +------------------------------------------------------------------------------ +Range "application" : 0x00040000 to 0x0004ffff + + 0x00040000 (ABORT,HIGH) : Application supplied message + 0x00040001 (FATAL,HIGH) : Application supplied message + 0x00040002 (SORRY,HIGH) : Application supplied message + 0x00040003 (WARNING,HIGH) : Application supplied message + 0x00040004 (HINT,HIGH) : Application supplied message + 0x00040005 (NOTE,HIGH) : Application supplied message + 0x00040006 (UPDATE,HIGH) : Application supplied message + 0x00040007 (DEBUG,HIGH) : Application supplied message + 0x00040008 (*,HIGH) : Application supplied message + + +------------------------------------------------------------------------------ +Range "libisofs-xorriso" : 0x00050000 to 0x0005ffff + +This is an alternative representation of libisofs.so.6 error codes in xorriso. +If values returned by iso_error_get_code() do not fit into 0x30000 to 0x3ffff +then they get truncated to 16 bit and mapped into this range. +(This should never need to happen, of course.) + +------------------------------------------------------------------------------ +Range "libisoburn" : 0x00060000 to 0x00006ffff + + 0x00060000 (*,*) : Message which shall be attributed to libisoburn + + >>> the messages of libisoburn need to be registered individually + + +------------------------------------------------------------------------------ + +#endif /* LIDBAX_MSGS_________________ */ + + + +#ifdef LIBISO_MSGS_H_INTERNAL + + /* Internal Functions */ + + +/** Lock before doing side effect operations on m */ +static int libiso_msgs_lock(struct libiso_msgs *m, int flag); + +/** Unlock after effect operations on m are done */ +static int libiso_msgs_unlock(struct libiso_msgs *m, int flag); + + +/** Create new empty message item. + @param link Previous item in queue + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure +*/ +static int libiso_msgs_item_new(struct libiso_msgs_item **item, + struct libiso_msgs_item *link, int flag); + +/** Destroy a message item obtained by libiso_msgs_obtain(). The submitted + pointer gets set to NULL. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 for success, 0 for pointer to NULL +*/ +static int libiso_msgs_item_destroy(struct libiso_msgs_item **item, int flag); + + +#endif /* LIBISO_MSGS_H_INTERNAL */ + + +#endif /* ! LIBISO_MSGS_H_INCLUDED */ diff --git a/libisofs/branches/thomas/libisofs/libisofs.h b/libisofs/branches/thomas/libisofs/libisofs.h new file mode 100644 index 00000000..2cbc37fd --- /dev/null +++ b/libisofs/branches/thomas/libisofs/libisofs.h @@ -0,0 +1,3165 @@ +/* + * Copyright (c) 2007-2008 Vreixo Formoso, Mario Danic + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ +#ifndef LIBISO_LIBISOFS_H_ +#define LIBISO_LIBISOFS_H_ + +#include +#include +#include + +struct burn_source; + +/** + * Context for image creation. It holds the files that will be added to image, + * and several options to control libisofs behavior. + * + * @since 0.6.2 + */ +typedef struct Iso_Image IsoImage; + +/* + * A node in the iso tree, i.e. a file that will be written to image. + * + * It can represent any kind of files. When needed, you can get the type with + * iso_node_get_type() and cast it to the appropiate subtype. Useful macros + * are provided, see below. + * + * @since 0.6.2 + */ +typedef struct Iso_Node IsoNode; + +/** + * A directory in the iso tree. It is an special type of IsoNode and can be + * casted to it in any case. + * + * @since 0.6.2 + */ +typedef struct Iso_Dir IsoDir; + +/** + * A symbolic link in the iso tree. It is an special type of IsoNode and can be + * casted to it in any case. + * + * @since 0.6.2 + */ +typedef struct Iso_Symlink IsoSymlink; + +/** + * A regular file in the iso tree. It is an special type of IsoNode and can be + * casted to it in any case. + * + * @since 0.6.2 + */ +typedef struct Iso_File IsoFile; + +/** + * An special file in the iso tree. This is used to represent any POSIX file + * other that regular files, directories or symlinks, i.e.: socket, block and + * character devices, and fifos. + * It is an special type of IsoNode and can be casted to it in any case. + * + * @since 0.6.2 + */ +typedef struct Iso_Special IsoSpecial; + +/** + * The type of an IsoNode. + * + * When an user gets an IsoNode from an image, (s)he can use + * iso_node_get_type() to get the current type of the node, and then + * cast to the appropriate subtype. For example: + * + * ... + * IsoNode *node; + * res = iso_dir_iter_next(iter, &node); + * if (res == 1 && iso_node_get_type(node) == LIBISO_DIR) { + * IsoDir *dir = (IsoDir *)node; + * ... + * } + * + * @since 0.6.2 + */ +enum IsoNodeType { + LIBISO_DIR, + LIBISO_FILE, + LIBISO_SYMLINK, + LIBISO_SPECIAL, + LIBISO_BOOT +}; + +/* macros to check node type */ +#define ISO_NODE_IS_DIR(n) (iso_node_get_type(n) == LIBISO_DIR) +#define ISO_NODE_IS_FILE(n) (iso_node_get_type(n) == LIBISO_FILE) +#define ISO_NODE_IS_SYMLINK(n) (iso_node_get_type(n) == LIBISO_SYMLINK) +#define ISO_NODE_IS_SPECIAL(n) (iso_node_get_type(n) == LIBISO_SPECIAL) +#define ISO_NODE_IS_BOOTCAT(n) (iso_node_get_type(n) == LIBISO_BOOT) + +/* macros for safe downcasting */ +#define ISO_DIR(n) ((IsoDir*)(ISO_NODE_IS_DIR(n) ? n : NULL)) +#define ISO_FILE(n) ((IsoFile*)(ISO_NODE_IS_FILE(n) ? n : NULL)) +#define ISO_SYMLINK(n) ((IsoSymlink*)(ISO_NODE_IS_SYMLINK(n) ? n : NULL)) +#define ISO_SPECIAL(n) ((IsoSpecial*)(ISO_NODE_IS_SPECIAL(n) ? n : NULL)) + +#define ISO_NODE(n) ((IsoNode*)n) + +/** + * Context for iterate on directory children. + * @see iso_dir_get_children() + * + * @since 0.6.2 + */ +typedef struct Iso_Dir_Iter IsoDirIter; + +/** + * It represents an El-Torito boot image. + * + * @since 0.6.2 + */ +typedef struct el_torito_boot_image ElToritoBootImage; + +/** + * An special type of IsoNode that acts as a placeholder for an El-Torito + * boot catalog. Once written, it will appear as a regular file. + * + * @since 0.6.2 + */ +typedef struct Iso_Boot IsoBoot; + +/** + * Flag used to hide a file in the RR/ISO or Joliet tree. + * + * @see iso_node_set_hidden + * @since 0.6.2 + */ +enum IsoHideNodeFlag { + /** Hide the node in the ECMA-119 / RR tree */ + LIBISO_HIDE_ON_RR = 1 << 0, + /** Hide the node in the Joliet tree, if Joliet extension are enabled */ + LIBISO_HIDE_ON_JOLIET = 1 << 1, + /** Hide the node in the ISO-9660:1999 tree, if that format is enabled */ + LIBISO_HIDE_ON_1999 = 1 << 2 +}; + +/** + * El-Torito bootable image type. + * + * @since 0.6.2 + */ +enum eltorito_boot_media_type { + ELTORITO_FLOPPY_EMUL, + ELTORITO_HARD_DISC_EMUL, + ELTORITO_NO_EMUL +}; + +/** + * Replace mode used when addding a node to a file. + * This controls how libisofs will act when you tried to add to a dir a file + * with the same name that an existing file. + * + * @since 0.6.2 + */ +enum iso_replace_mode { + /** + * Never replace an existing node, and instead fail with + * ISO_NODE_NAME_NOT_UNIQUE. + */ + ISO_REPLACE_NEVER, + /** + * Always replace the old node with the new. + */ + ISO_REPLACE_ALWAYS, + /** + * Replace with the new node if it is the same file type + */ + ISO_REPLACE_IF_SAME_TYPE, + /** + * Replace with the new node if it is the same file type and its ctime + * is newer than the old one. + */ + ISO_REPLACE_IF_SAME_TYPE_AND_NEWER, + /** + * Replace with the new node if its ctime is newer than the old one. + */ + ISO_REPLACE_IF_NEWER + /* + * TODO #00006 define more values + * -if both are dirs, add contents (and what to do with conflicts?) + */ +}; + +/** + * Options for image written. + * @see iso_write_opts_new() + * @since 0.6.2 + */ +typedef struct iso_write_opts IsoWriteOpts; + +/** + * Options for image reading or import. + * @see iso_read_opts_new() + * @since 0.6.2 + */ +typedef struct iso_read_opts IsoReadOpts; + +/** + * Source for image reading. + * + * @see struct iso_data_source + * @since 0.6.2 + */ +typedef struct iso_data_source IsoDataSource; + +/** + * Data source used by libisofs for reading an existing image. + * + * It offers homogeneous read access to arbitrary blocks to different sources + * for images, such as .iso files, CD/DVD drives, etc... + * + * To create a multisession image, libisofs needs a IsoDataSource, that the + * user must provide. The function iso_data_source_new_from_file() constructs + * an IsoDataSource that uses POSIX I/O functions to access data. You can use + * it with regular .iso images, and also with block devices that represent a + * drive. + * + * @since 0.6.2 + */ +struct iso_data_source +{ + + /* reserved for future usage, set to 0 */ + int version; + + /** + * Reference count for the data source. Should be 1 when a new source + * is created. Don't access it directly, but with iso_data_source_ref() + * and iso_data_source_unref() functions. + */ + unsigned int refcount; + + /** + * Opens the given source. You must open() the source before any attempt + * to read data from it. The open is the right place for grabbing the + * underlying resources. + * + * @return + * 1 if success, < 0 on error + */ + int (*open)(IsoDataSource *src); + + /** + * Close a given source, freeing all system resources previously grabbed in + * open(). + * + * @return + * 1 if success, < 0 on error + */ + int (*close)(IsoDataSource *src); + + /** + * Read an arbitrary block (2048 bytes) of data from the source. + * + * @param lba + * Block to be read. + * @param buffer + * Buffer where the data will be written. It should have at least + * 2048 bytes. + * @return + * 1 if success, < 0 on error + */ + int (*read_block)(IsoDataSource *src, uint32_t lba, uint8_t *buffer); + + /** + * Clean up the source specific data. Never call this directly, it is + * automatically called by iso_data_source_unref() when refcount reach + * 0. + */ + void (*free_data)(IsoDataSource *); + + /** Source specific data */ + void *data; +}; + +/** + * Return information for image. This is optionally allocated by libisofs, + * as a way to inform user about the features of an existing image, such as + * extensions present, size, ... + * + * @see iso_image_import() + * @since 0.6.2 + */ +typedef struct iso_read_image_features IsoReadImageFeatures; + +/** + * POSIX abstraction for source files. + * + * @see struct iso_file_source + * @since 0.6.2 + */ +typedef struct iso_file_source IsoFileSource; + +/** + * Abstract for source filesystems. + * + * @see struct iso_filesystem + * @since 0.6.2 + */ +typedef struct iso_filesystem IsoFilesystem; + +/** + * Interface that defines the operations (methods) available for an + * IsoFileSource. + * + * @see struct IsoFileSource_Iface + * @since 0.6.2 + */ +typedef struct IsoFileSource_Iface IsoFileSourceIface; + +/** + * IsoFilesystem implementation to deal with ISO images, and to offer a way to + * access specific information of the image, such as several volume attributes, + * extensions being used, El-Torito artifacts... + * + * @since 0.6.2 + */ +typedef IsoFilesystem IsoImageFilesystem; + +/** + * See IsoFilesystem->get_id() for info about this. + * @since 0.6.2 + */ +extern unsigned int iso_fs_global_id; + +/** + * An IsoFilesystem is a handler for a source of files, or a "filesystem". + * That is defined as a set of files that are organized in a hierarchical + * structure. + * + * A filesystem allows libisofs to access files from several sources in + * an homogeneous way, thus abstracting the underlying operations needed to + * access and read file contents. Note that this doesn't need to be tied + * to the disc filesystem used in the partition being accessed. For example, + * we have an IsoFilesystem implementation to access any mounted filesystem, + * using standard Linux functions. It is also legal, of course, to implement + * an IsoFilesystem to deal with a specific filesystem over raw partitions. + * That is what we do, for example, to access an ISO Image. + * + * Each file inside an IsoFilesystem is represented as an IsoFileSource object, + * that defines POSIX-like interface for accessing files. + * + * @since 0.6.2 + */ +struct iso_filesystem +{ + /** + * Type of filesystem. + * "file" -> local filesystem + * "iso " -> iso image filesystem + */ + char type[4]; + + /* reserved for future usage, set to 0 */ + int version; + + /** + * Get the root of a filesystem. + * + * @return + * 1 on success, < 0 on error + */ + int (*get_root)(IsoFilesystem *fs, IsoFileSource **root); + + /** + * Retrieve a file from its absolute path inside the filesystem. + * + * @return + * 1 success, < 0 error + * Error codes: + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_OUT_OF_MEM + * ISO_FILE_ERROR + * ISO_NULL_POINTER + */ + int (*get_by_path)(IsoFilesystem *fs, const char *path, + IsoFileSource **file); + + /** + * Get filesystem identifier. + * + * If the filesystem is able to generate correct values of the st_dev + * and st_ino fields for the struct stat of each file, this should + * return an unique number, greater than 0. + * + * To get a identifier for your filesystem implementation you should + * use iso_fs_global_id, incrementing it by one each time. + * + * Otherwise, if you can't ensure values in the struct stat are valid, + * this should return 0. + */ + unsigned int (*get_id)(IsoFilesystem *fs); + + /** + * Opens the filesystem for several read operations. Calling this funcion + * is not needed at all, each time that the underlying system resource + * needs to be accessed, it is openned propertly. + * However, if you plan to execute several operations on the filesystem, + * it is a good idea to open it previously, to prevent several open/close + * operations to occur. + * + * @return 1 on success, < 0 on error + */ + int (*open)(IsoFilesystem *fs); + + /** + * Close the filesystem, thus freeing all system resources. You should + * call this function if you have previously open() it. + * Note that you can open()/close() a filesystem several times. + * + * @return 1 on success, < 0 on error + */ + int (*close)(IsoFilesystem *fs); + + /** + * Free implementation specific data. Should never be called by user. + * Use iso_filesystem_unref() instead. + */ + void (*free)(IsoFilesystem *fs); + + /* internal usage, do never access them directly */ + unsigned int refcount; + void *data; +}; + +/** + * Interface definition for an IsoFileSource. Defines the POSIX-like function + * to access files and abstract underlying source. + * + * @since 0.6.2 + */ +struct IsoFileSource_Iface +{ + /* reserved for future usage, set to 0 */ + int version; + + /** + * Get the path, relative to the filesystem this file source belongs to. + * + * @return + * the path of the FileSource inside the filesystem, it should be + * freed when no more needed. + */ + char* (*get_path)(IsoFileSource *src); + + /** + * Get the name of the file, with the dir component of the path. + * + * @return + * the name of the file, it should be freed when no more needed. + */ + char* (*get_name)(IsoFileSource *src); + + /** + * Get information about the file. It is equivalent to lstat(2). + * + * @return + * 1 success, < 0 error + * Error codes: + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_OUT_OF_MEM + * ISO_FILE_ERROR + * ISO_NULL_POINTER + */ + int (*lstat)(IsoFileSource *src, struct stat *info); + + /** + * Get information about the file. If the file is a symlink, the info + * returned refers to the destination. It is equivalent to stat(2). + * + * @return + * 1 success, < 0 error + * Error codes: + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_OUT_OF_MEM + * ISO_FILE_ERROR + * ISO_NULL_POINTER + */ + int (*stat)(IsoFileSource *src, struct stat *info); + + /** + * Check if the process has access to read file contents. Note that this + * is not necessarily related with (l)stat functions. For example, in a + * filesystem implementation to deal with an ISO image, if the user has + * read access to the image it will be able to read all files inside it, + * despite of the particular permission of each file in the RR tree, that + * are what the above functions return. + * + * @return + * 1 if process has read access, < 0 on error + * Error codes: + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_OUT_OF_MEM + * ISO_FILE_ERROR + * ISO_NULL_POINTER + */ + int (*access)(IsoFileSource *src); + + /** + * Opens the source. + * @return 1 on success, < 0 on error + * Error codes: + * ISO_FILE_ALREADY_OPENNED + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_OUT_OF_MEM + * ISO_FILE_ERROR + * ISO_NULL_POINTER + */ + int (*open)(IsoFileSource *src); + + /** + * Close a previuously openned file + * @return 1 on success, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_FILE_NOT_OPENNED + */ + int (*close)(IsoFileSource *src); + + /** + * Attempts to read up to count bytes from the given source into + * the buffer starting at buf. + * + * The file src must be open() before calling this, and close() when no + * more needed. Not valid for dirs. On symlinks it reads the destination + * file. + * + * @return + * number of bytes read, 0 if EOF, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_FILE_NOT_OPENNED + * ISO_WRONG_ARG_VALUE -> if count == 0 + * ISO_FILE_IS_DIR + * ISO_OUT_OF_MEM + * ISO_INTERRUPTED + */ + int (*read)(IsoFileSource *src, void *buf, size_t count); + + /** + * Read a directory. + * + * Each call to this function will return a new children, until we reach + * the end of file (i.e, no more children), in that case it returns 0. + * + * The dir must be open() before calling this, and close() when no more + * needed. Only valid for dirs. + * + * Note that "." and ".." children MUST NOT BE returned. + * + * @param child + * pointer to be filled with the given child. Undefined on error or OEF + * @return + * 1 on success, 0 if EOF (no more children), < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_FILE_NOT_OPENNED + * ISO_FILE_IS_NOT_DIR + * ISO_OUT_OF_MEM + */ + int (*readdir)(IsoFileSource *src, IsoFileSource **child); + + /** + * Read the destination of a symlink. You don't need to open the file + * to call this. + * + * @param buf + * allocated buffer of at least bufsiz bytes. + * The dest. will be copied there, and it will be NULL-terminated + * @param bufsiz + * characters to be copied. Destination link will be truncated if + * it is larger than given size. This include the \0 character. + * @return + * 1 on success, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_WRONG_ARG_VALUE -> if bufsiz <= 0 + * ISO_FILE_IS_NOT_SYMLINK + * ISO_OUT_OF_MEM + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * + */ + int (*readlink)(IsoFileSource *src, char *buf, size_t bufsiz); + + /** + * Get the filesystem for this source. No extra ref is added, so you + * musn't unref the IsoFilesystem. + * + * @return + * The filesystem, NULL on error + */ + IsoFilesystem* (*get_filesystem)(IsoFileSource *src); + + /** + * Free implementation specific data. Should never be called by user. + * Use iso_file_source_unref() instead. + */ + void (*free)(IsoFileSource *src); + + /* + * TODO #00004 Add a get_mime_type() function. + * This can be useful for GUI apps, to choose the icon of the file + */ +}; + +/** + * An IsoFile Source is a POSIX abstraction of a file. + * + * @since 0.6.2 + */ +struct iso_file_source +{ + const IsoFileSourceIface *class; + int refcount; + void *data; +}; + +/** + * Initialize libisofs. You must call this before any usage of the library. + * @return 1 on success, < 0 on error + * + * @since 0.6.2 + */ +int iso_init(); + +/** + * Finalize libisofs. + * + * @since 0.6.2 + */ +void iso_finish(); + +/** + * Create a new image, empty. + * + * The image will be owned by you and should be unref() when no more needed. + * + * @param name + * Name of the image. This will be used as volset_id and volume_id. + * @param image + * Location where the image pointer will be stored. + * @return + * 1 sucess, < 0 error + * + * @since 0.6.2 + */ +int iso_image_new(const char *name, IsoImage **image); + + +/** + * The following two functions three macros are utilities to help ensuring + * version match of application, compile time header, and runtime library. + */ +/** + * Get version of the libisofs library at runtime. + * + * @since 0.6.2 + */ +void iso_lib_version(int *major, int *minor, int *micro); + +/** + * Check at runtime if the library is ABI compatible with the given version. + * + * @return + * 1 lib is compatible, 0 is not. + * + * @since 0.6.2 + */ +int iso_lib_is_compatible(int major, int minor, int micro); + + +/** + * These three release version numbers tell the revision of this header file + * and of the API it describes. They are memorized by applications at + * compile time. + * They must show the same values as these symbols in ./configure.ac + * LIBISOFS_MAJOR_VERSION=... + * LIBISOFS_MINOR_VERSION=... + * LIBISOFS_MICRO_VERSION=... + * Note to anybody who does own work inside libisofs: + * Any change of configure.ac or libisofs.h has to keep up this equality ! + * + * Before usage of these macros on your code, please read the usage discussion + * below. + * + * @since 0.6.2 + */ +#define iso_lib_header_version_major 0 +#define iso_lib_header_version_minor 6 +#define iso_lib_header_version_micro 3 + +/** + * Usage discussion: + * + * Some developers of the libburnia project have differing opinions how to + * ensure the compatibility of libaries and applications. + * + * It is about whether to use at compile time and at runtime the version + * numbers provided here. Thomas Schmitt advises to use them. Vreixo Formoso + * advises to use other means. + * + * At compile time: + * + * Vreixo Formoso advises to leave proper version matching to properly + * programmed checks in the the application's build system, which will + * eventually refuse compilation. + * + * Thomas Schmitt advises to use the macros defined here for comparison with + * the application's requirements of library revisions and to eventually + * break compilation. + * + * Both advises are combinable. I.e. be master of your build system and have + * #if checks in the source code of your application, nevertheless. + * + * At runtime (via iso_lib_is_compatible()): + * + * Vreixo Formoso advises to compare the application's requirements of + * library revisions with the runtime library. This is to allow runtime + * libraries which are young enough for the application but too old for + * the lib*.h files seen at compile time. + * + * Thomas Schmitt advises to compare the header revisions defined here with + * the runtime library. This is to enforce a strictly monotonous chain of + * revisions from app to header to library, at the cost of excluding some older + * libraries. + * + * These two advises are mutually exclusive. + */ + + +/** + * Creates an IsoWriteOpts for writing an image. You should set the options + * desired with the correspondent setters. + * + * Options by default are determined by the selected profile. Fifo size is set + * by default to 2 MB. + * + * @param opts + * Pointer to the location where the newly created IsoWriteOpts will be + * stored. You should free it with iso_write_opts_free() when no more + * needed. + * @param profile + * Default profile for image creation. For now the following values are + * defined: + * ---> 0 [BASIC] + * No extensions are enabled, and ISO level is set to 1. Only suitable + * for usage for very old and limited systems (like MS-DOS), or by a + * start point from which to set your custom options. + * ---> 1 [BACKUP] + * POSIX compatibility for backup. Simple settings, ISO level is set to + * 2 and RR extensions are enabled. Useful for backup purposes. + * ---> 2 [DISTRIBUTION] + * Setting for information distribution. Both RR and Joliet are enabled + * to maximize compatibility with most systems. Permissions are set to + * default values, and timestamps to the time of recording. + * @return + * 1 success, < 0 error + * + * @since 0.6.2 + */ +int iso_write_opts_new(IsoWriteOpts **opts, int profile); + +/** + * Free an IsoWriteOpts previously allocated with iso_write_opts_new(). + * + * @since 0.6.2 + */ +void iso_write_opts_free(IsoWriteOpts *opts); + +/** + * Set the ISO-9960 level to write at. + * + * @param level + * -> 1 for higher compatibility with old systems. With this level + * filenames are restricted to 8.3 characters. + * -> 2 to allow up to 31 filename characters. + * @return + * 1 success, < 0 error + * + * @since 0.6.2 + */ +int iso_write_opts_set_iso_level(IsoWriteOpts *opts, int level); + +/** + * Whether to use or not Rock Ridge extensions. + * + * This are standard extensions to ECMA-119, intended to add POSIX filesystem + * features to ECMA-119 images. Thus, usage of this flag is highly recommended + * for images used on GNU/Linux systems. With the usage of RR extension, the + * resulting image will have long filenames (up to 255 characters), deeper + * directory structure, POSIX permissions and owner info on files and + * directories, support for symbolic links or special files... All that + * attributes can be modified/setted with the appropiate function. + * + * @param enable + * 1 to enable RR extension, 0 to not add them + * @return + * 1 success, < 0 error + * + * @since 0.6.2 + */ +int iso_write_opts_set_rockridge(IsoWriteOpts *opts, int enable); + +/** + * Whether to add the non-standard Joliet extension to the image. + * + * This extensions are heavily used in Microsoft Windows systems, so if you + * plan to use your disc on such a system you should add this extension. + * Usage of Joliet supplies longer filesystem length (up to 64 unicode + * characters), and deeper directory structure. + * + * @param enable + * 1 to enable Joliet extension, 0 to not add them + * @return + * 1 success, < 0 error + * + * @since 0.6.2 + */ +int iso_write_opts_set_joliet(IsoWriteOpts *opts, int enable); + +/** + * Whether to use newer ISO-9660:1999 version. + * + * This is the second version of ISO-9660. It allows longer filenames and has + * less restrictions than old ISO-9660. However, nobody is using it so there + * are no much reasons to enable this. + * + * @since 0.6.2 + */ +int iso_write_opts_set_iso1999(IsoWriteOpts *opts, int enable); + +/** + * Omit the version number (";1") at the end of the ISO-9660 identifiers. + * This breaks ECMA-119 specification, but version numbers are usually not + * used, so it should work on most systems. Use with caution. + * + * @since 0.6.2 + */ +int iso_write_opts_set_omit_version_numbers(IsoWriteOpts *opts, int omit); + +/** + * Allow ISO-9660 directory hierarchy to be deeper than 8 levels. + * This breaks ECMA-119 specification. Use with caution. + * + * @since 0.6.2 + */ +int iso_write_opts_set_allow_deep_paths(IsoWriteOpts *opts, int allow); + +/** + * Allow path in the ISO-9660 tree to have more than 255 characters. + * This breaks ECMA-119 specification. Use with caution. + * + * @since 0.6.2 + */ +int iso_write_opts_set_allow_longer_paths(IsoWriteOpts *opts, int allow); + +/** + * Allow a single file or directory hierarchy to have up to 37 characters. + * This is larger than the 31 characters allowed by ISO level 2, and the + * extra space is taken from the version number, so this also forces + * omit_version_numbers. + * This breaks ECMA-119 specification and could lead to buffer overflow + * problems on old systems. Use with caution. + * + * @since 0.6.2 + */ +int iso_write_opts_set_max_37_char_filenames(IsoWriteOpts *opts, int allow); + +/** + * ISO-9660 forces filenames to have a ".", that separates file name from + * extension. libisofs adds it if original filename doesn't has one. Set + * this to 1 to prevent this behavior. + * This breaks ECMA-119 specification. Use with caution. + * + * @since 0.6.2 + */ +int iso_write_opts_set_no_force_dots(IsoWriteOpts *opts, int no); + +/** + * Allow lowercase characters in ISO-9660 filenames. By default, only + * uppercase characters, numbers and a few other characters are allowed. + * This breaks ECMA-119 specification. Use with caution. + * + * @since 0.6.2 + */ +int iso_write_opts_set_allow_lowercase(IsoWriteOpts *opts, int allow); + +/** + * Allow all ASCII characters to be appear on an ISO-9660 filename. Note + * that "/" and "\0" characters are never allowed, even in RR names. + * This breaks ECMA-119 specification. Use with caution. + * + * @since 0.6.2 + */ +int iso_write_opts_set_allow_full_ascii(IsoWriteOpts *opts, int allow); + +/** + * Allow all characters to be part of Volume and Volset identifiers on + * the Primary Volume Descriptor. This breaks ISO-9660 contraints, but + * should work on modern systems. + * + * @since 0.6.2 + */ +int iso_write_opts_set_relaxed_vol_atts(IsoWriteOpts *opts, int allow); + +/** + * Allow paths in the Joliet tree to have more than 240 characters. + * This breaks Joliet specification. Use with caution. + * + * @since 0.6.2 + */ +int iso_write_opts_set_joliet_longer_paths(IsoWriteOpts *opts, int allow); + +/** + * Whether to sort files based on their weight. + * + * @see iso_node_set_sort_weight + * @since 0.6.2 + */ +int iso_write_opts_set_sort_files(IsoWriteOpts *opts, int sort); + +/** + * Whether to set default values for files and directory permissions, gid and + * uid. All these take one of three values: 0, 1 or 2. + * + * If 0, the corresponding attribute will be kept as setted in the IsoNode. + * Unless you have changed it, it corresponds to the value on disc, so it + * is suitable for backup purposes. If set to 1, the corresponding attrib. + * will be changed by a default suitable value. Finally, if you set it to + * 2, the attrib. will be changed with the value specified by the functioins + * below. Note that for mode attributes, only the permissions are set, the + * file type remains unchanged. + * + * @see iso_write_opts_set_default_dir_mode + * @see iso_write_opts_set_default_file_mode + * @see iso_write_opts_set_default_uid + * @see iso_write_opts_set_default_gid + * @since 0.6.2 + */ +int iso_write_opts_set_replace_mode(IsoWriteOpts *opts, int dir_mode, + int file_mode, int uid, int gid); + +/** + * Set the mode to use on dirs when you set the replace_mode of dirs to 2. + * + * @see iso_write_opts_set_replace_mode + * @since 0.6.2 + */ +int iso_write_opts_set_default_dir_mode(IsoWriteOpts *opts, mode_t dir_mode); + +/** + * Set the mode to use on files when you set the replace_mode of files to 2. + * + * @see iso_write_opts_set_replace_mode + * @since 0.6.2 + */ +int iso_write_opts_set_default_file_mode(IsoWriteOpts *opts, mode_t file_mode); + +/** + * Set the uid to use when you set the replace_uid to 2. + * + * @see iso_write_opts_set_replace_mode + * @since 0.6.2 + */ +int iso_write_opts_set_default_uid(IsoWriteOpts *opts, uid_t uid); + +/** + * Set the gid to use when you set the replace_gid to 2. + * + * @see iso_write_opts_set_replace_mode + * @since 0.6.2 + */ +int iso_write_opts_set_default_gid(IsoWriteOpts *opts, gid_t gid); + +/** + * 0 to use IsoNode timestamps, 1 to use recording time, 2 to use + * values from timestamp field. This has only meaning if RR extensions + * are enabled. + * + * @see iso_write_opts_set_default_timestamp + * @since 0.6.2 + */ +int iso_write_opts_set_replace_timestamps(IsoWriteOpts *opts, int replace); + +/** + * Set the timestamp to use when you set the replace_timestamps to 2. + * + * @see iso_write_opts_set_replace_timestamps + * @since 0.6.2 + */ +int iso_write_opts_set_default_timestamp(IsoWriteOpts *opts, time_t timestamp); + +/** + * Whether to always record timestamps in GMT. + * + * By default, libisofs stores local time information on image. You can set + * this to always store timestamps in GMT. This is useful if you want to hide + * your timezone, or you live in a timezone that can't be represented in + * ECMA-119. These are timezones whose offset from GMT is greater than +13 + * hours, lower than -12 hours, or not a multiple of 15 minutes. + * + * @since 0.6.2 + */ +int iso_write_opts_set_always_gmt(IsoWriteOpts *opts, int gmt); + +/** + * Set the charset to use for the RR names of the files that will be created + * on the image. + * NULL to use default charset, that is the locale charset. + * You can obtain the list of charsets supported on your system executing + * "iconv -l" in a shell. + * + * @since 0.6.2 + */ +int iso_write_opts_set_output_charset(IsoWriteOpts *opts, const char *charset); + +/** + * Set the type of the image to create. Libisofs support two kind of images: + * stand-alone and appendable. + * + * A stand-alone image is an image that is valid alone, and that can be + * mounted by its own. This is the kind of image you will want to create + * in most cases. A stand-alone image can be burned in an empty CD or DVD, + * or write to an .iso file for future burning or distribution. + * + * On the other side, an appendable image is not self contained, it refers + * to serveral files that are stored outside the image. Its usage is for + * multisession discs, where you add data in a new session, while the + * previous session data can still be accessed. In those cases, the old + * data is not written again. Instead, the new image refers to it, and thus + * it's only valid when appended to the original. Note that in those cases + * the image will be written after the original, and thus you will want + * to use a ms_block greater than 0. + * + * Note that if you haven't import a previous image (by means of + * iso_image_import()), the image will always be a stand-alone image, as + * there is no previous data to refer to. + * + * @param appendable + * 1 to create an appendable image, 0 for an stand-alone one. + * + * @since 0.6.2 + */ +int iso_write_opts_set_appendable(IsoWriteOpts *opts, int appendable); + +/** + * Set the start block of the image. It is supposed to be the lba where the + * first block of the image will be written on disc. All references inside the + * ISO image will take this into account, thus providing a mountable image. + * + * For appendable images, that are written to a new session, you should + * pass here the lba of the next writable address on disc. + * + * In stand alone images this is usually 0. However, you may want to + * provide a different ms_block if you don't plan to burn the image in the + * first session on disc, such as in some CD-Extra disc whether the data + * image is written in a new session after some audio tracks. + * + * @since 0.6.2 + */ +int iso_write_opts_set_ms_block(IsoWriteOpts *opts, uint32_t ms_block); + +/** + * Sets the buffer where to store the descriptors that need to be written + * at the beginning of a overwriteable media to grow the image. + * + * @param overwrite + * When not NULL, it should point to a buffer of at least 64KiB, where + * libisofs will write the contents that should be written at the + * beginning of a overwriteable media, to grow the image. The growing + * of an image is a way, used by first time in growisofs by Andy Polyakov, + * to allow the appending of new data to non-multisession media, such + * as DVD+RW, in the same way you append a new session to a multisession + * disc, i.e., without need to write again the contents of the previous + * image. + * + * Note that if you want this kind of image growing, you will also need to + * set appendable to "1" and provide a valid ms_block after the previous + * image. + * + * You should initialize the buffer either with 0s, or with the contents of + * the first blocks of the image you're growing. In most cases, 0 is good + * enought. + * + * If you don't need this information, for example because you're creating a + * new image from scratch of because you will create an image for a true + * multisession media, just don't set this buffer or set it to NULL. + * + * @since 0.6.2 + */ +int iso_write_opts_set_overwrite_buf(IsoWriteOpts *opts, uint8_t *overwrite); + +/** + * Set the size, in number of blocks, of the FIFO buffer used between the + * writer thread and the burn_source. You have to provide at least a 32 + * blocks buffer. Default value is set to 2MB, if that is ok for you, you + * don't need to call this function. + * + * @since 0.6.2 + */ +int iso_write_opts_set_fifo_size(IsoWriteOpts *opts, size_t fifo_size); + +/** + * Create a burn_source to actually write the image. That burn_source can be + * used with libburn as a data source for a track. + * + * @param image + * The image to write. + * @param opts + * The options for image generation. All needed data will be copied, so + * you can free the given struct once this function returns. + * @param burn_src + * Location where the pointer to the burn_source will be stored + * @return + * 1 on success, < 0 on error + * + * @since 0.6.2 + */ +int iso_image_create_burn_source(IsoImage *image, IsoWriteOpts *opts, + struct burn_source **burn_src); + +/** + * Creates an IsoReadOpts for reading an existent image. You should set the + * options desired with the correspondent setters. Note that you may want to + * set the start block value. + * + * Options by default are determined by the selected profile. + * + * @param opts + * Pointer to the location where the newly created IsoReadOpts will be + * stored. You should free it with iso_read_opts_free() when no more + * needed. + * @param profile + * Default profile for image reading. For now the following values are + * defined: + * ---> 0 [STANDARD] + * Suitable for most situations. All extension are read. When both + * Joliet and RR extension are present, RR is used. + * @return + * 1 success, < 0 error + * + * @since 0.6.2 + */ +int iso_read_opts_new(IsoReadOpts **opts, int profile); + +/** + * Free an IsoReadOpts previously allocated with iso_read_opts_new(). + * + * @since 0.6.2 + */ +void iso_read_opts_free(IsoReadOpts *opts); + +/** + * Set the block where the image begins. It is usually 0, but may be different + * on a multisession disc. + * + * @since 0.6.2 + */ +int iso_read_opts_set_start_block(IsoReadOpts *opts, uint32_t block); + +/** + * Do not read Rock Ridge extensions. + * In most cases you don't want to use this. It could be useful if RR info + * is damaged, or if you want to use the Joliet tree. + * + * @since 0.6.2 + */ +int iso_read_opts_set_no_rockridge(IsoReadOpts *opts, int norr); + +/** + * Do not read Joliet extensions. + * + * @since 0.6.2 + */ +int iso_read_opts_set_no_joliet(IsoReadOpts *opts, int nojoliet); + +/** + * Do not read ISO 9660:1999 enhanced tree + * + * @since 0.6.2 + */ +int iso_read_opts_set_no_iso1999(IsoReadOpts *opts, int noiso1999); + +/** + * Whether to prefer Joliet over RR. libisofs usually prefers RR over + * Joliet, as it give us much more info about files. So, if both extensions + * are present, RR is used. You can set this if you prefer Joliet, but + * note that this is not very recommended. This doesn't mean than RR + * extensions are not read: if no Joliet is present, libisofs will read + * RR tree. + * + * @since 0.6.2 + */ +int iso_read_opts_set_preferjoliet(IsoReadOpts *opts, int preferjoliet); + +/** + * Set default uid for files when RR extensions are not present. + * + * @since 0.6.2 + */ +int iso_read_opts_set_default_uid(IsoReadOpts *opts, uid_t uid); + +/** + * Set default gid for files when RR extensions are not present. + * + * @since 0.6.2 + */ +int iso_read_opts_set_default_gid(IsoReadOpts *opts, gid_t gid); + +/** + * Set default permissions for files when RR extensions are not present. + * + * @param file_perm + * Permissions for files. + * @param dir_perm + * Permissions for directories. + * + * @since 0.6.2 + */ +int iso_read_opts_set_default_permissions(IsoReadOpts *opts, mode_t file_perm, + mode_t dir_perm); + +/** + * Set the input charset of the file names on the image. NULL to use locale + * charset. You have to specify a charset if the image filenames are encoded + * in a charset different that the local one. This could happen, for example, + * if the image was created on a system with different charset. + * + * @param charset + * The charset to use as input charset. You can obtain the list of + * charsets supported on your system executing "iconv -l" in a shell. + * + * @since 0.6.2 + */ +int iso_read_opts_set_input_charset(IsoReadOpts *opts, const char *charset); + +/** + * Import a previous session or image, for growing or modify. + * + * @param image + * The image context to which old image will be imported. Note that all + * files added to image, and image attributes, will be replaced with the + * contents of the old image. + * TODO #00025 support for merging old image files + * @param src + * Data Source from which old image will be read. A extra reference is + * added, so you still need to iso_data_source_unref() yours. + * @param opts + * Options for image import. All needed data will be copied, so you + * can free the given struct once this function returns. + * @param features + * If not NULL, a new IsoReadImageFeatures will be allocated and filled + * with the features of the old image. It should be freed with + * iso_read_image_features_destroy() when no more needed. You can pass + * NULL if you're not interested on them. + * @return + * 1 on success, < 0 on error + * + * @since 0.6.2 + */ +int iso_image_import(IsoImage *image, IsoDataSource *src, IsoReadOpts *opts, + IsoReadImageFeatures **features); + +/** + * Destroy an IsoReadImageFeatures object obtained with iso_image_import. + * + * @since 0.6.2 + */ +void iso_read_image_features_destroy(IsoReadImageFeatures *f); + +/** + * Get the size (in 2048 byte block) of the image, as reported in the PVM. + * + * @since 0.6.2 + */ +uint32_t iso_read_image_features_get_size(IsoReadImageFeatures *f); + +/** + * Whether RockRidge extensions are present in the image imported. + * + * @since 0.6.2 + */ +int iso_read_image_features_has_rockridge(IsoReadImageFeatures *f); + +/** + * Whether Joliet extensions are present in the image imported. + * + * @since 0.6.2 + */ +int iso_read_image_features_has_joliet(IsoReadImageFeatures *f); + +/** + * Whether the image is recorded according to ISO 9660:1999, i.e. it has + * a version 2 Enhanced Volume Descriptor. + * + * @since 0.6.2 + */ +int iso_read_image_features_has_iso1999(IsoReadImageFeatures *f); + +/** + * Whether El-Torito boot record is present present in the image imported. + * + * @since 0.6.2 + */ +int iso_read_image_features_has_eltorito(IsoReadImageFeatures *f); + +/** + * Increments the reference counting of the given image. + * + * @since 0.6.2 + */ +void iso_image_ref(IsoImage *image); + +/** + * Decrements the reference couting of the given image. + * If it reaches 0, the image is free, together with its tree nodes (whether + * their refcount reach 0 too, of course). + * + * @since 0.6.2 + */ +void iso_image_unref(IsoImage *image); + +/** + * Attach user defined data to the image. Use this if your application needs + * to store addition info together with the IsoImage. If the image already + * has data attached, the old data will be freed. + * + * @param data + * Pointer to application defined data that will be attached to the + * image. You can pass NULL to remove any already attached data. + * @param give_up + * Function that will be called when the image does not need the data + * any more. It receives the data pointer as an argumente, and eventually + * causes data to be freed. It can be NULL if you don't need it. + * @return + * 1 on succes, < 0 on error + * + * @since 0.6.2 + */ +int iso_image_attach_data(IsoImage *image, void *data, void (*give_up)(void*)); + +/** + * The the data previously attached with iso_image_attach_data() + * + * @since 0.6.2 + */ +void *iso_image_get_attached_data(IsoImage *image); + +/** + * Get the root directory of the image. + * No extra ref is added to it, so you musn't unref it. Use iso_node_ref() + * if you want to get your own reference. + * + * @since 0.6.2 + */ +IsoDir *iso_image_get_root(const IsoImage *image); + +/** + * Fill in the volset identifier for a image. + * + * @since 0.6.2 + */ +void iso_image_set_volset_id(IsoImage *image, const char *volset_id); + +/** + * Get the volset identifier. + * The returned string is owned by the image and should not be freed nor + * changed. + * + * @since 0.6.2 + */ +const char *iso_image_get_volset_id(const IsoImage *image); + +/** + * Fill in the volume identifier for a image. + * + * @since 0.6.2 + */ +void iso_image_set_volume_id(IsoImage *image, const char *volume_id); + +/** + * Get the volume identifier. + * The returned string is owned by the image and should not be freed nor + * changed. + * + * @since 0.6.2 + */ +const char *iso_image_get_volume_id(const IsoImage *image); + +/** + * Fill in the publisher for a image. + * + * @since 0.6.2 + */ +void iso_image_set_publisher_id(IsoImage *image, const char *publisher_id); + +/** + * Get the publisher of a image. + * The returned string is owned by the image and should not be freed nor + * changed. + * + * @since 0.6.2 + */ +const char *iso_image_get_publisher_id(const IsoImage *image); + +/** + * Fill in the data preparer for a image. + * + * @since 0.6.2 + */ +void iso_image_set_data_preparer_id(IsoImage *image, + const char *data_preparer_id); + +/** + * Get the data preparer of a image. + * The returned string is owned by the image and should not be freed nor + * changed. + * + * @since 0.6.2 + */ +const char *iso_image_get_data_preparer_id(const IsoImage *image); + +/** + * Fill in the system id for a image. Up to 32 characters. + * + * @since 0.6.2 + */ +void iso_image_set_system_id(IsoImage *image, const char *system_id); + +/** + * Get the system id of a image. + * The returned string is owned by the image and should not be freed nor + * changed. + * + * @since 0.6.2 + */ +const char *iso_image_get_system_id(const IsoImage *image); + +/** + * Fill in the application id for a image. Up to 128 chars. + * + * @since 0.6.2 + */ +void iso_image_set_application_id(IsoImage *image, const char *application_id); + +/** + * Get the application id of a image. + * The returned string is owned by the image and should not be freed nor + * changed. + * + * @since 0.6.2 + */ +const char *iso_image_get_application_id(const IsoImage *image); + +/** + * Fill copyright information for the image. Usually this refers + * to a file on disc. Up to 37 characters. + * + * @since 0.6.2 + */ +void iso_image_set_copyright_file_id(IsoImage *image, + const char *copyright_file_id); + +/** + * Get the copyright information of a image. + * The returned string is owned by the image and should not be freed nor + * changed. + * + * @since 0.6.2 + */ +const char *iso_image_get_copyright_file_id(const IsoImage *image); + +/** + * Fill abstract information for the image. Usually this refers + * to a file on disc. Up to 37 characters. + * + * @since 0.6.2 + */ +void iso_image_set_abstract_file_id(IsoImage *image, + const char *abstract_file_id); + +/** + * Get the abstract information of a image. + * The returned string is owned by the image and should not be freed nor + * changed. + * + * @since 0.6.2 + */ +const char *iso_image_get_abstract_file_id(const IsoImage *image); + +/** + * Fill biblio information for the image. Usually this refers + * to a file on disc. Up to 37 characters. + * + * @since 0.6.2 + */ +void iso_image_set_biblio_file_id(IsoImage *image, const char *biblio_file_id); + +/** + * Get the biblio information of a image. + * The returned string is owned by the image and should not be freed nor + * changed. + * + * @since 0.6.2 + */ +const char *iso_image_get_biblio_file_id(const IsoImage *image); + +/** + * Create a bootable image by adding a El-Torito boot image. + * + * This also add a catalog boot node to the image filesystem tree. + * + * @param image + * The image to make bootable. If it was already bootable this function + * returns an error and the image remains unmodified. + * @param image_path + * The path on the image tree of a regular file to use as default boot + * image. + * @param type + * The boot media type. This can be one of 3 types: + * - Floppy emulation: Boot image file must be exactly + * 1200 kB, 1440 kB or 2880 kB. + * - Hard disc emulation: The image must begin with a master + * boot record with a single image. + * - No emulation. You should specify load segment and load size + * of image. + * @param catalog_path + * The path on the image tree where the catalog will be stored. The + * directory component of this path must be a directory existent on the + * image tree, and the filename component must be unique among all + * children of that directory on image. Otherwise a correspodent error + * code will be returned. This function will add an IsoBoot node that acts + * as a placeholder for the real catalog, that will be generated at image + * creation time. + * @param boot + * Location where a pointer to the added boot image will be stored. That + * object is owned by the IsoImage and should not be freed by the user, + * nor dereferenced once the last reference to the IsoImage was disposed + * via iso_image_unref(). A NULL value is allowed if you don't need a + * reference to the boot image. + * @return + * 1 on success, < 0 on error + * + * @since 0.6.2 + */ +int iso_image_set_boot_image(IsoImage *image, const char *image_path, + enum eltorito_boot_media_type type, + const char *catalog_path, + ElToritoBootImage **boot); + +/* TODO #00026 : add support for "hidden" bootable images. */ + +/** + * Get El-Torito boot image of an ISO image, if any. + * + * This can be useful, for example, to check if a volume read from a previous + * session or an existing image is bootable. It can also be useful to get + * the image and catalog tree nodes. An application would want those, for + * example, to prevent the user removing it. + * + * Both nodes are owned by libisofs and should not be freed. You can get your + * own ref with iso_node_ref(). You can can also check if the node is already + * on the tree by getting its parent (note that when reading El-Torito info + * from a previous image, the nodes might not be on the tree even if you haven't + * removed them). Remember that you'll need to get a new ref + * (with iso_node_ref()) before inserting them again to the tree, and probably + * you will also need to set the name or permissions. + * + * @param image + * The image from which to get the boot image. + * @param boot + * If not NULL, it will be filled with a pointer to the boot image, if + * any. That object is owned by the IsoImage and should not be freed by + * the user, nor dereferenced once the last reference to the IsoImage was + * disposed via iso_image_unref(). + * @param imgnode + * When not NULL, it will be filled with the image tree node. No extra ref + * is added, you can use iso_node_ref() to get one if you need it. + * @param catnode + * When not NULL, it will be filled with the catnode tree node. No extra + * ref is added, you can use iso_node_ref() to get one if you need it. + * @return + * 1 on success, 0 is the image is not bootable (i.e., it has no El-Torito + * image), < 0 error. + * + * @since 0.6.2 + */ +int iso_image_get_boot_image(IsoImage *image, ElToritoBootImage **boot, + IsoFile **imgnode, IsoBoot **catnode); + +/** + * Removes the El-Torito bootable image. + * + * The IsoBoot node that acts as placeholder for the catalog is also removed + * for the image tree, if there. + * If the image is not bootable (don't have el-torito boot image) this function + * just returns. + * + * @since 0.6.2 + */ +void iso_image_remove_boot_image(IsoImage *image); + +/** + * Sets the load segment for the initial boot image. This is only for + * no emulation boot images, and is a NOP for other image types. + * + * @since 0.6.2 + */ +void el_torito_set_load_seg(ElToritoBootImage *bootimg, short segment); + +/** + * Sets the number of sectors (512b) to be load at load segment during + * the initial boot procedure. This is only for + * no emulation boot images, and is a NOP for other image types. + * + * @since 0.6.2 + */ +void el_torito_set_load_size(ElToritoBootImage *bootimg, short sectors); + +/** + * Marks the specified boot image as not bootable + * + * @since 0.6.2 + */ +void el_torito_set_no_bootable(ElToritoBootImage *bootimg); + +/** + * Specifies that this image needs to be patched. This involves the writting + * of a 56 bytes boot information table at offset 8 of the boot image file. + * The original boot image file won't be modified. + * This is needed for isolinux boot images. + * + * @since 0.6.2 + */ +void el_torito_patch_isolinux_image(ElToritoBootImage *bootimg); + +/** + * Increments the reference counting of the given node. + * + * @since 0.6.2 + */ +void iso_node_ref(IsoNode *node); + +/** + * Decrements the reference couting of the given node. + * If it reach 0, the node is free, and, if the node is a directory, + * its children will be unref() too. + * + * @since 0.6.2 + */ +void iso_node_unref(IsoNode *node); + +/** + * Get the type of an IsoNode. + * + * @since 0.6.2 + */ +enum IsoNodeType iso_node_get_type(IsoNode *node); + +/** + * Set the name of a node. Note that if the node is already added to a dir + * this can fail if dir already contains a node with the new name. + * + * @param node + * The node whose name you want to change. Note that you can't change + * the name of the root. + * @param name + * The name for the node. If you supply an empty string or a + * name greater than 255 characters this returns with failure, and + * node name is not modified. + * @return + * 1 on success, < 0 on error + * + * @since 0.6.2 + */ +int iso_node_set_name(IsoNode *node, const char *name); + +/** + * Get the name of a node. + * The returned string belongs to the node and should not be modified nor + * freed. Use strdup if you really need your own copy. + * + * @since 0.6.2 + */ +const char *iso_node_get_name(const IsoNode *node); + +/** + * Set the permissions for the node. This attribute is only useful when + * Rock Ridge extensions are enabled. + * + * @param mode + * bitmask with the permissions of the node, as specified in 'man 2 stat'. + * The file type bitfields will be ignored, only file permissions will be + * modified. + * + * @since 0.6.2 + */ +void iso_node_set_permissions(IsoNode *node, mode_t mode); + +/** + * Get the permissions for the node + * + * @since 0.6.2 + */ +mode_t iso_node_get_permissions(const IsoNode *node); + +/** + * Get the mode of the node, both permissions and file type, as specified in + * 'man 2 stat'. + * + * @since 0.6.2 + */ +mode_t iso_node_get_mode(const IsoNode *node); + +/** + * Set the user id for the node. This attribute is only useful when + * Rock Ridge extensions are enabled. + * + * @since 0.6.2 + */ +void iso_node_set_uid(IsoNode *node, uid_t uid); + +/** + * Get the user id of the node. + * + * @since 0.6.2 + */ +uid_t iso_node_get_uid(const IsoNode *node); + +/** + * Set the group id for the node. This attribute is only useful when + * Rock Ridge extensions are enabled. + * + * @since 0.6.2 + */ +void iso_node_set_gid(IsoNode *node, gid_t gid); + +/** + * Get the group id of the node. + * + * @since 0.6.2 + */ +gid_t iso_node_get_gid(const IsoNode *node); + +/** + * Set the time of last modification of the file + * + * @since 0.6.2 + */ +void iso_node_set_mtime(IsoNode *node, time_t time); + +/** + * Get the time of last modification of the file + * + * @since 0.6.2 + */ +time_t iso_node_get_mtime(const IsoNode *node); + +/** + * Set the time of last access to the file + * + * @since 0.6.2 + */ +void iso_node_set_atime(IsoNode *node, time_t time); + +/** + * Get the time of last access to the file + * + * @since 0.6.2 + */ +time_t iso_node_get_atime(const IsoNode *node); + +/** + * Set the time of last status change of the file + * + * @since 0.6.2 + */ +void iso_node_set_ctime(IsoNode *node, time_t time); + +/** + * Get the time of last status change of the file + * + * @since 0.6.2 + */ +time_t iso_node_get_ctime(const IsoNode *node); + +/** + * Set if the node will be hidden in RR/ISO tree, Joliet tree or both. + * + * If the file is setted as hidden in one tree, it won't be included there, so + * it won't be visible in a OS accessing CD using that tree. For example, + * GNU/Linux systems access to Rock Ridge / ISO9960 tree in order to see + * what is recorded on CD, while MS Windows make use of the Joliet tree. If a + * file is hidden only in Joliet, it won't be visible in Windows systems, + * while still visible in Linux. + * + * If a file is hidden in both trees, it won't be written to image. + * + * @param node + * The node that is to be hidden. + * @param hide_attrs + * IsoHideNodeFlag's to set the trees in which file will be hidden. + * + * @since 0.6.2 + */ +void iso_node_set_hidden(IsoNode *node, int hide_attrs); + +/** + * Add a new node to a dir. Note that this function don't add a new ref to + * the node, so you don't need to free it, it will be automatically freed + * when the dir is deleted. Of course, if you want to keep using the node + * after the dir life, you need to iso_node_ref() it. + * + * @param dir + * the dir where to add the node + * @param child + * the node to add. You must ensure that the node hasn't previously added + * to other dir, and that the node name is unique inside the child. + * Otherwise this function will return a failure, and the child won't be + * inserted. + * @param replace + * if the dir already contains a node with the same name, whether to + * replace or not the old node with this. + * @return + * number of nodes in dir if succes, < 0 otherwise + * Possible errors: + * ISO_NULL_POINTER, if dir or child are NULL + * ISO_NODE_ALREADY_ADDED, if child is already added to other dir + * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists + * ISO_WRONG_ARG_VALUE, if child == dir, or replace != (0,1) + * + * @since 0.6.2 + */ +int iso_dir_add_node(IsoDir *dir, IsoNode *child, + enum iso_replace_mode replace); + +/** + * Locate a node inside a given dir. + * + * @param dir + * The dir where to look for the node. + * @param name + * The name of the node + * @param node + * Location for a pointer to the node, it will filled with NULL if the dir + * doesn't have a child with the given name. + * The node will be owned by the dir and shouldn't be unref(). Just call + * iso_node_ref() to get your own reference to the node. + * Note that you can pass NULL is the only thing you want to do is check + * if a node with such name already exists on dir. + * @return + * 1 node found, 0 child has no such node, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if dir or name are NULL + * + * @since 0.6.2 + */ +int iso_dir_get_node(IsoDir *dir, const char *name, IsoNode **node); + +/** + * Get the number of children of a directory. + * + * @return + * >= 0 number of items, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if dir is NULL + * + * @since 0.6.2 + */ +int iso_dir_get_children_count(IsoDir *dir); + +/** + * Removes a child from a directory. + * The child is not freed, so you will become the owner of the node. Later + * you can add the node to another dir (calling iso_dir_add_node), or free + * it if you don't need it (with iso_node_unref). + * + * @return + * 1 on success, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if node is NULL + * ISO_NODE_NOT_ADDED_TO_DIR, if node doesn't belong to a dir + * + * @since 0.6.2 + */ +int iso_node_take(IsoNode *node); + +/** + * Removes a child from a directory and free (unref) it. + * If you want to keep the child alive, you need to iso_node_ref() it + * before this call, but in that case iso_node_take() is a better + * alternative. + * + * @return + * 1 on success, < 0 error + * + * @since 0.6.2 + */ +int iso_node_remove(IsoNode *node); + +/* + * Get the parent of the given iso tree node. No extra ref is added to the + * returned directory, you must take your ref. with iso_node_ref() if you + * need it. + * + * If node is the root node, the same node will be returned as its parent. + * + * This returns NULL if the node doesn't pertain to any tree + * (it was removed/take). + * + * @since 0.6.2 + */ +IsoDir *iso_node_get_parent(IsoNode *node); + +/** + * Get an iterator for the children of the given dir. + * + * You can iterate over the children with iso_dir_iter_next. When finished, + * you should free the iterator with iso_dir_iter_free. + * You musn't delete a child of the same dir, using iso_node_take() or + * iso_node_remove(), while you're using the iterator. You can use + * iso_node_take_iter() or iso_node_remove_iter() instead. + * + * You can use the iterator in the way like this + * + * IsoDirIter *iter; + * IsoNode *node; + * if ( iso_dir_get_children(dir, &iter) != 1 ) { + * // handle error + * } + * while ( iso_dir_iter_next(iter, &node) == 1 ) { + * // do something with the child + * } + * iso_dir_iter_free(iter); + * + * An iterator is intended to be used in a single iteration over the + * children of a dir. Thus, it should be treated as a temporary object, + * and free as soon as possible. + * + * @return + * 1 success, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if dir or iter are NULL + * ISO_OUT_OF_MEM + * + * @since 0.6.2 + */ +int iso_dir_get_children(const IsoDir *dir, IsoDirIter **iter); + +/** + * Get the next child. + * Take care that the node is owned by its parent, and will be unref() when + * the parent is freed. If you want your own ref to it, call iso_node_ref() + * on it. + * + * @return + * 1 success, 0 if dir has no more elements, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if node or iter are NULL + * ISO_ERROR, on wrong iter usage, usual caused by modiying the + * dir during iteration + * + * @since 0.6.2 + */ +int iso_dir_iter_next(IsoDirIter *iter, IsoNode **node); + +/** + * Check if there're more children. + * + * @return + * 1 dir has more elements, 0 no, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if iter is NULL + * + * @since 0.6.2 + */ +int iso_dir_iter_has_next(IsoDirIter *iter); + +/** + * Free a dir iterator. + * + * @since 0.6.2 + */ +void iso_dir_iter_free(IsoDirIter *iter); + +/** + * Removes a child from a directory during an iteration, without freeing it. + * It's like iso_node_take(), but to be used during a directory iteration. + * The node removed will be the last returned by the iteration. + * + * The behavior on two call to this function without calling iso_dir_iter_next + * between then is undefined, and should never occur. (TODO protect against this?) + * + * @return + * 1 on succes, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if iter is NULL + * ISO_ERROR, on wrong iter usage, for example by call this before + * iso_dir_iter_next. + * + * @since 0.6.2 + */ +int iso_dir_iter_take(IsoDirIter *iter); + +/** + * Removes a child from a directory during an iteration and unref() it. + * It's like iso_node_remove(), but to be used during a directory iteration. + * The node removed will be the last returned by the iteration. + * + * The behavior on two call to this function without calling iso_tree_iter_next + * between then is undefined, and should never occur. (TODO protect against this?) + * + * @return + * 1 on succes, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if iter is NULL + * ISO_ERROR, on wrong iter usage, for example by call this before + * iso_dir_iter_next. + * + * @since 0.6.2 + */ +int iso_dir_iter_remove(IsoDirIter *iter); + +/** + * Get the destination of a node. + * The returned string belongs to the node and should not be modified nor + * freed. Use strdup if you really need your own copy. + * + * @since 0.6.2 + */ +const char *iso_symlink_get_dest(const IsoSymlink *link); + +/** + * Set the destination of a link. + * + * @param dest + * New destination for the link. It must be a non-empty string, otherwise + * this function doesn't modify previous destination. + * @return + * 1 on success, < 0 on error + * + * @since 0.6.2 + */ +int iso_symlink_set_dest(IsoSymlink *link, const char *dest); + +/** + * Sets the order in which a node will be written on image. High weihted files + * will be written first, so in a disc them will be written near the center. + * + * @param node + * The node which weight will be changed. If it's a dir, this function + * will change the weight of all its children. For nodes other that dirs + * or regular files, this function has no effect. + * @param w + * The weight as a integer number, the greater this value is, the + * closer from the begining of image the file will be written. + * + * @since 0.6.2 + */ +void iso_node_set_sort_weight(IsoNode *node, int w); + +/** + * Get the sort weight of a file. + * + * @since 0.6.2 + */ +int iso_file_get_sort_weight(IsoFile *file); + +/** + * Get the size of the file, in bytes + * + * @since 0.6.2 + */ +off_t iso_file_get_size(IsoFile *file); + +/** + * Add a new directory to the iso tree. Permissions, owner and hidden atts + * are taken from parent, you can modify them later. + * + * @param parent + * the dir where the new directory will be created + * @param name + * name for the new dir. If a node with same name already exists on + * parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. + * @param dir + * place where to store a pointer to the newly created dir. No extra + * ref is addded, so you will need to call iso_node_ref() if you really + * need it. You can pass NULL in this parameter if you don't need the + * pointer. + * @return + * number of nodes in parent if success, < 0 otherwise + * Possible errors: + * ISO_NULL_POINTER, if parent or name are NULL + * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists + * ISO_OUT_OF_MEM + * + * @since 0.6.2 + */ +int iso_tree_add_new_dir(IsoDir *parent, const char *name, IsoDir **dir); + +/* + TODO #00007 expose Stream and this function: + int iso_tree_add_new_file(IsoDir *parent, const char *name, stream, file) + */ + +/** + * Add a new symlink to the directory tree. Permissions are set to 0777, + * owner and hidden atts are taken from parent. You can modify any of them + * later. + * + * @param parent + * the dir where the new symlink will be created + * @param name + * name for the new symlink. If a node with same name already exists on + * parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. + * @param dest + * destination of the link + * @param link + * place where to store a pointer to the newly created link. No extra + * ref is addded, so you will need to call iso_node_ref() if you really + * need it. You can pass NULL in this parameter if you don't need the + * pointer + * @return + * number of nodes in parent if success, < 0 otherwise + * Possible errors: + * ISO_NULL_POINTER, if parent, name or dest are NULL + * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists + * ISO_OUT_OF_MEM + * + * @since 0.6.2 + */ +int iso_tree_add_new_symlink(IsoDir *parent, const char *name, + const char *dest, IsoSymlink **link); + +/** + * Add a new special file to the directory tree. As far as libisofs concerns, + * an special file is a block device, a character device, a FIFO (named pipe) + * or a socket. You can choose the specific kind of file you want to add + * by setting mode propertly (see man 2 stat). + * + * Note that special files are only written to image when Rock Ridge + * extensions are enabled. Moreover, a special file is just a directory entry + * in the image tree, no data is written beyond that. + * + * Owner and hidden atts are taken from parent. You can modify any of them + * later. + * + * @param parent + * the dir where the new special file will be created + * @param name + * name for the new special file. If a node with same name already exists + * on parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. + * @param mode + * file type and permissions for the new node. Note that you can't + * specify any kind of file here, only special types are allowed. i.e, + * S_IFSOCK, S_IFBLK, S_IFCHR and S_IFIFO are valid types; S_IFLNK, + * S_IFREG and S_IFDIR aren't. + * @param dev + * device ID, equivalent to the st_rdev field in man 2 stat. + * @param special + * place where to store a pointer to the newly created special file. No + * extra ref is addded, so you will need to call iso_node_ref() if you + * really need it. You can pass NULL in this parameter if you don't need + * the pointer. + * @return + * number of nodes in parent if success, < 0 otherwise + * Possible errors: + * ISO_NULL_POINTER, if parent, name or dest are NULL + * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists + * ISO_WRONG_ARG_VALUE if you select a incorrect mode + * ISO_OUT_OF_MEM + * + * @since 0.6.2 + */ +int iso_tree_add_new_special(IsoDir *parent, const char *name, mode_t mode, + dev_t dev, IsoSpecial **special); + +/** + * Set whether to follow or not symbolic links when added a file from a source + * to IsoImage. Default behavior is to not follow symlinks. + * + * @since 0.6.2 + */ +void iso_tree_set_follow_symlinks(IsoImage *image, int follow); + +/** + * Get current setting for follow_symlinks. + * + * @see iso_tree_set_follow_symlinks + * @since 0.6.2 + */ +int iso_tree_get_follow_symlinks(IsoImage *image); + +/** + * Set whether to skip or not hidden files when adding a directory recursibely. + * Default behavior is to not ignore them, i.e., to add hidden files to image. + * + * @since 0.6.2 + */ +void iso_tree_set_ignore_hidden(IsoImage *image, int skip); + +/** + * Get current setting for ignore_hidden. + * + * @see iso_tree_set_ignore_hidden + * @since 0.6.2 + */ +int iso_tree_get_ignore_hidden(IsoImage *image); + +/** + * Set the replace mode, that defines the behavior of libisofs when adding + * a node whit the same name that an existent one, during a recursive + * directory addition. + * + * @since 0.6.2 + */ +void iso_tree_set_replace_mode(IsoImage *image, enum iso_replace_mode mode); + +/** + * Get current setting for replace_mode. + * + * @see iso_tree_set_replace_mode + * @since 0.6.2 + */ +enum iso_replace_mode iso_tree_get_replace_mode(IsoImage *image); + +/** + * Set whether to skip or not special files. Default behavior is to not skip + * them. Note that, despite of this setting, special files won't never be added + * to an image unless RR extensions were enabled. + * + * @param skip + * Bitmask to determine what kind of special files will be skipped: + * bit0: ignore FIFOs + * bit1: ignore Sockets + * bit2: ignore char devices + * bit3: ignore block devices + * + * @since 0.6.2 + */ +void iso_tree_set_ignore_special(IsoImage *image, int skip); + +/** + * Get current setting for ignore_special. + * + * @see iso_tree_set_ignore_special + * @since 0.6.2 + */ +int iso_tree_get_ignore_special(IsoImage *image); + +/** + * Add a excluded path. These are paths that won't never added to image, + * and will be excluded even when adding recursively its parent directory. + * + * For example, in + * + * iso_tree_add_exclude(image, "/home/user/data/private"); + * iso_tree_add_dir_rec(image, root, "/home/user/data"); + * + * the directory /home/user/data/private won't be added to image. + * + * However, if you explicity add a deeper dir, it won't be excluded. i.e., + * in the following example. + * + * iso_tree_add_exclude(image, "/home/user/data"); + * iso_tree_add_dir_rec(image, root, "/home/user/data/private"); + * + * the directory /home/user/data/private is added. On the other, side, and + * foollowing the the example above, + * + * iso_tree_add_dir_rec(image, root, "/home/user"); + * + * will exclude the directory "/home/user/data". + * + * Absolute paths are not mandatory, you can, for example, add a relative + * path such as: + * + * iso_tree_add_exclude(image, "private"); + * iso_tree_add_exclude(image, "user/data"); + * + * to excluve, respectively, all files or dirs named private, and also all + * files or dirs named data that belong to a folder named "user". Not that the + * above rule about deeper dirs is still valid. i.e., if you call + * + * iso_tree_add_dir_rec(image, root, "/home/user/data/music"); + * + * it is included even containing "user/data" string. However, a possible + * "/home/user/data/music/user/data" is not added. + * + * Usual wildcards, such as * or ? are also supported, with the usual meaning + * as stated in "man 7 glob". For example + * + * // to exclude backup text files + * iso_tree_add_exclude(image, "*.~"); + * + * @return + * 1 on success, < 0 on error + * + * @since 0.6.2 + */ +int iso_tree_add_exclude(IsoImage *image, const char *path); + +/** + * Remove a previously added exclude. + * + * @see iso_tree_add_exclude + * @return + * 1 on success, 0 exclude do not exists, < 0 on error + * + * @since 0.6.2 + */ +int iso_tree_remove_exclude(IsoImage *image, const char *path); + +/** + * Set a callback function that libisofs will call for each file that is + * added to the given image by a recursive addition function. This includes + * image import. + * + * @param report + * pointer to a function that will be called just before a file will be + * added to the image. You can control whether the file will be in fact + * added or ignored. + * This function should return 1 to add the file, 0 to ignore it and + * continue, < 0 to abort the process + * NULL is allowed if you don't want any callback. + * + * @since 0.6.2 + */ +void iso_tree_set_report_callback(IsoImage *image, + int (*report)(IsoImage*, IsoFileSource*)); + +/** + * Add a new node to the image tree, from an existing file. + * + * TODO comment Builder and Filesystem related issues when exposing both + * + * All attributes will be taken from the source file. The appropriate file + * type will be created. + * + * @param image + * The image + * @param parent + * The directory in the image tree where the node will be added. + * @param path + * The path of the file to add in the filesystem. + * @param node + * place where to store a pointer to the newly added file. No + * extra ref is addded, so you will need to call iso_node_ref() if you + * really need it. You can pass NULL in this parameter if you don't need + * the pointer. + * @return + * number of nodes in parent if success, < 0 otherwise + * Possible errors: + * ISO_NULL_POINTER, if image, parent or path are NULL + * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists + * ISO_OUT_OF_MEM + * + * @since 0.6.2 + */ +int iso_tree_add_node(IsoImage *image, IsoDir *parent, const char *path, + IsoNode **node); + +/** + * Add the contents of a dir to a given directory of the iso tree. + * + * There are several options to control what files are added or how they are + * managed. Take a look at iso_tree_set_* functions to see diferent options + * for recursive directory addition. + * + * TODO comment Builder and Filesystem related issues when exposing both + * + * @param image + * The image to which the directory belong. + * @param parent + * Directory on the image tree where to add the contents of the dir + * @param dir + * Path to a dir in the filesystem + * @return + * number of nodes in parent if success, < 0 otherwise + * + * @since 0.6.2 + */ +int iso_tree_add_dir_rec(IsoImage *image, IsoDir *parent, const char *dir); + +/** + * Locate a node by its path on image. + * + * @param node + * Location for a pointer to the node, it will filled with NULL if the + * given path does not exists on image. + * The node will be owned by the image and shouldn't be unref(). Just call + * iso_node_ref() to get your own reference to the node. + * Note that you can pass NULL is the only thing you want to do is check + * if a node with such path really exists. + * @return + * 1 found, 0 not found, < 0 error + * + * @since 0.6.2 + */ +int iso_tree_path_to_node(IsoImage *image, const char *path, IsoNode **node); + +/** + * Increments the reference counting of the given IsoDataSource. + * + * @since 0.6.2 + */ +void iso_data_source_ref(IsoDataSource *src); + +/** + * Decrements the reference counting of the given IsoDataSource, freeing it + * if refcount reach 0. + * + * @since 0.6.2 + */ +void iso_data_source_unref(IsoDataSource *src); + +/** + * Create a new IsoDataSource from a local file. This is suitable for + * accessing regular .iso images, or to acces drives via its block device + * and standard POSIX I/O calls. + * + * @param path + * The path of the file + * @param src + * Will be filled with the pointer to the newly created data source. + * @return + * 1 on success, < 0 on error. + * + * @since 0.6.2 + */ +int iso_data_source_new_from_file(const char *path, IsoDataSource **src); + +/** + * Get the status of the buffer used by a burn_source. + * + * @param b + * A burn_source previously obtained with + * iso_image_create_burn_source(). + * @param size + * Will be filled with the total size of the buffer, in bytes + * @param free_bytes + * Will be filled with the bytes currently available in buffer + * @return + * < 0 error, > 0 state: + * 1="active" : input and consumption are active + * 2="ending" : input has ended without error + * 3="failing" : input had error and ended, + * 5="abandoned" : consumption has ended prematurely + * 6="ended" : consumption has ended without input error + * 7="aborted" : consumption has ended after input error + * + * @since 0.6.2 + */ +int iso_ring_buffer_get_status(struct burn_source *b, size_t *size, + size_t *free_bytes); + +#define ISO_MSGS_MESSAGE_LEN 4096 + +/** + * Control queueing and stderr printing of messages from libisofs. + * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", + * "NOTE", "UPDATE", "DEBUG", "ALL". + * + * @param queue_severity Gives the minimum limit for messages to be queued. + * Default: "NEVER". If you queue messages then you + * must consume them by iso_msgs_obtain(). + * @param print_severity Does the same for messages to be printed directly + * to stderr. + * @param print_id A text prefix to be printed before the message. + * @return >0 for success, <=0 for error + * + * @since 0.6.2 + */ +int iso_set_msgs_severities(char *queue_severity, char *print_severity, + char *print_id); + +/** + * Obtain the oldest pending libisofs message from the queue which has at + * least the given minimum_severity. This message and any older message of + * lower severity will get discarded from the queue and is then lost forever. + * + * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", + * "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER" + * will discard the whole queue. + * + * @param error_code + * Will become a unique error code as listed at the end of this header + * @param imgid + * Id of the image that was issued the message. + * @param msg_text + * Must provide at least ISO_MSGS_MESSAGE_LEN bytes. + * @param severity + * Will become the severity related to the message and should provide at + * least 80 bytes. + * @return + * 1 if a matching item was found, 0 if not, <0 for severe errors + * + * @since 0.6.2 + */ +int iso_obtain_msgs(char *minimum_severity, int *error_code, int *imgid, + char msg_text[], char severity[]); + + +/** Submit a message to the libisofs queueing system. It will be queued or + printed as if it was generated by libburn itself. + @param error_code The unique error code of your message. + Submit 0 if you do not have reserved error codes within + the libburnia project. + @param msg_text Not more than ISO_MSGS_MESSAGE_LEN characters of + message text. + @param os_errno Eventual errno related to the message. Submit 0 if + the message is not related to a operating system error. + @param severity One of "ABORT", "FATAL", "FAILURE", "SORRY", "WARNING", + "HINT", "NOTE", "UPDATE", "DEBUG". Defaults to "FATAL". + @param origin Submit 0 for now. + @return 1 if message was delivered, <=0 if failure + + @since 0.6.4 +*/ +int iso_msgs_submit(int error_code, char msg_text[], int os_errno, + char severity[], int origin); + + +/** Convert a severity name into a severity number, which gives the severity + rank of the name. + @since 0.6.4 + @param severity_name A name as with iso_msgs_submit(), e.g. "SORRY". + @param severity_number The rank number: the higher, the more severe. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure + + @since 0.6.4 +*/ +int iso_text_to_sev(char *severity_name, int *severity_number, int flag); + + +/** Convert a severity number into a severity name + @param severity_number The rank number: the higher, the more severe. + @param severity_name A name as with iso_msgs_submit(), e.g. "SORRY". + @param flag Bitfield for control purposes (unused yet, submit 0) + + @since 0.6.4 +*/ +int iso_sev_to_text(int severity_number, char **severity_name, int flag); + + +/** + * Get the id of an IsoImage, used for message reporting. This message id, + * retrieved with iso_obtain_msgs(), can be used to distinguish what + * IsoImage has isssued a given message. + * + * @since 0.6.2 + */ +int iso_image_get_msg_id(IsoImage *image); + +/** + * Get a textual description of a libisofs error. + * + * @since 0.6.2 + */ +const char *iso_error_to_msg(int errcode); + +/** + * Get the severity of a given error code + * @return + * 0x10000000 -> DEBUG + * 0x20000000 -> UPDATE + * 0x30000000 -> NOTE + * 0x40000000 -> HINT + * 0x50000000 -> WARNING + * 0x60000000 -> SORRY + * 0x64000000 -> MISHAP + * 0x68000000 -> FAILURE + * 0x70000000 -> FATAL + * 0x71000000 -> ABORT + * + * @since 0.6.2 + */ +int iso_error_get_severity(int e); + +/** + * Get the priority of a given error. + * @return + * 0x00000000 -> ZERO + * 0x10000000 -> LOW + * 0x20000000 -> MEDIUM + * 0x30000000 -> HIGH + * + * @since 0.6.2 + */ +int iso_error_get_priority(int e); + +/** + * Get the message queue code of a libisofs error. + */ +int iso_error_get_code(int e); + +/** + * Set the minimum error severity that causes a libisofs operation to + * be aborted as soon as possible. + * + * @param severity + * one of "FAILURE", "MISHAP", "SORRY", "WARNING", "HINT", "NOTE". + * Severities greater or equal than FAILURE always cause program to abort. + * Severities under NOTE won't never cause function abort. + * @return + * Previous abort priority on success, < 0 on error. + * + * @since 0.6.2 + */ +int iso_set_abort_severity(char *severity); + +/** + * Return the messenger object handle used by libisofs. This handle + * may be used by related libraries to their own compatible + * messenger objects and thus to direct their messages to the libisofs + * message queue. See also: libburn, API function burn_set_messenger(). + * + * @return the handle. Do only use with compatible + * + * @since 0.6.2 + */ +void *iso_get_messenger(); + +/** + * Take a ref to the given IsoFileSource. + * + * @since 0.6.2 + */ +void iso_file_source_ref(IsoFileSource *src); + +/** + * Drop your ref to the given IsoFileSource, eventually freeing the associated + * system resources. + * + * @since 0.6.2 + */ +void iso_file_source_unref(IsoFileSource *src); + +/* + * this are just helpers to invoque methods in class + */ + +/** + * Get the path, relative to the filesystem this file source + * belongs to. + * + * @return + * the path of the FileSource inside the filesystem, it should be + * freed when no more needed. + * + * @since 0.6.2 + */ +char* iso_file_source_get_path(IsoFileSource *src); + +/** + * Get the name of the file, with the dir component of the path. + * + * @return + * the name of the file, it should be freed when no more needed. + * + * @since 0.6.2 + */ +char* iso_file_source_get_name(IsoFileSource *src); + +/** + * Get information about the file. + * @return + * 1 success, < 0 error + * Error codes: + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_OUT_OF_MEM + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * + * @since 0.6.2 + */ +int iso_file_source_lstat(IsoFileSource *src, struct stat *info); + +/** + * Check if the process has access to read file contents. Note that this + * is not necessarily related with (l)stat functions. For example, in a + * filesystem implementation to deal with an ISO image, if the user has + * read access to the image it will be able to read all files inside it, + * despite of the particular permission of each file in the RR tree, that + * are what the above functions return. + * + * @return + * 1 if process has read access, < 0 on error + * Error codes: + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_OUT_OF_MEM + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * + * @since 0.6.2 + */ +int iso_file_source_access(IsoFileSource *src); + +/** + * Get information about the file. If the file is a symlink, the info + * returned refers to the destination. + * + * @return + * 1 success, < 0 error + * Error codes: + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_OUT_OF_MEM + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * + * @since 0.6.2 + */ +int iso_file_source_stat(IsoFileSource *src, struct stat *info); + +/** + * Opens the source. + * @return 1 on success, < 0 on error + * Error codes: + * ISO_FILE_ALREADY_OPENNED + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_OUT_OF_MEM + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * + * @since 0.6.2 + */ +int iso_file_source_open(IsoFileSource *src); + +/** + * Close a previuously openned file + * @return 1 on success, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_FILE_NOT_OPENNED + * + * @since 0.6.2 + */ +int iso_file_source_close(IsoFileSource *src); + +/** + * Attempts to read up to count bytes from the given source into + * the buffer starting at buf. + * + * The file src must be open() before calling this, and close() when no + * more needed. Not valid for dirs. On symlinks it reads the destination + * file. + * + * @param src + * The given source + * @param buf + * Pointer to a buffer of at least count bytes where the read data will be + * stored + * @param count + * Bytes to read + * @return + * number of bytes read, 0 if EOF, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_FILE_NOT_OPENNED + * ISO_WRONG_ARG_VALUE -> if count == 0 + * ISO_FILE_IS_DIR + * ISO_OUT_OF_MEM + * ISO_INTERRUPTED + * + * @since 0.6.2 + */ +int iso_file_source_read(IsoFileSource *src, void *buf, size_t count); + +/** + * Read a directory. + * + * Each call to this function will return a new children, until we reach + * the end of file (i.e, no more children), in that case it returns 0. + * + * The dir must be open() before calling this, and close() when no more + * needed. Only valid for dirs. + * + * Note that "." and ".." children MUST NOT BE returned. + * + * @param child + * pointer to be filled with the given child. Undefined on error or OEF + * @return + * 1 on success, 0 if EOF (no more children), < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_FILE_NOT_OPENNED + * ISO_FILE_IS_NOT_DIR + * ISO_OUT_OF_MEM + * + * @since 0.6.2 + */ +int iso_file_source_readdir(IsoFileSource *src, IsoFileSource **child); + +/** + * Read the destination of a symlink. You don't need to open the file + * to call this. + * + * @param src + * An IsoFileSource corresponding to a symbolic link. + * @param buf + * allocated buffer of at least bufsiz bytes. + * The dest. will be copied there, and it will be NULL-terminated + * @param bufsiz + * characters to be copied. Destination link will be truncated if + * it is larger than given size. This include the '\0' character. + * @return + * 1 on success, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_WRONG_ARG_VALUE -> if bufsiz <= 0 + * ISO_FILE_IS_NOT_SYMLINK + * ISO_OUT_OF_MEM + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * + * @since 0.6.2 + */ +int iso_file_source_readlink(IsoFileSource *src, char *buf, size_t bufsiz); + +/** + * Get the filesystem for this source. No extra ref is added, so you + * musn't unref the IsoFilesystem. + * + * @return + * The filesystem, NULL on error + * + * @since 0.6.2 + */ +IsoFilesystem* iso_file_source_get_filesystem(IsoFileSource *src); + +/** + * Take a ref to the given IsoFilesystem + * + * @since 0.6.2 + */ +void iso_filesystem_ref(IsoFilesystem *fs); + +/** + * Drop your ref to the given IsoFilesystem, evetually freeing associated + * resources. + * + * @since 0.6.2 + */ +void iso_filesystem_unref(IsoFilesystem *fs); + +/** + * Create a new IsoFilesystem to access a existent ISO image. + * + * @param src + * Data source to access data. + * @param opts + * Image read options + * @param msgid + * An image identifer, obtained with iso_image_get_msg_id(), used to + * associated messages issued by the filesystem implementation with an + * existent image. If you are not using this filesystem in relation with + * any image context, just use 0x1fffff as the value for this parameter. + * @param fs + * Will be filled with a pointer to the filesystem that can be used + * to access image contents. + * @param + * 1 on success, < 0 on error + * + * @since 0.6.2 + */ +int iso_image_filesystem_new(IsoDataSource *src, IsoReadOpts *opts, int msgid, + IsoImageFilesystem **fs); + +/** + * Get the volset identifier for an existent image. The returned string belong + * to the IsoImageFilesystem and shouldn't be free() nor modified. + * + * @since 0.6.2 + */ +const char *iso_image_fs_get_volset_id(IsoImageFilesystem *fs); + +/** + * Get the volume identifier for an existent image. The returned string belong + * to the IsoImageFilesystem and shouldn't be free() nor modified. + * + * @since 0.6.2 + */ +const char *iso_image_fs_get_volume_id(IsoImageFilesystem *fs); + +/** + * Get the publisher identifier for an existent image. The returned string + * belong to the IsoImageFilesystem and shouldn't be free() nor modified. + * + * @since 0.6.2 + */ +const char *iso_image_fs_get_publisher_id(IsoImageFilesystem *fs); + +/** + * Get the data preparer identifier for an existent image. The returned string + * belong to the IsoImageFilesystem and shouldn't be free() nor modified. + * + * @since 0.6.2 + */ +const char *iso_image_fs_get_data_preparer_id(IsoImageFilesystem *fs); + +/** + * Get the system identifier for an existent image. The returned string belong + * to the IsoImageFilesystem and shouldn't be free() nor modified. + * + * @since 0.6.2 + */ +const char *iso_image_fs_get_system_id(IsoImageFilesystem *fs); + +/** + * Get the application identifier for an existent image. The returned string + * belong to the IsoImageFilesystem and shouldn't be free() nor modified. + * + * @since 0.6.2 + */ +const char *iso_image_fs_get_application_id(IsoImageFilesystem *fs); + +/** + * Get the copyright file identifier for an existent image. The returned string + * belong to the IsoImageFilesystem and shouldn't be free() nor modified. + * + * @since 0.6.2 + */ +const char *iso_image_fs_get_copyright_file_id(IsoImageFilesystem *fs); + +/** + * Get the abstract file identifier for an existent image. The returned string + * belong to the IsoImageFilesystem and shouldn't be free() nor modified. + * + * @since 0.6.2 + */ +const char *iso_image_fs_get_abstract_file_id(IsoImageFilesystem *fs); + +/** + * Get the biblio file identifier for an existent image. The returned string + * belong to the IsoImageFilesystem and shouldn't be free() nor modified. + * + * @since 0.6.2 + */ +const char *iso_image_fs_get_biblio_file_id(IsoImageFilesystem *fs); + +/************ Error codes and return values for libisofs ********************/ + +/** successfully execution */ +#define ISO_SUCCESS 1 + +/** + * special return value, it could be or not an error depending on the + * context. + */ +#define ISO_NONE 0 + +/** Operation canceled (FAILURE,HIGH, -1) */ +#define ISO_CANCELED 0xE830FFFF + +/** Unknown or unexpected fatal error (FATAL,HIGH, -2) */ +#define ISO_FATAL_ERROR 0xF030FFFE + +/** Unknown or unexpected error (FAILURE,HIGH, -3) */ +#define ISO_ERROR 0xE830FFFD + +/** Internal programming error. Please report this bug (FATAL,HIGH, -4) */ +#define ISO_ASSERT_FAILURE 0xF030FFFC + +/** + * NULL pointer as value for an arg. that doesn't allow NULL (FAILURE,HIGH, -5) + */ +#define ISO_NULL_POINTER 0xE830FFFB + +/** Memory allocation error (FATAL,HIGH, -6) */ +#define ISO_OUT_OF_MEM 0xF030FFFA + +/** Interrupted by a signal (FATAL,HIGH, -7) */ +#define ISO_INTERRUPTED 0xF030FFF9 + +/** Invalid parameter value (FAILURE,HIGH, -8) */ +#define ISO_WRONG_ARG_VALUE 0xE830FFF8 + +/** Can't create a needed thread (FATAL,HIGH, -9) */ +#define ISO_THREAD_ERROR 0xF030FFF7 + +/** Write error (FAILURE,HIGH, -10) */ +#define ISO_WRITE_ERROR 0xE830FFF6 + +/** Buffer read error (FAILURE,HIGH, -11) */ +#define ISO_BUF_READ_ERROR 0xE830FFF5 + +/** Trying to add to a dir a node already added to a dir (FAILURE,HIGH, -64) */ +#define ISO_NODE_ALREADY_ADDED 0xE830FFC0 + +/** Node with same name already exists (FAILURE,HIGH, -65) */ +#define ISO_NODE_NAME_NOT_UNIQUE 0xE830FFBF + +/** Trying to remove a node that was not added to dir (FAILURE,HIGH, -65) */ +#define ISO_NODE_NOT_ADDED_TO_DIR 0xE830FFBE + +/** A requested node does not exist (FAILURE,HIGH, -66) */ +#define ISO_NODE_DOESNT_EXIST 0xE830FFBD + +/** + * Try to set the boot image of an already bootable image (FAILURE,HIGH, -67) + */ +#define ISO_IMAGE_ALREADY_BOOTABLE 0xE830FFBC + +/** Trying to use an invalid file as boot image (FAILURE,HIGH, -68) */ +#define ISO_BOOT_IMAGE_NOT_VALID 0xE830FFBB + +/** + * Error on file operation (FAILURE,HIGH, -128) + * (take a look at more specified error codes below) + */ +#define ISO_FILE_ERROR 0xE830FF80 + +/** Trying to open an already openned file (FAILURE,HIGH, -129) */ +#define ISO_FILE_ALREADY_OPENNED 0xE830FF7F + +/** Access to file is not allowed (FAILURE,HIGH, -130) */ +#define ISO_FILE_ACCESS_DENIED 0xE830FF7E + +/** Incorrect path to file (FAILURE,HIGH, -131) */ +#define ISO_FILE_BAD_PATH 0xE830FF7D + +/** The file does not exist in the filesystem (FAILURE,HIGH, -132) */ +#define ISO_FILE_DOESNT_EXIST 0xE830FF7C + +/** Trying to read or close a file not openned (FAILURE,HIGH, -133) */ +#define ISO_FILE_NOT_OPENNED 0xE830FF7B + +/** Directory used where no dir is expected (FAILURE,HIGH, -134) */ +#define ISO_FILE_IS_DIR 0xE830FF7A + +/** Read error (FAILURE,HIGH, -135) */ +#define ISO_FILE_READ_ERROR 0xE830FF79 + +/** Not dir used where a dir is expected (FAILURE,HIGH, -136) */ +#define ISO_FILE_IS_NOT_DIR 0xE830FF78 + +/** Not symlink used where a symlink is expected (FAILURE,HIGH, -137) */ +#define ISO_FILE_IS_NOT_SYMLINK 0xE830FF77 + +/** Can't seek to specified location (FAILURE,HIGH, -138) */ +#define ISO_FILE_SEEK_ERROR 0xE830FF76 + +/** File not supported in ECMA-119 tree and thus ignored (HINT,MEDIUM, -139) */ +#define ISO_FILE_IGNORED 0xC020FF75 + +/* A file is bigger than supported by used standard (HINT,MEDIUM, -140) */ +#define ISO_FILE_TOO_BIG 0xC020FF74 + +/* File read error during image creation (MISHAP,HIGH, -141) */ +#define ISO_FILE_CANT_WRITE 0xE430FF73 + +/* Can't convert filename to requested charset (HINT,MEDIUM, -142) */ +#define ISO_FILENAME_WRONG_CHARSET 0xC020FF72 + +/* File can't be added to the tree (SORRY,HIGH, -143) */ +#define ISO_FILE_CANT_ADD 0xE030FF71 + +/** + * File path break specification constraints and will be ignored + * (HINT,MEDIUM, -141) + */ +#define ISO_FILE_IMGPATH_WRONG 0xC020FF70 + +/** Charset conversion error (FAILURE,HIGH, -256) */ +#define ISO_CHARSET_CONV_ERROR 0xE830FF00 + +/** + * Too much files to mangle, i.e. we cannot guarantee unique file names + * (FAILURE,HIGH, -257) + */ +#define ISO_MANGLE_TOO_MUCH_FILES 0xE830FEFF + +/* image related errors */ + +/** + * Wrong or damaged Primary Volume Descriptor (FAILURE,HIGH, -320) + * This could mean that the file is not a valid ISO image. + */ +#define ISO_WRONG_PVD 0xE830FEC0 + +/** Wrong or damaged RR entry (SORRY,HIGH, -321) */ +#define ISO_WRONG_RR 0xE030FEBF + +/** Unsupported RR feature (SORRY,HIGH, -322) */ +#define ISO_UNSUPPORTED_RR 0xE030FEBE + +/** Wrong or damaged ECMA-119 (FAILURE,HIGH, -323) */ +#define ISO_WRONG_ECMA119 0xE830FEBD + +/** Unsupported ECMA-119 feature (FAILURE,HIGH, -324) */ +#define ISO_UNSUPPORTED_ECMA119 0xE830FEBC + +/** Wrong or damaged El-Torito catalog (SORRY,HIGH, -325) */ +#define ISO_WRONG_EL_TORITO 0xE030FEBB + +/** Unsupported El-Torito feature (SORRY,HIGH, -326) */ +#define ISO_UNSUPPORTED_EL_TORITO 0xE030FEBA + +/** Can't patch an isolinux boot image (SORRY,HIGH, -327) */ +#define ISO_ISOLINUX_CANT_PATCH 0xE030FEB9 + +/** Unsupported SUSP feature (SORRY,HIGH, -328) */ +#define ISO_UNSUPPORTED_SUSP 0xE030FEB8 + +/** Error on a RR entry that can be ignored (WARNING,HIGH, -329) */ +#define ISO_WRONG_RR_WARN 0xD030FEB7 + +/** Error on a RR entry that can be ignored (HINT,MEDIUM, -330) */ +#define ISO_SUSP_UNHANDLED 0xC020FEB6 + +/** Multiple ER SUSP entries found (WARNING,HIGH, -331) */ +#define ISO_SUSP_MULTIPLE_ER 0xD030FEB5 + +/** Unsupported volume descriptor found (HINT,MEDIUM, -332) */ +#define ISO_UNSUPPORTED_VD 0xC020FEB4 + +/** El-Torito related warning (WARNING,HIGH, -333) */ +#define ISO_EL_TORITO_WARN 0xD030FEB3 + +/** Image write cancelled (MISHAP,HIGH, -334) */ +#define ISO_IMAGE_WRITE_CANCELED 0xE430FEB2 + +/** El-Torito image is hidden (WARNING,HIGH, -335) */ +#define ISO_EL_TORITO_HIDDEN 0xD030FEB1 + +#endif /*LIBISO_LIBISOFS_H_*/ diff --git a/libisofs/branches/thomas/libisofs/messages.c b/libisofs/branches/thomas/libisofs/messages.c new file mode 100644 index 00000000..064c4411 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/messages.c @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ +#include +#include +#include +#include + +#include "libiso_msgs.h" +#include "libisofs.h" +#include "messages.h" + +/* + * error codes are 32 bit numbers, that follow the following conventions: + * + * bit 31 (MSB) -> 1 (to make the value always negative) + * bits 30-24 -> Encoded severity (Use ISO_ERR_SEV to translate an error code + * to a LIBISO_MSGS_SEV_* constant) + * = 0x10 -> DEBUG + * = 0x20 -> UPDATE + * = 0x30 -> NOTE + * = 0x40 -> HINT + * = 0x50 -> WARNING + * = 0x60 -> SORRY + * = 0x64 -> MISHAP + * = 0x68 -> FAILURE + * = 0x70 -> FATAL + * = 0x71 -> ABORT + * bits 23-20 -> Encoded priority (Use ISO_ERR_PRIO to translate an error code + * to a LIBISO_MSGS_PRIO_* constant) + * = 0x0 -> ZERO + * = 0x1 -> LOW + * = 0x2 -> MEDIUM + * = 0x3 -> HIGH + * bits 19-16 -> Reserved for future usage (maybe message ranges) + * bits 15-0 -> Error code + */ +#define ISO_ERR_SEV(e) (e & 0x7F000000) +#define ISO_ERR_PRIO(e) ((e & 0x00F00000) << 8) +#define ISO_ERR_CODE(e) ((e & 0x0000FFFF) | 0x00030000) + +int iso_message_id = LIBISO_MSGS_ORIGIN_IMAGE_BASE; + +/** + * Threshold for aborting. + */ +int abort_threshold = LIBISO_MSGS_SEV_FAILURE; + +#define MAX_MSG_LEN 4096 + +struct libiso_msgs *libiso_msgr = NULL; + +int iso_init() +{ + if (libiso_msgr == NULL) { + if (libiso_msgs_new(&libiso_msgr, 0) <= 0) + return ISO_FATAL_ERROR; + } + libiso_msgs_set_severities(libiso_msgr, LIBISO_MSGS_SEV_NEVER, + LIBISO_MSGS_SEV_FATAL, "libisofs: ", 0); + return 1; +} + +void iso_finish() +{ + libiso_msgs_destroy(&libiso_msgr, 0); +} + +int iso_set_abort_severity(char *severity) +{ + int ret, sevno; + + ret = libiso_msgs__text_to_sev(severity, &sevno, 0); + if (ret <= 0) + return ISO_WRONG_ARG_VALUE; + if (sevno > LIBISO_MSGS_SEV_FAILURE || sevno < LIBISO_MSGS_SEV_NOTE) + return ISO_WRONG_ARG_VALUE; + ret = abort_threshold; + abort_threshold = sevno; + return ret; +} + +void iso_msg_debug(int imgid, const char *fmt, ...) +{ + char msg[MAX_MSG_LEN]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(msg, MAX_MSG_LEN, fmt, ap); + va_end(ap); + + libiso_msgs_submit(libiso_msgr, imgid, 0x00000002, LIBISO_MSGS_SEV_DEBUG, + LIBISO_MSGS_PRIO_ZERO, msg, 0, 0); +} + +const char *iso_error_to_msg(int errcode) +{ + switch(errcode) { + case ISO_CANCELED: + return "Operation canceled"; + case ISO_FATAL_ERROR: + return "Unknown or unexpected fatal error"; + case ISO_ERROR: + return "Unknown or unexpected error"; + case ISO_ASSERT_FAILURE: + return "Internal programming error. Please report this bug"; + case ISO_NULL_POINTER: + return "NULL pointer as value for an arg. that doesn't allow NULL"; + case ISO_OUT_OF_MEM: + return "Memory allocation error"; + case ISO_INTERRUPTED: + return "Interrupted by a signal"; + case ISO_WRONG_ARG_VALUE: + return "Invalid parameter value"; + case ISO_THREAD_ERROR: + return "Can't create a needed thread"; + case ISO_WRITE_ERROR: + return "Write error"; + case ISO_BUF_READ_ERROR: + return "Buffer read error"; + case ISO_NODE_ALREADY_ADDED: + return "Trying to add to a dir a node already added to a dir"; + case ISO_NODE_NAME_NOT_UNIQUE: + return "Node with same name already exists"; + case ISO_NODE_NOT_ADDED_TO_DIR: + return "Trying to remove a node that was not added to dir"; + case ISO_NODE_DOESNT_EXIST: + return "A requested node does not exist"; + case ISO_IMAGE_ALREADY_BOOTABLE: + return "Try to set the boot image of an already bootable image"; + case ISO_BOOT_IMAGE_NOT_VALID: + return "Trying to use an invalid file as boot image"; + case ISO_FILE_ERROR: + return "Error on file operation"; + case ISO_FILE_ALREADY_OPENNED: + return "Trying to open an already openned file"; + case ISO_FILE_ACCESS_DENIED: + return "Access to file is not allowed"; + case ISO_FILE_BAD_PATH: + return "Incorrect path to file"; + case ISO_FILE_DOESNT_EXIST: + return "The file does not exist in the filesystem"; + case ISO_FILE_NOT_OPENNED: + return "Trying to read or close a file not openned"; + case ISO_FILE_IS_DIR: + return "Directory used where no dir is expected"; + case ISO_FILE_READ_ERROR: + return "Read error"; + case ISO_FILE_IS_NOT_DIR: + return "Not dir used where a dir is expected"; + case ISO_FILE_IS_NOT_SYMLINK: + return "Not symlink used where a symlink is expected"; + case ISO_FILE_SEEK_ERROR: + return "Can't seek to specified location"; + case ISO_FILE_IGNORED: + return "File not supported in ECMA-119 tree and thus ignored"; + case ISO_FILE_TOO_BIG: + return "A file is bigger than supported by used standard"; + case ISO_FILE_CANT_WRITE: + return "File read error during image creation"; + case ISO_FILENAME_WRONG_CHARSET: + return "Can't convert filename to requested charset"; + case ISO_FILE_CANT_ADD: + return "File can't be added to the tree"; + case ISO_FILE_IMGPATH_WRONG: + return "File path break specification constraints and will be ignored"; + case ISO_CHARSET_CONV_ERROR: + return "Charset conversion error"; + case ISO_MANGLE_TOO_MUCH_FILES: + return "Too much files to mangle, can't guarantee unique file names"; + case ISO_WRONG_PVD: + return "Wrong or damaged Primary Volume Descriptor"; + case ISO_WRONG_RR: + return "Wrong or damaged RR entry"; + case ISO_UNSUPPORTED_RR: + return "Unsupported RR feature"; + case ISO_WRONG_ECMA119: + return "Wrong or damaged ECMA-119"; + case ISO_UNSUPPORTED_ECMA119: + return "Unsupported ECMA-119 feature"; + case ISO_WRONG_EL_TORITO: + return "Wrong or damaged El-Torito catalog"; + case ISO_UNSUPPORTED_EL_TORITO: + return "Unsupported El-Torito feature"; + case ISO_ISOLINUX_CANT_PATCH: + return "Can't patch isolinux boot image"; + case ISO_UNSUPPORTED_SUSP: + return "Unsupported SUSP feature"; + case ISO_WRONG_RR_WARN: + return "Error on a RR entry that can be ignored"; + case ISO_SUSP_UNHANDLED: + return "Error on a RR entry that can be ignored"; + case ISO_SUSP_MULTIPLE_ER: + return "Multiple ER SUSP entries found"; + case ISO_UNSUPPORTED_VD: + return "Unsupported volume descriptor found"; + case ISO_EL_TORITO_WARN: + return "El-Torito related warning"; + case ISO_IMAGE_WRITE_CANCELED: + return "Image write cancelled"; + case ISO_EL_TORITO_HIDDEN: + return "El-Torito image is hidden"; + default: + return "Unknown error"; + } +} + +int iso_msg_submit(int imgid, int errcode, int causedby, const char *fmt, ...) +{ + char msg[MAX_MSG_LEN]; + va_list ap; + + /* when called with ISO_CANCELED, we don't need to submit any message */ + if (errcode == ISO_CANCELED && fmt == NULL) { + return ISO_CANCELED; + } + + if (fmt) { + va_start(ap, fmt); + vsnprintf(msg, MAX_MSG_LEN, fmt, ap); + va_end(ap); + } else { + strncpy(msg, iso_error_to_msg(errcode), MAX_MSG_LEN); + } + + libiso_msgs_submit(libiso_msgr, imgid, ISO_ERR_CODE(errcode), + ISO_ERR_SEV(errcode), ISO_ERR_PRIO(errcode), msg, 0, 0); + if (causedby != 0) { + snprintf(msg, MAX_MSG_LEN, " > Caused by: %s", + iso_error_to_msg(causedby)); + libiso_msgs_submit(libiso_msgr, imgid, ISO_ERR_CODE(causedby), + LIBISO_MSGS_SEV_NOTE, LIBISO_MSGS_PRIO_LOW, msg, 0, 0); + if (ISO_ERR_SEV(causedby) == LIBISO_MSGS_SEV_FATAL) { + return ISO_CANCELED; + } + } + + if (ISO_ERR_SEV(errcode) >= abort_threshold) { + return ISO_CANCELED; + } else { + return 0; + } +} + +/** + * Control queueing and stderr printing of messages from libisofs. + * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", + * "NOTE", "UPDATE", "DEBUG", "ALL". + * + * @param queue_severity Gives the minimum limit for messages to be queued. + * Default: "NEVER". If you queue messages then you + * must consume them by iso_msgs_obtain(). + * @param print_severity Does the same for messages to be printed directly + * to stderr. + * @param print_id A text prefix to be printed before the message. + * @return >0 for success, <=0 for error + */ +int iso_set_msgs_severities(char *queue_severity, char *print_severity, + char *print_id) +{ + int ret, queue_sevno, print_sevno; + + ret = libiso_msgs__text_to_sev(queue_severity, &queue_sevno, 0); + if (ret <= 0) + return 0; + ret = libiso_msgs__text_to_sev(print_severity, &print_sevno, 0); + if (ret <= 0) + return 0; + ret = libiso_msgs_set_severities(libiso_msgr, queue_sevno, print_sevno, + print_id, 0); + if (ret <= 0) + return 0; + return 1; +} + +/** + * Obtain the oldest pending libisofs message from the queue which has at + * least the given minimum_severity. This message and any older message of + * lower severity will get discarded from the queue and is then lost forever. + * + * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", + * "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER" + * will discard the whole queue. + * + * @param error_code Will become a unique error code as listed in messages.h + * @param imgid Id of the image that was issued the message. + * @param msg_text Must provide at least ISO_MSGS_MESSAGE_LEN bytes. + * @param severity Will become the severity related to the message and + * should provide at least 80 bytes. + * @return 1 if a matching item was found, 0 if not, <0 for severe errors + */ +int iso_obtain_msgs(char *minimum_severity, int *error_code, int *imgid, + char msg_text[], char severity[]) +{ + int ret, minimum_sevno, sevno, priority, os_errno; + double timestamp; + pid_t pid; + char *textpt, *sev_name; + struct libiso_msgs_item *item= NULL; + + ret = libiso_msgs__text_to_sev(minimum_severity, &minimum_sevno, 0); + if (ret <= 0) + return 0; + ret = libiso_msgs_obtain(libiso_msgr, &item, minimum_sevno, + LIBISO_MSGS_PRIO_ZERO, 0); + if (ret <= 0) + goto ex; + ret = libiso_msgs_item_get_msg(item, error_code, &textpt, &os_errno, 0); + if (ret <= 0) + goto ex; + strncpy(msg_text, textpt, ISO_MSGS_MESSAGE_LEN-1); + if (strlen(textpt) >= ISO_MSGS_MESSAGE_LEN) + msg_text[ISO_MSGS_MESSAGE_LEN-1] = 0; + + ret = libiso_msgs_item_get_origin(item, ×tamp, &pid, imgid, 0); + if (ret <= 0) + goto ex; + + severity[0]= 0; + ret = libiso_msgs_item_get_rank(item, &sevno, &priority, 0); + if (ret <= 0) + goto ex; + ret = libiso_msgs__sev_to_text(sevno, &sev_name, 0); + if (ret <= 0) + goto ex; + strcpy(severity, sev_name); + + ret = 1; + ex: ; + libiso_msgs_destroy_item(libiso_msgr, &item, 0); + return ret; +} + + +/* ts A80222 : derived from libburn/init.c:burn_msgs_submit() +*/ +int iso_msgs_submit(int error_code, char msg_text[], int os_errno, + char severity[], int origin) +{ + int ret, sevno; + + ret = libiso_msgs__text_to_sev(severity, &sevno, 0); + if (ret <= 0) + sevno = LIBISO_MSGS_SEV_ALL; + if (error_code <= 0) { + switch(sevno) { + case LIBISO_MSGS_SEV_ABORT: error_code = 0x00040000; + break; case LIBISO_MSGS_SEV_FATAL: error_code = 0x00040001; + break; case LIBISO_MSGS_SEV_SORRY: error_code = 0x00040002; + break; case LIBISO_MSGS_SEV_WARNING: error_code = 0x00040003; + break; case LIBISO_MSGS_SEV_HINT: error_code = 0x00040004; + break; case LIBISO_MSGS_SEV_NOTE: error_code = 0x00040005; + break; case LIBISO_MSGS_SEV_UPDATE: error_code = 0x00040006; + break; case LIBISO_MSGS_SEV_DEBUG: error_code = 0x00040007; + break; default: error_code = 0x00040008; + } + } + ret = libiso_msgs_submit(libiso_msgr, origin, error_code, + sevno, LIBISO_MSGS_PRIO_HIGH, msg_text, os_errno, 0); + return ret; +} + + +/* ts A80222 : derived from libburn/init.c:burn_text_to_sev() +*/ +int iso_text_to_sev(char *severity_name, int *sevno, int flag) +{ + int ret; + + ret = libiso_msgs__text_to_sev(severity_name, sevno, 0); + if (ret <= 0) + *sevno = LIBISO_MSGS_SEV_FATAL; + return ret; +} + + +/* ts A80222 : derived from libburn/init.c:burn_sev_to_text() +*/ +int iso_sev_to_text(int severity_number, char **severity_name, int flag) +{ + int ret; + + ret = libiso_msgs__sev_to_text(severity_number, severity_name, 0); + return ret; +} + + +/** + * Return the messenger object handle used by libisofs. This handle + * may be used by related libraries to their own compatible + * messenger objects and thus to direct their messages to the libisofs + * message queue. See also: libburn, API function burn_set_messenger(). + * + * @return the handle. Do only use with compatible + */ +void *iso_get_messenger() +{ + return libiso_msgr; +} + +int iso_error_get_severity(int e) +{ + return ISO_ERR_SEV(e); +} + +int iso_error_get_priority(int e) +{ + return ISO_ERR_PRIO(e); +} + +int iso_error_get_code(int e) +{ + return ISO_ERR_CODE(e); +} + + +/* ts A80222 */ +int iso_report_errfile(char *path, int error_code, int os_errno, int flag) +{ + libiso_msgs_submit(libiso_msgr, 0, error_code, + LIBISO_MSGS_SEV_ERRFILE, LIBISO_MSGS_PRIO_HIGH, + path, os_errno, 0); + return(1); +} diff --git a/libisofs/branches/thomas/libisofs/messages.h b/libisofs/branches/thomas/libisofs/messages.h new file mode 100644 index 00000000..dc8b98c2 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/messages.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/* + * Message handling for libisofs + */ + +#ifndef MESSAGES_H_ +#define MESSAGES_H_ + +/** + * Take and increment this variable to get a valid identifier for message + * origin. + */ +extern int iso_message_id; + +/** + * Submit a debug message. + */ +void iso_msg_debug(int imgid, const char *fmt, ...); + +/** + * + * @param errcode + * The error code. + * @param causedby + * Error that was caused the errcode. If this error is a FATAL error, + * < 0 will be returned in any case. Use 0 if there is no previous + * cause for the error. + * @return + * 1 on success, < 0 if function must abort asap. + */ +int iso_msg_submit(int imgid, int errcode, int causedby, const char *fmt, ...); + + +/* ts A80222 */ +/* To be called with events which report incidents with individual input + files from the local filesystem. Not with image nodes, files containing an + image or similar file-like objects. +*/ +int iso_report_errfile(char *path, int error_code, int os_errno, int flag); + + +#endif /*MESSAGES_H_*/ diff --git a/libisofs/branches/thomas/libisofs/node.c b/libisofs/branches/thomas/libisofs/node.c new file mode 100644 index 00000000..25dfcc07 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/node.c @@ -0,0 +1,884 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "libisofs.h" +#include "node.h" +#include "stream.h" + +#include +#include +#include +#include + +/** + * Increments the reference counting of the given node. + */ +void iso_node_ref(IsoNode *node) +{ + ++node->refcount; +} + +/** + * Decrements the reference couting of the given node. + * If it reach 0, the node is free, and, if the node is a directory, + * its children will be unref() too. + */ +void iso_node_unref(IsoNode *node) +{ + if (--node->refcount == 0) { + switch (node->type) { + case LIBISO_DIR: + { + IsoNode *child = ((IsoDir*)node)->children; + while (child != NULL) { + IsoNode *tmp = child->next; + child->parent = NULL; + iso_node_unref(child); + child = tmp; + } + } + break; + case LIBISO_FILE: + { + IsoFile *file = (IsoFile*) node; + iso_stream_unref(file->stream); + } + break; + case LIBISO_SYMLINK: + { + IsoSymlink *link = (IsoSymlink*) node; + free(link->dest); + } + default: + /* other kind of nodes does not need to delete anything here */ + break; + } + +#ifdef LIBISO_EXTENDED_INFORMATION + if (node->xinfo) { + /* free extended info */ + node->xinfo->process(node->xinfo->data, 1); + free(node->xinfo); + } +#endif + free(node->name); + free(node); + } +} + +/** + * Get the type of an IsoNode. + */ +enum IsoNodeType iso_node_get_type(IsoNode *node) +{ + return node->type; +} + +/** + * Set the name of a node. + * + * @param name The name in UTF-8 encoding + */ +int iso_node_set_name(IsoNode *node, const char *name) +{ + char *new; + + if ((IsoNode*)node->parent == node) { + /* you can't change name of the root node */ + return ISO_WRONG_ARG_VALUE; + } + + /* check if the name is valid */ + if (!iso_node_is_valid_name(name)) { + return ISO_WRONG_ARG_VALUE; + } + + if (node->parent != NULL) { + /* check if parent already has a node with same name */ + if (iso_dir_get_node(node->parent, name, NULL) == 1) { + return ISO_NODE_NAME_NOT_UNIQUE; + } + } + + new = strdup(name); + if (new == NULL) { + return ISO_OUT_OF_MEM; + } + free(node->name); + node->name = new; + if (node->parent != NULL) { + IsoDir *parent; + int res; + /* take and add again to ensure correct children order */ + parent = node->parent; + iso_node_take(node); + res = iso_dir_add_node(parent, node, 0); + if (res < 0) { + return res; + } + } + return ISO_SUCCESS; +} + +/** + * Get the name of a node (in UTF-8). + * The returned string belongs to the node and should not be modified nor + * freed. Use strdup if you really need your own copy. + */ +const char *iso_node_get_name(const IsoNode *node) +{ + return node->name; +} + +/** + * Set the permissions for the node. This attribute is only useful when + * Rock Ridge extensions are enabled. + * + * @param mode + * bitmask with the permissions of the node, as specified in 'man 2 stat'. + * The file type bitfields will be ignored, only file permissions will be + * modified. + */ +void iso_node_set_permissions(IsoNode *node, mode_t mode) +{ + node->mode = (node->mode & S_IFMT) | (mode & ~S_IFMT); +} + +/** + * Get the permissions for the node + */ +mode_t iso_node_get_permissions(const IsoNode *node) +{ + return node->mode & ~S_IFMT; +} + +/** + * Get the mode of the node, both permissions and file type, as specified in + * 'man 2 stat'. + */ +mode_t iso_node_get_mode(const IsoNode *node) +{ + return node->mode; +} + +/** + * Set the user id for the node. This attribute is only useful when + * Rock Ridge extensions are enabled. + */ +void iso_node_set_uid(IsoNode *node, uid_t uid) +{ + node->uid = uid; +} + +/** + * Get the user id of the node. + */ +uid_t iso_node_get_uid(const IsoNode *node) +{ + return node->uid; +} + +/** + * Set the group id for the node. This attribute is only useful when + * Rock Ridge extensions are enabled. + */ +void iso_node_set_gid(IsoNode *node, gid_t gid) +{ + node->gid = gid; +} + +/** + * Get the group id of the node. + */ +gid_t iso_node_get_gid(const IsoNode *node) +{ + return node->gid; +} + +/** + * Set the time of last modification of the file + */ +void iso_node_set_mtime(IsoNode *node, time_t time) +{ + node->mtime = time; +} + +/** + * Get the time of last modification of the file + */ +time_t iso_node_get_mtime(const IsoNode *node) +{ + return node->mtime; +} + +/** + * Set the time of last access to the file + */ +void iso_node_set_atime(IsoNode *node, time_t time) +{ + node->atime = time; +} + +/** + * Get the time of last access to the file + */ +time_t iso_node_get_atime(const IsoNode *node) +{ + return node->atime; +} + +/** + * Set the time of last status change of the file + */ +void iso_node_set_ctime(IsoNode *node, time_t time) +{ + node->ctime = time; +} + +/** + * Get the time of last status change of the file + */ +time_t iso_node_get_ctime(const IsoNode *node) +{ + return node->ctime; +} + +void iso_node_set_hidden(IsoNode *node, int hide_attrs) +{ + /* you can't hide root node */ + if ((IsoNode*)node->parent != node) { + node->hidden = hide_attrs; + } +} + +/** + * Add a new node to a dir. Note that this function don't add a new ref to + * the node, so you don't need to free it, it will be automatically freed + * when the dir is deleted. Of course, if you want to keep using the node + * after the dir life, you need to iso_node_ref() it. + * + * @param dir + * the dir where to add the node + * @param child + * the node to add. You must ensure that the node hasn't previously added + * to other dir, and that the node name is unique inside the child. + * Otherwise this function will return a failure, and the child won't be + * inserted. + * @param replace + * if the dir already contains a node with the same name, whether to + * replace or not the old node with this. + * @return + * number of nodes in dir if succes, < 0 otherwise + */ +int iso_dir_add_node(IsoDir *dir, IsoNode *child, + enum iso_replace_mode replace) +{ + IsoNode **pos; + + if (dir == NULL || child == NULL) { + return ISO_NULL_POINTER; + } + if ((IsoNode*)dir == child) { + return ISO_WRONG_ARG_VALUE; + } + + /* + * check if child is already added to another dir, or if child + * is the root node, where parent == itself + */ + if (child->parent != NULL || child->parent == (IsoDir*)child) { + return ISO_NODE_ALREADY_ADDED; + } + + iso_dir_find(dir, child->name, &pos); + return iso_dir_insert(dir, child, pos, replace); +} + +/** + * Locate a node inside a given dir. + * + * @param name + * The name of the node + * @param node + * Location for a pointer to the node, it will filled with NULL if the dir + * doesn't have a child with the given name. + * The node will be owned by the dir and shouldn't be unref(). Just call + * iso_node_ref() to get your own reference to the node. + * Note that you can pass NULL is the only thing you want to do is check + * if a node with such name already exists on dir. + * @return + * 1 node found, 0 child has no such node, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if dir or name are NULL + */ +int iso_dir_get_node(IsoDir *dir, const char *name, IsoNode **node) +{ + int ret; + IsoNode **pos; + if (dir == NULL || name == NULL) { + return ISO_NULL_POINTER; + } + + ret = iso_dir_exists(dir, name, &pos); + if (ret == 0) { + if (node) { + *node = NULL; + } + return 0; /* node not found */ + } + + if (node) { + *node = *pos; + } + return 1; +} + +/** + * Get the number of children of a directory. + * + * @return + * >= 0 number of items, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if dir is NULL + */ +int iso_dir_get_children_count(IsoDir *dir) +{ + if (dir == NULL) { + return ISO_NULL_POINTER; + } + return dir->nchildren; +} + +int iso_dir_get_children(const IsoDir *dir, IsoDirIter **iter) +{ + IsoDirIter *it; + + if (dir == NULL || iter == NULL) { + return ISO_NULL_POINTER; + } + it = malloc(sizeof(IsoDirIter)); + if (it == NULL) { + return ISO_OUT_OF_MEM; + } + + it->dir = dir; + it->pos = dir->children; + + *iter = it; + return ISO_SUCCESS; +} + +int iso_dir_iter_next(IsoDirIter *iter, IsoNode **node) +{ + IsoNode *n; + if (iter == NULL || node == NULL) { + return ISO_NULL_POINTER; + } + n = iter->pos; + if (n == NULL) { + *node = NULL; + return 0; + } + if (n->parent != iter->dir) { + /* this can happen if the node has been moved to another dir */ + return ISO_ERROR; + } + *node = n; + iter->pos = n->next; + return ISO_SUCCESS; +} + +/** + * Check if there're more children. + * + * @return + * 1 dir has more elements, 0 no, < 0 error + * Possible errors: + * ISO_NULL_POINTER, if iter is NULL + */ +int iso_dir_iter_has_next(IsoDirIter *iter) +{ + if (iter == NULL) { + return ISO_NULL_POINTER; + } + return iter->pos == NULL ? 0 : 1; +} + +void iso_dir_iter_free(IsoDirIter *iter) +{ + free(iter); +} + +static IsoNode** iso_dir_find_node(IsoDir *dir, IsoNode *node) +{ + IsoNode **pos; + pos = &(dir->children); + while (*pos != NULL && *pos != node) { + pos = &((*pos)->next); + } + return pos; +} + +/** + * Removes a child from a directory. + * The child is not freed, so you will become the owner of the node. Later + * you can add the node to another dir (calling iso_dir_add_node), or free + * it if you don't need it (with iso_node_unref). + * + * @return + * 1 on success, < 0 error + */ +int iso_node_take(IsoNode *node) +{ + IsoNode **pos; + IsoDir* dir; + + if (node == NULL) { + return ISO_NULL_POINTER; + } + dir = node->parent; + if (dir == NULL) { + return ISO_NODE_NOT_ADDED_TO_DIR; + } + pos = iso_dir_find_node(dir, node); + if (pos == NULL) { + /* should never occur */ + return ISO_ERROR; + } + *pos = node->next; + node->parent = NULL; + node->next = NULL; + dir->nchildren--; + return ISO_SUCCESS; +} + +/** + * Removes a child from a directory and free (unref) it. + * If you want to keep the child alive, you need to iso_node_ref() it + * before this call, but in that case iso_node_take() is a better + * alternative. + * + * @return + * 1 on success, < 0 error + */ +int iso_node_remove(IsoNode *node) +{ + int ret; + ret = iso_node_take(node); + if (ret == ISO_SUCCESS) { + iso_node_unref(node); + } + return ret; +} + +/* + * Get the parent of the given iso tree node. No extra ref is added to the + * returned directory, you must take your ref. with iso_node_ref() if you + * need it. + * + * If node is the root node, the same node will be returned as its parent. + * + * This returns NULL if the node doesn't pertain to any tree + * (it was removed/take). + */ +IsoDir *iso_node_get_parent(IsoNode *node) +{ + return node->parent; +} + +/* TODO #00005 optimize iso_dir_iter_take */ +int iso_dir_iter_take(IsoDirIter *iter) +{ + IsoNode *pos; + if (iter == NULL) { + return ISO_NULL_POINTER; + } + + pos = iter->dir->children; + if (iter->pos == pos) { + return ISO_ERROR; + } + while (pos != NULL && pos->next == iter->pos) { + pos = pos->next; + } + if (pos == NULL) { + return ISO_ERROR; + } + return iso_node_take(pos); +} + +int iso_dir_iter_remove(IsoDirIter *iter) +{ + IsoNode *pos; + if (iter == NULL) { + return ISO_NULL_POINTER; + } + pos = iter->dir->children; + if (iter->pos == pos) { + return ISO_ERROR; + } + while (pos != NULL && pos->next == iter->pos) { + pos = pos->next; + } + if (pos == NULL) { + return ISO_ERROR; + } + return iso_node_remove(pos); +} + +/** + * Get the destination of a node. + * The returned string belongs to the node and should not be modified nor + * freed. Use strdup if you really need your own copy. + */ +const char *iso_symlink_get_dest(const IsoSymlink *link) +{ + return link->dest; +} + +/** + * Set the destination of a link. + */ +int iso_symlink_set_dest(IsoSymlink *link, const char *dest) +{ + char *d; + if (!iso_node_is_valid_link_dest(dest)) { + /* guard against null or empty dest */ + return ISO_WRONG_ARG_VALUE; + } + d = strdup(dest); + if (d == NULL) { + return ISO_OUT_OF_MEM; + } + free(link->dest); + link->dest = d; + return ISO_SUCCESS; +} + +/** + * Sets the order in which a node will be written on image. High weihted files + * will be written first, so in a disc them will be written near the center. + * + * @param node + * The node which weight will be changed. If it's a dir, this function + * will change the weight of all its children. For nodes other that dirs + * or regular files, this function has no effect. + * @param w + * The weight as a integer number, the greater this value is, the + * closer from the begining of image the file will be written. + */ +void iso_node_set_sort_weight(IsoNode *node, int w) +{ + if (node->type == LIBISO_DIR) { + IsoNode *child = ((IsoDir*)node)->children; + while (child) { + iso_node_set_sort_weight(child, w); + child = child->next; + } + } else if (node->type == LIBISO_FILE) { + ((IsoFile*)node)->sort_weight = w; + } +} + +/** + * Get the sort weight of a file. + */ +int iso_file_get_sort_weight(IsoFile *file) +{ + return file->sort_weight; +} + +/** + * Get the size of the file, in bytes + */ +off_t iso_file_get_size(IsoFile *file) +{ + return iso_stream_get_size(file->stream); +} + +/** + * Check if a given name is valid for an iso node. + * + * @return + * 1 if yes, 0 if not + */ +int iso_node_is_valid_name(const char *name) +{ + /* a name can't be NULL */ + if (name == NULL) { + return 0; + } + + /* guard against the empty string or big names... */ + if (name[0] == '\0' || strlen(name) > 255) { + return 0; + } + + /* ...against "." and ".." names... */ + if (!strcmp(name, ".") || !strcmp(name, "..")) { + return 0; + } + + /* ...and against names with '/' */ + if (strchr(name, '/') != NULL) { + return 0; + } + return 1; +} + +/** + * Check if a given path is valid for the destination of a link. + * + * @return + * 1 if yes, 0 if not + */ +int iso_node_is_valid_link_dest(const char *dest) +{ + int ret; + char *ptr, *brk_info, *component; + + /* a dest can't be NULL */ + if (dest == NULL) { + return 0; + } + + /* guard against the empty string or big dest... */ + if (dest[0] == '\0' || strlen(dest) > PATH_MAX) { + return 0; + } + + /* check that all components are valid */ + if (!strcmp(dest, "/")) { + /* "/" is a valid component */ + return 1; + } + + ptr = strdup(dest); + if (ptr == NULL) { + return 0; + } + + ret = 1; + component = strtok_r(ptr, "/", &brk_info); + while (component) { + if (strcmp(component, ".") && strcmp(component, "..")) { + ret = iso_node_is_valid_name(component); + if (ret == 0) { + break; + } + } + component = strtok_r(NULL, "/", &brk_info); + } + free(ptr); + + return ret; +} + +void iso_dir_find(IsoDir *dir, const char *name, IsoNode ***pos) +{ + *pos = &(dir->children); + while (**pos != NULL && strcmp((**pos)->name, name) < 0) { + *pos = &((**pos)->next); + } +} + +int iso_dir_exists(IsoDir *dir, const char *name, IsoNode ***pos) +{ + IsoNode **node; + + iso_dir_find(dir, name, &node); + if (pos) { + *pos = node; + } + return (*node != NULL && !strcmp((*node)->name, name)) ? 1 : 0; +} + +int iso_dir_insert(IsoDir *dir, IsoNode *node, IsoNode **pos, + enum iso_replace_mode replace) +{ + if (*pos != NULL && !strcmp((*pos)->name, node->name)) { + /* a node with same name already exists */ + switch(replace) { + case ISO_REPLACE_NEVER: + return ISO_NODE_NAME_NOT_UNIQUE; + case ISO_REPLACE_IF_NEWER: + if ((*pos)->mtime >= node->mtime) { + /* old file is newer */ + return ISO_NODE_NAME_NOT_UNIQUE; + } + break; + case ISO_REPLACE_IF_SAME_TYPE_AND_NEWER: + if ((*pos)->mtime >= node->mtime) { + /* old file is newer */ + return ISO_NODE_NAME_NOT_UNIQUE; + } + /* fall down */ + case ISO_REPLACE_IF_SAME_TYPE: + if ((node->mode & S_IFMT) != ((*pos)->mode & S_IFMT)) { + /* different file types */ + return ISO_NODE_NAME_NOT_UNIQUE; + } + break; + case ISO_REPLACE_ALWAYS: + break; + default: + /* CAN'T HAPPEN */ + return ISO_ASSERT_FAILURE; + } + + /* if we are reach here we have to replace */ + node->next = (*pos)->next; + (*pos)->parent = NULL; + (*pos)->next = NULL; + iso_node_unref(*pos); + *pos = node; + node->parent = dir; + return dir->nchildren; + } + + node->next = *pos; + *pos = node; + node->parent = dir; + + return ++dir->nchildren; +} + +int iso_node_new_root(IsoDir **root) +{ + IsoDir *dir; + + dir = calloc(1, sizeof(IsoDir)); + if (dir == NULL) { + return ISO_OUT_OF_MEM; + } + dir->node.refcount = 1; + dir->node.type = LIBISO_DIR; + dir->node.atime = dir->node.ctime = dir->node.mtime = time(NULL); + dir->node.mode = S_IFDIR | 0555; + + /* set parent to itself, to prevent root to be added to another dir */ + dir->node.parent = dir; + *root = dir; + return ISO_SUCCESS; +} + +int iso_node_new_dir(char *name, IsoDir **dir) +{ + IsoDir *new; + + if (dir == NULL || name == NULL) { + return ISO_NULL_POINTER; + } + + /* check if the name is valid */ + if (!iso_node_is_valid_name(name)) { + return ISO_WRONG_ARG_VALUE; + } + + new = calloc(1, sizeof(IsoDir)); + if (new == NULL) { + return ISO_OUT_OF_MEM; + } + new->node.refcount = 1; + new->node.type = LIBISO_DIR; + new->node.name = name; + new->node.mode = S_IFDIR; + *dir = new; + return ISO_SUCCESS; +} + +int iso_node_new_file(char *name, IsoStream *stream, IsoFile **file) +{ + IsoFile *new; + + if (file == NULL || name == NULL || stream == NULL) { + return ISO_NULL_POINTER; + } + + /* check if the name is valid */ + if (!iso_node_is_valid_name(name)) { + return ISO_WRONG_ARG_VALUE; + } + + new = calloc(1, sizeof(IsoFile)); + if (new == NULL) { + return ISO_OUT_OF_MEM; + } + new->node.refcount = 1; + new->node.type = LIBISO_FILE; + new->node.name = name; + new->node.mode = S_IFREG; + new->stream = stream; + + *file = new; + return ISO_SUCCESS; +} + +int iso_node_new_symlink(char *name, char *dest, IsoSymlink **link) +{ + IsoSymlink *new; + + if (link == NULL || name == NULL || dest == NULL) { + return ISO_NULL_POINTER; + } + + /* check if the name is valid */ + if (!iso_node_is_valid_name(name)) { + return ISO_WRONG_ARG_VALUE; + } + + /* check if destination is valid */ + if (!iso_node_is_valid_link_dest(dest)) { + /* guard against null or empty dest */ + return ISO_WRONG_ARG_VALUE; + } + + new = calloc(1, sizeof(IsoSymlink)); + if (new == NULL) { + return ISO_OUT_OF_MEM; + } + new->node.refcount = 1; + new->node.type = LIBISO_SYMLINK; + new->node.name = name; + new->dest = dest; + new->node.mode = S_IFLNK; + *link = new; + return ISO_SUCCESS; +} + +int iso_node_new_special(char *name, mode_t mode, dev_t dev, + IsoSpecial **special) +{ + IsoSpecial *new; + + if (special == NULL || name == NULL) { + return ISO_NULL_POINTER; + } + if (S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)) { + return ISO_WRONG_ARG_VALUE; + } + + /* check if the name is valid */ + if (!iso_node_is_valid_name(name)) { + return ISO_WRONG_ARG_VALUE; + } + + new = calloc(1, sizeof(IsoSpecial)); + if (new == NULL) { + return ISO_OUT_OF_MEM; + } + new->node.refcount = 1; + new->node.type = LIBISO_SPECIAL; + new->node.name = name; + + new->node.mode = mode; + new->dev = dev; + + *special = new; + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/node.h b/libisofs/branches/thomas/libisofs/node.h new file mode 100644 index 00000000..b2764f9e --- /dev/null +++ b/libisofs/branches/thomas/libisofs/node.h @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ +#ifndef LIBISO_NODE_H_ +#define LIBISO_NODE_H_ + +/* + * Definitions for the public iso tree + */ + +#include "libisofs.h" +#include "stream.h" + +#include +#include +#include +#include + +/* #define LIBISO_EXTENDED_INFORMATION */ +#ifdef LIBISO_EXTENDED_INFORMATION + +/** + * The extended information is a way to attach additional information to each + * IsoNode. External applications may want to use this extension system to + * store application speficic information related to each node. On the other + * side, libisofs may make use of this struct to attach information to nodes in + * some particular, uncommon, cases, without incrementing the size of the + * IsoNode struct. + * + * It is implemented like a chained list. + */ +typedef struct iso_extended_info IsoExtendedInfo; + +struct iso_extended_info { + /** + * Next struct in the chain. NULL if it is the last item + */ + IsoExtendedInfo *next; + + /** + * Function to handle this particular extended information. The function + * pointer acts as an identifier for the type of the information. Structs + * with same information type must use the same function. + * + * @param data + * Attached data + * @param flag + * What to do with the data. At this time the following values are + * defined: + * -> 1 the data must be freed + * @return + * 1 + */ + int (*process)(void *data, int flag); + + /** + * Pointer to information specific data. + */ + void *data; +}; + +#endif + +/** + * + */ +struct Iso_Node +{ + /* + * Initilized to 1, originally owned by user, until added to another node. + * Then it is owned by the parent node, so the user must take his own ref + * if needed. With the exception of the creation functions, none of the + * other libisofs functions that return an IsoNode increment its + * refcount. This is responsablity of the client, if (s)he needs it. + */ + int refcount; + + /** Type of the IsoNode, do not confuse with mode */ + enum IsoNodeType type; + + char *name; /**< Real name, in default charset */ + + mode_t mode; /**< protection */ + uid_t uid; /**< user ID of owner */ + gid_t gid; /**< group ID of owner */ + + /* TODO #00001 : consider adding new timestamps */ + time_t atime; /**< time of last access */ + time_t mtime; /**< time of last modification */ + time_t ctime; /**< time of last status change */ + + int hidden; /**< whether the node will be hidden, see IsoHideNodeFlag */ + + IsoDir *parent; /**< parent node, NULL for root */ + + /* + * Pointer to the linked list of children in a dir. + */ + IsoNode *next; + +#ifdef LIBISO_EXTENDED_INFORMATION + /** + * Extended information for the node. + */ + IsoExtendedInfo *xinfo; +#endif +}; + +struct Iso_Dir +{ + IsoNode node; + + size_t nchildren; /**< The number of children of this directory. */ + IsoNode *children; /**< list of children. ptr to first child */ +}; + +struct Iso_File +{ + IsoNode node; + + /** + * Location of a file extent in a ms disc, 0 for newly added file + */ + uint32_t msblock; + + /** + * It sorts the order in which the file data is written to the CD image. + * Higher weighting files are written at the beginning of image + */ + int sort_weight; + IsoStream *stream; +}; + +struct Iso_Symlink +{ + IsoNode node; + + char *dest; +}; + +struct Iso_Special +{ + IsoNode node; + dev_t dev; +}; + +/** + * An iterator for directory children. + */ +struct Iso_Dir_Iter +{ + const IsoDir *dir; + IsoNode *pos; +}; + +int iso_node_new_root(IsoDir **root); + +/** + * Create a new IsoDir. Attributes, uid/gid, timestamps, etc are set to + * default (0) values. You must set them. + * + * @param name + * Name for the node. It is not strdup() so you shouldn't use this + * reference when this function returns successfully. NULL is not + * allowed. + * @param dir + * + * @return + * 1 on success, < 0 on error. + */ +int iso_node_new_dir(char *name, IsoDir **dir); + +/** + * Create a new file node. Attributes, uid/gid, timestamps, etc are set to + * default (0) values. You must set them. + * + * @param name + * Name for the node. It is not strdup() so you shouldn't use this + * reference when this function returns successfully. NULL is not + * allowed. + * @param stream + * Source for file contents. The reference is taken by the node, + * you must call iso_stream_ref() if you need your own ref. + * @return + * 1 on success, < 0 on error. + */ +int iso_node_new_file(char *name, IsoStream *stream, IsoFile **file); + +/** + * Creates a new IsoSymlink node. Attributes, uid/gid, timestamps, etc are set + * to default (0) values. You must set them. + * + * @param name + * name for the new symlink. It is not strdup() so you shouldn't use this + * reference when this function returns successfully. NULL is not + * allowed. + * @param dest + * destination of the link. It is not strdup() so you shouldn't use this + * reference when this function returns successfully. NULL is not + * allowed. + * @param link + * place where to store a pointer to the newly created link. + * @return + * 1 on success, < 0 otherwise + */ +int iso_node_new_symlink(char *name, char *dest, IsoSymlink **link); + +/** + * Create a new special file node. As far as libisofs concerns, + * an special file is a block device, a character device, a FIFO (named pipe) + * or a socket. You can choose the specific kind of file you want to add + * by setting mode propertly (see man 2 stat). + * + * Note that special files are only written to image when Rock Ridge + * extensions are enabled. Moreover, a special file is just a directory entry + * in the image tree, no data is written beyond that. + * + * Owner and hidden atts are taken from parent. You can modify any of them + * later. + * + * @param name + * name for the new special file. It is not strdup() so you shouldn't use + * this reference when this function returns successfully. NULL is not + * allowed. + * @param mode + * file type and permissions for the new node. Note that you can't + * specify any kind of file here, only special types are allowed. i.e, + * S_IFSOCK, S_IFBLK, S_IFCHR and S_IFIFO are valid types; S_IFLNK, + * S_IFREG and S_IFDIR aren't. + * @param dev + * device ID, equivalent to the st_rdev field in man 2 stat. + * @param special + * place where to store a pointer to the newly created special file. + * @return + * 1 on success, < 0 otherwise + */ +int iso_node_new_special(char *name, mode_t mode, dev_t dev, + IsoSpecial **special); + +/** + * Check if a given name is valid for an iso node. + * + * @return + * 1 if yes, 0 if not + */ +int iso_node_is_valid_name(const char *name); + +/** + * Check if a given path is valid for the destination of a link. + * + * @return + * 1 if yes, 0 if not + */ +int iso_node_is_valid_link_dest(const char *dest); + +/** + * Find the position where to insert a node + * + * @param dir + * A valid dir. It can't be NULL + * @param name + * The node name to search for. It can't be NULL + * @param pos + * Will be filled with the position where to insert. It can't be NULL + */ +void iso_dir_find(IsoDir *dir, const char *name, IsoNode ***pos); + +/** + * Check if a node with the given name exists in a dir. + * + * @param dir + * A valid dir. It can't be NULL + * @param name + * The node name to search for. It can't be NULL + * @param pos + * If not NULL, will be filled with the position where to insert. If the + * node exists, (**pos) will refer to the given node. + * @return + * 1 if node exists, 0 if not + */ +int iso_dir_exists(IsoDir *dir, const char *name, IsoNode ***pos); + +/** + * Inserts a given node in a dir, at the specified position. + * + * @param dir + * Dir where to insert. It can't be NULL + * @param node + * The node to insert. It can't be NULL + * @param pos + * Position where the node will be inserted. It is a pointer previously + * obtained with a call to iso_dir_exists() or iso_dir_find(). + * It can't be NULL. + * @param replace + * Whether to replace an old node with the same name with the new node. + * @return + * If success, number of children in dir. < 0 on error + */ +int iso_dir_insert(IsoDir *dir, IsoNode *node, IsoNode **pos, + enum iso_replace_mode replace); + +#endif /*LIBISO_NODE_H_*/ diff --git a/libisofs/branches/thomas/libisofs/rockridge.c b/libisofs/branches/thomas/libisofs/rockridge.c new file mode 100644 index 00000000..e57ed3d1 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/rockridge.c @@ -0,0 +1,1208 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * Copyright (c) 2007 Mario Danic + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "rockridge.h" +#include "node.h" +#include "ecma119_tree.h" +#include "writer.h" +#include "messages.h" +#include "image.h" + +#include + +static +int susp_append(Ecma119Image *t, struct susp_info *susp, uint8_t *data) +{ + susp->n_susp_fields++; + susp->susp_fields = realloc(susp->susp_fields, sizeof(void*) + * susp->n_susp_fields); + if (susp->susp_fields == NULL) { + return ISO_OUT_OF_MEM; + } + susp->susp_fields[susp->n_susp_fields - 1] = data; + susp->suf_len += data[2]; + return ISO_SUCCESS; +} + +static +int susp_append_ce(Ecma119Image *t, struct susp_info *susp, uint8_t *data) +{ + susp->n_ce_susp_fields++; + susp->ce_susp_fields = realloc(susp->ce_susp_fields, sizeof(void*) + * susp->n_ce_susp_fields); + if (susp->ce_susp_fields == NULL) { + return ISO_OUT_OF_MEM; + } + susp->ce_susp_fields[susp->n_ce_susp_fields - 1] = data; + susp->ce_len += data[2]; + return ISO_SUCCESS; +} + +static +uid_t px_get_uid(Ecma119Image *t, Ecma119Node *n) +{ + if (t->replace_uid) { + return t->uid; + } else { + return n->node->uid; + } +} + +static +uid_t px_get_gid(Ecma119Image *t, Ecma119Node *n) +{ + if (t->replace_gid) { + return t->gid; + } else { + return n->node->gid; + } +} + +static +mode_t px_get_mode(Ecma119Image *t, Ecma119Node *n) +{ + if ((n->type == ECMA119_DIR || n->type == ECMA119_PLACEHOLDER)) { + if (t->replace_dir_mode) { + return (n->node->mode & S_IFMT) | t->dir_mode; + } + } else { + if (t->replace_file_mode) { + return (n->node->mode & S_IFMT) | t->file_mode; + } + } + return n->node->mode; +} + +/** + * Add a PX System Use Entry. The PX System Use Entry is used to add POSIX + * file attributes, such as access permissions or user and group id, to a + * ECMA 119 directory record. (RRIP, 4.1.1) + */ +static +int rrip_add_PX(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) +{ + uint8_t *PX = malloc(44); + if (PX == NULL) { + return ISO_OUT_OF_MEM; + } + + PX[0] = 'P'; + PX[1] = 'X'; + PX[2] = 44; + PX[3] = 1; + iso_bb(&PX[4], px_get_mode(t, n), 4); + iso_bb(&PX[12], n->nlink, 4); + iso_bb(&PX[20], px_get_uid(t, n), 4); + iso_bb(&PX[28], px_get_gid(t, n), 4); + iso_bb(&PX[36], n->ino, 4); + + return susp_append(t, susp, PX); +} + +/** + * Add to the given tree node a TF System Use Entry, used to record some + * time stamps related to the file (RRIP, 4.1.6). + */ +static +int rrip_add_TF(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) +{ + IsoNode *iso; + uint8_t *TF = malloc(5 + 3 * 7); + if (TF == NULL) { + return ISO_OUT_OF_MEM; + } + + TF[0] = 'T'; + TF[1] = 'F'; + TF[2] = 5 + 3 * 7; + TF[3] = 1; + TF[4] = (1 << 1) | (1 << 2) | (1 << 3); + + iso = n->node; + iso_datetime_7(&TF[5], t->replace_timestamps ? t->timestamp : iso->mtime, + t->always_gmt); + iso_datetime_7(&TF[12], t->replace_timestamps ? t->timestamp : iso->atime, + t->always_gmt); + iso_datetime_7(&TF[19], t->replace_timestamps ? t->timestamp : iso->ctime, + t->always_gmt); + return susp_append(t, susp, TF); +} + +/** + * Add a PL System Use Entry, used to record the location of the original + * parent directory of a directory which has been relocated. + * + * This is special because it doesn't modify the susp fields of the directory + * that gets passed to it; it modifies the susp fields of the ".." entry in + * that directory. + * + * See RRIP, 4.1.5.2 for more details. + */ +static +int rrip_add_PL(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) +{ + uint8_t *PL; + + if (n->type != ECMA119_DIR || n->info.dir->real_parent == NULL) { + /* should never occur */ + return ISO_ASSERT_FAILURE; + } + + PL = malloc(12); + if (PL == NULL) { + return ISO_OUT_OF_MEM; + } + + PL[0] = 'P'; + PL[1] = 'L'; + PL[2] = 12; + PL[3] = 1; + + /* write the location of the real parent, already computed */ + iso_bb(&PL[4], n->info.dir->real_parent->info.dir->block, 4); + return susp_append(t, susp, PL); +} + +/** + * Add a RE System Use Entry to the given tree node. The purpose of the + * this System Use Entry is to indicate to an RRIP-compliant receiving + * system that the Directory Record in which an "RE" System Use Entry is + * recorded has been relocated from another position in the original + * Directory Hierarchy. + * + * See RRIP, 4.1.5.3 for more details. + */ +static +int rrip_add_RE(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) +{ + uint8_t *RE = malloc(4); + if (RE == NULL) { + return ISO_OUT_OF_MEM; + } + + RE[0] = 'R'; + RE[1] = 'E'; + RE[2] = 4; + RE[3] = 1; + return susp_append(t, susp, RE); +} + +/** + * Add a PN System Use Entry to the given tree node. + * The PN System Use Entry is used to store the device number, and it's + * mandatory if the tree node corresponds to a character or block device. + * + * See RRIP, 4.1.2 for more details. + */ +static +int rrip_add_PN(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) +{ + IsoSpecial *node; + uint8_t *PN; + + node = (IsoSpecial*)n->node; + if (node->node.type != LIBISO_SPECIAL) { + /* should never occur */ + return ISO_ASSERT_FAILURE; + } + + PN = malloc(20); + if (PN == NULL) { + return ISO_OUT_OF_MEM; + } + + PN[0] = 'P'; + PN[1] = 'N'; + PN[2] = 20; + PN[3] = 1; + iso_bb(&PN[4], node->dev >> 32, 4); + iso_bb(&PN[12], node->dev & 0xffffffff, 4); + return susp_append(t, susp, PN); +} + +/** + * Add to the given tree node a CL System Use Entry, that is used to record + * the new location of a directory which has been relocated. + * + * See RRIP, 4.1.5.1 for more details. + */ +static +int rrip_add_CL(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) +{ + uint8_t *CL; + if (n->type != ECMA119_PLACEHOLDER) { + /* should never occur */ + return ISO_ASSERT_FAILURE; + } + CL = malloc(12); + if (CL == NULL) { + return ISO_OUT_OF_MEM; + } + + CL[0] = 'C'; + CL[1] = 'L'; + CL[2] = 12; + CL[3] = 1; + iso_bb(&CL[4], n->info.real_me->info.dir->block, 4); + return susp_append(t, susp, CL); +} + +/** + * Convert a RR filename to the requested charset. On any conversion error, + * the original name will be used. + */ +static +char *get_rr_fname(Ecma119Image *t, const char *str) +{ + int ret; + char *name; + + if (!strcmp(t->input_charset, t->output_charset)) { + /* no conversion needed */ + return strdup(str); + } + + ret = strconv(str, t->input_charset, t->output_charset, &name); + if (ret < 0) { + /* TODO we should check for possible cancelation */ + iso_msg_submit(t->image->id, ISO_FILENAME_WRONG_CHARSET, ret, + "Charset conversion error. Can't convert %s from %s to %s", + str, t->input_charset, t->output_charset); + + /* use the original name, it's the best we can do */ + name = strdup(str); + } + + return name; +} + +/** + * Add a NM System Use Entry to the given tree node. The purpose of this + * System Use Entry is to store the content of an Alternate Name to support + * POSIX-style or other names. + * + * See RRIP, 4.1.4 for more details. + * + * @param size + * Length of the name to be included into the NM + * @param flags + * @param ce + * Whether to add or not to CE + */ +static +int rrip_add_NM(Ecma119Image *t, struct susp_info *susp, char *name, int size, + int flags, int ce) +{ + uint8_t *NM = malloc(size + 5); + if (NM == NULL) { + return ISO_OUT_OF_MEM; + } + + NM[0] = 'N'; + NM[1] = 'M'; + NM[2] = size + 5; + NM[3] = 1; + NM[4] = flags; + if (size) { + memcpy(&NM[5], name, size); + } + if (ce) { + return susp_append_ce(t, susp, NM); + } else { + return susp_append(t, susp, NM); + } +} + +/** + * Add a new SL component (RRIP, 4.1.3.1) to a list of components. + * + * @param n + * Number of components. It will be updated. + * @param compos + * Pointer to the list of components. + * @param s + * The component content + * @param size + * Size of the component content + * @param fl + * Flags + * @return + * 1 on success, < 0 on error + */ +static +int rrip_SL_append_comp(size_t *n, uint8_t ***comps, char *s, int size, char fl) +{ + uint8_t *comp = malloc(size + 2); + if (comp == NULL) { + return ISO_OUT_OF_MEM; + } + + (*n)++; + comp[0] = fl; + comp[1] = size; + *comps = realloc(*comps, (*n) * sizeof(void*)); + if (*comps == NULL) { + free(comp); + return ISO_OUT_OF_MEM; + } + (*comps)[(*n) - 1] = comp; + + if (size) { + memcpy(&comp[2], s, size); + } + return ISO_SUCCESS; +} + +/** + * Add a SL System Use Entry to the given tree node. This is used to store + * the content of a symbolic link, and is mandatory if the tree node + * indicates a symbolic link (RRIP, 4.1.3). + * + * @param comp + * Components of the SL System Use Entry. If they don't fit in a single + * SL, more than one SL will be added. + * @param n + * Number of components in comp + * @param ce + * Whether to add to a continuation area or system use field. + */ +static +int rrip_add_SL(Ecma119Image *t, struct susp_info *susp, uint8_t **comp, + size_t n, int ce) +{ + int ret, i, j; + + int total_comp_len = 0; + size_t pos, written = 0; + + uint8_t *SL; + + for (i = 0; i < n; i++) { + + total_comp_len += comp[i][1] + 2; + if (total_comp_len > 250) { + /* we need a new SL entry */ + total_comp_len -= comp[i][1] + 2; + SL = malloc(total_comp_len + 5); + if (SL == NULL) { + return ISO_OUT_OF_MEM; + } + + SL[0] = 'S'; + SL[1] = 'L'; + SL[2] = total_comp_len + 5; + SL[3] = 1; + SL[4] = 1; /* CONTINUE */ + pos = 5; + for (j = written; j < i; j++) { + memcpy(&SL[pos], comp[j], comp[j][1] + 2); + pos += comp[j][1] + 2; + } + + /* + * In this case we are sure we're writting to CE. Check for + * debug purposes + */ + if (ce == 0) { + return ISO_ASSERT_FAILURE; + } + ret = susp_append_ce(t, susp, SL); + if (ret < 0) { + return ret; + } + written = i; + total_comp_len = comp[i][1] + 2; + } + } + + SL = malloc(total_comp_len + 5); + if (SL == NULL) { + return ISO_OUT_OF_MEM; + } + + SL[0] = 'S'; + SL[1] = 'L'; + SL[2] = total_comp_len + 5; + SL[3] = 1; + SL[4] = 0; + pos = 5; + + for (j = written; j < n; j++) { + memcpy(&SL[pos], comp[j], comp[j][1] + 2); + pos += comp[j][1] + 2; + } + if (ce) { + ret = susp_append_ce(t, susp, SL); + } else { + ret = susp_append(t, susp, SL); + } + return ret; +} + +/** + * Add a SUSP "ER" System Use Entry to identify the Rock Ridge specification. + * + * The "ER" System Use Entry is used to uniquely identify a specification + * compliant with SUSP. This method adds to the given tree node "." entry + * the "ER" corresponding to the RR protocol. + * + * See SUSP, 5.5 and RRIP, 4.3 for more details. + */ +static +int rrip_add_ER(Ecma119Image *t, struct susp_info *susp) +{ + unsigned char *ER = malloc(182); + if (ER == NULL) { + return ISO_OUT_OF_MEM; + } + + ER[0] = 'E'; + ER[1] = 'R'; + ER[2] = 182; + ER[3] = 1; + ER[4] = 9; + ER[5] = 72; + ER[6] = 93; + ER[7] = 1; + memcpy(&ER[8], "IEEE_1282", 9); + memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX " + "FILE SYSTEM SEMANTICS.", 72); + memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, " + "PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93); + + /** This always goes to continuation area */ + return susp_append_ce(t, susp, ER); +} + +/** + * Add a CE System Use Entry to the given tree node. A "CE" is used to add + * a continuation area, where additional System Use Entry can be written. + * (SUSP, 5.1). + */ +static +int susp_add_CE(Ecma119Image *t, size_t ce_len, struct susp_info *susp) +{ + uint8_t *CE = malloc(28); + if (CE == NULL) { + return ISO_OUT_OF_MEM; + } + + CE[0] = 'C'; + CE[1] = 'E'; + CE[2] = 28; + CE[3] = 1; + iso_bb(&CE[4], susp->ce_block, 4); + iso_bb(&CE[12], susp->ce_len, 4); + iso_bb(&CE[20], ce_len, 4); + + return susp_append(t, susp, CE); +} + +/** + * Add a SP System Use Entry. The SP provide an identifier that the SUSP is + * used within the volume. The SP shall be recorded in the "." entry of the + * root directory. See SUSP, 5.3 for more details. + */ +static +int susp_add_SP(Ecma119Image *t, struct susp_info *susp) +{ + unsigned char *SP = malloc(7); + if (SP == NULL) { + return ISO_OUT_OF_MEM; + } + + SP[0] = 'S'; + SP[1] = 'P'; + SP[2] = (char)7; + SP[3] = (char)1; + SP[4] = 0xbe; + SP[5] = 0xef; + SP[6] = 0; + return susp_append(t, susp, SP); +} + +/** + * Compute the length needed for write all RR and SUSP entries for a given + * node. + * + * @param type + * 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".." + * for that node (i.e., it will refer to the parent) + * @param space + * Available space in the System Use Area for the directory record. + * @param ce + * Will be filled with the space needed in a CE + * @return + * The size needed for the RR entries in the System Use Area + */ +size_t rrip_calc_len(Ecma119Image *t, Ecma119Node *n, int type, size_t space, + size_t *ce) +{ + size_t su_size; + + /* space min is 255 - 33 - 37 = 185 + * At the same time, it is always an odd number, but we need to pad it + * propertly to ensure the length of a directory record is a even number + * (ECMA-119, 9.1.13). Thus, in fact the real space is always space - 1 + */ + space--; + *ce = 0; + + /* PX and TF, we are sure they always fit in SUA */ + su_size = 44 + 26; + + if (n->type == ECMA119_DIR) { + if (n->info.dir->real_parent != NULL) { + /* it is a reallocated entry */ + if (type == 2) { + /* we need to add a PL entry */ + su_size += 12; + } else if (type == 0) { + /* we need to add a RE entry */ + su_size += 4; + } + } + } else if (n->type == ECMA119_SPECIAL) { + if (S_ISBLK(n->node->mode) || S_ISCHR(n->node->mode)) { + /* block or char device, we need a PN entry */ + su_size += 20; + } + } else if (n->type == ECMA119_PLACEHOLDER) { + /* we need the CL entry */ + su_size += 12; + } + + if (type == 0) { + char *name = get_rr_fname(t, n->node->name); + size_t namelen = strlen(name); + free(name); + + /* NM entry */ + if (su_size + 5 + namelen <= space) { + /* ok, it fits in System Use Area */ + su_size += 5 + namelen; + } else { + /* the NM will be divided in a CE */ + namelen = namelen - (space - su_size - 5 - 28); + *ce = 5 + namelen; + su_size = space; + } + if (n->type == ECMA119_SYMLINK) { + /* + * for symlinks, we also need to write the SL + */ + char *dest, *cur, *prev; + size_t sl_len = 5; + int cew = (*ce != 0); /* are we writing to CE? */ + + dest = get_rr_fname(t, ((IsoSymlink*)n->node)->dest); + prev = dest; + cur = strchr(prev, '/'); + while (1) { + size_t clen; + if (cur) { + clen = cur - prev; + } else { + /* last component */ + clen = strlen(prev); + } + + if (clen == 1 && prev[0] == '.') { + clen = 0; + } else if (clen == 2 && prev[0] == '.' && prev[1] == '.') { + clen = 0; + } + + /* flags and len for each component record (RRIP, 4.1.3.1) */ + clen += 2; + + if (!cew) { + /* we are still writing to the SUA */ + if (su_size + sl_len + clen > space) { + /* + * ok, we need a Continuation Area anyway + * TODO this can be handled better, but for now SL + * will be completelly moved into the CA + */ + if (su_size + 28 <= space) { + /* the CE entry fills without reducing NM */ + su_size += 28; + } else { + /* we need to reduce NM */ + *ce = (28 - (space - su_size)) + 5; + su_size = space; + } + cew = 1; + } else { + sl_len += clen; + } + } + if (cew) { + if (sl_len + clen > 255) { + /* we need an additional SL entry */ + if (clen > 250) { + /* + * case 1, component too large to fit in a + * single SL entry. Thus, the component need + * to be divided anyway. + * Note than clen can be up to 255 + 2 = 257. + * + * First, we check how many bytes fit in current + * SL field + */ + int fit = 255 - sl_len - 2; + if (clen - 250 <= fit) { + /* + * the component can be divided between this + * and another SL entry + */ + *ce += 255; /* this SL, full */ + sl_len = 5 + (clen - fit); + } else { + /* + * the component will need a 2rd SL entry in + * any case, so we prefer to don't write + * anything in this SL + */ + *ce += sl_len + 255; + sl_len = 5 + (clen - 250) + 2; + } + } else { + /* case 2, create a new SL entry */ + *ce += sl_len; + sl_len = 5 + clen; + } + } else { + sl_len += clen; + } + } + + if (!cur || cur[1] == '\0') { + /* cur[1] can be \0 if dest ends with '/' */ + break; + } + prev = cur + 1; + cur = strchr(prev, '/'); + } + + free(dest); + + /* and finally write the pending SL field */ + if (!cew) { + /* the whole SL fits into the SUA */ + su_size += sl_len; + } else { + *ce += sl_len; + } + + } + } else { + + /* "." or ".." entry */ + su_size += 5; /* NM field */ + if (type == 1 && n->parent == NULL) { + /* + * "." for root directory + * we need to write SP and ER entries. The first fits in SUA, + * ER needs a Continuation Area, thus we also need a CE entry + */ + su_size += 7 + 28; /* SP + CE */ + *ce = 182; /* ER */ + } + } + + /* + * The System Use field inside the directory record must be padded if + * it is an odd number (ECMA-119, 9.1.13) + */ + su_size += (su_size % 2); + return su_size; +} + +/** + * Free all info in a struct susp_info. + */ +static +void susp_info_free(struct susp_info* susp) +{ + size_t i; + + for (i = 0; i < susp->n_susp_fields; ++i) { + free(susp->susp_fields[i]); + } + free(susp->susp_fields); + + for (i = 0; i < susp->n_ce_susp_fields; ++i) { + free(susp->ce_susp_fields[i]); + } + free(susp->ce_susp_fields); +} + +/** + * Fill a struct susp_info with the RR/SUSP entries needed for a given + * node. + * + * @param type + * 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".." + * for that node (i.e., it will refer to the parent) + * @param space + * Available space in the System Use Area for the directory record. + * @param info + * Pointer to the struct susp_info where the entries will be stored. + * If some entries need to go to a Continuation Area, they will be added + * to the existing ce_susp_fields, and ce_len will be incremented + * propertly. Please ensure ce_block is initialized propertly. + * @return + * 1 success, < 0 error + */ +int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type, + size_t space, struct susp_info *info) +{ + int ret; + size_t i; + Ecma119Node *node; + char *name = NULL; + char *dest = NULL; + + if (t == NULL || n == NULL || info == NULL) { + return ISO_NULL_POINTER; + } + if (type < 0 || type > 2 || space < 185) { + /* space min is 255 - 33 - 37 = 185 */ + return ISO_WRONG_ARG_VALUE; + } + + if (type == 2 && n->parent != NULL) { + node = n->parent; + } else { + node = n; + } + + /* space min is 255 - 33 - 37 = 185 + * At the same time, it is always an odd number, but we need to pad it + * propertly to ensure the length of a directory record is a even number + * (ECMA-119, 9.1.13). Thus, in fact the real space is always space - 1 + */ + space--; + + /* + * SP must be the first entry for the "." record of the root directory + * (SUSP, 5.3) + */ + if (type == 1 && n->parent == NULL) { + ret = susp_add_SP(t, info); + if (ret < 0) { + goto add_susp_cleanup; + } + } + + /* PX and TF, we are sure they always fit in SUA */ + ret = rrip_add_PX(t, node, info); + if (ret < 0) { + goto add_susp_cleanup; + } + ret = rrip_add_TF(t, node, info); + if (ret < 0) { + goto add_susp_cleanup; + } + + if (n->type == ECMA119_DIR) { + if (n->info.dir->real_parent != NULL) { + /* it is a reallocated entry */ + if (type == 2) { + /* + * we need to add a PL entry + * Note that we pass "n" as parameter, not "node" + */ + ret = rrip_add_PL(t, n, info); + if (ret < 0) { + goto add_susp_cleanup; + } + } else if (type == 0) { + /* we need to add a RE entry */ + ret = rrip_add_RE(t, node, info); + if (ret < 0) { + goto add_susp_cleanup; + } + } + } + } else if (n->type == ECMA119_SPECIAL) { + if (S_ISBLK(n->node->mode) || S_ISCHR(n->node->mode)) { + /* block or char device, we need a PN entry */ + ret = rrip_add_PN(t, node, info); + if (ret < 0) { + goto add_susp_cleanup; + } + } + } else if (n->type == ECMA119_PLACEHOLDER) { + /* we need the CL entry */ + ret = rrip_add_CL(t, node, info); + if (ret < 0) { + goto add_susp_cleanup; + } + } + + if (type == 0) { + size_t sua_free; /* free space in the SUA */ + int nm_type = 0; /* 0 whole entry in SUA, 1 part in CE */ + size_t ce_len = 0; /* len of the CE */ + size_t namelen; + + /* this two are only defined for symlinks */ + uint8_t **comps= NULL; /* components of the SL field */ + size_t n_comp = 0; /* number of components */ + + name = get_rr_fname(t, n->node->name); + namelen = strlen(name); + + sua_free = space - info->suf_len; + + /* NM entry */ + if (5 + namelen <= sua_free) { + /* ok, it fits in System Use Area */ + sua_free -= (5 + namelen); + nm_type = 0; + } else { + /* the NM will be divided in a CE */ + nm_type = 1; + namelen = namelen - (sua_free - 5 - 28); + ce_len = 5 + namelen; + sua_free = 0; + } + if (n->type == ECMA119_SYMLINK) { + /* + * for symlinks, we also need to write the SL + */ + char *cur, *prev; + size_t sl_len = 5; + int cew = (nm_type == 1); /* are we writing to CE? */ + + dest = get_rr_fname(t, ((IsoSymlink*)n->node)->dest); + prev = dest; + cur = strchr(prev, '/'); + while (1) { + size_t clen; + char cflag = 0; /* component flag (RRIP, 4.1.3.1) */ + if (cur) { + clen = cur - prev; + } else { + /* last component */ + clen = strlen(prev); + } + + if (clen == 0) { + /* this refers to the roor directory, '/' */ + cflag = 1 << 3; + } + if (clen == 1 && prev[0] == '.') { + clen = 0; + cflag = 1 << 1; + } else if (clen == 2 && prev[0] == '.' && prev[1] == '.') { + clen = 0; + cflag = 1 << 2; + } + + /* flags and len for each component record (RRIP, 4.1.3.1) */ + clen += 2; + + if (!cew) { + /* we are still writing to the SUA */ + if (sl_len + clen > sua_free) { + /* + * ok, we need a Continuation Area anyway + * TODO this can be handled better, but for now SL + * will be completelly moved into the CA + */ + if (28 <= sua_free) { + /* the CE entry fills without reducing NM */ + sua_free -= 28; + cew = 1; + } else { + /* we need to reduce NM */ + nm_type = 1; + ce_len = (28 - sua_free) + 5; + sua_free = 0; + cew = 1; + } + } else { + /* add the component */ + ret = rrip_SL_append_comp(&n_comp, &comps, prev, + clen - 2, cflag); + if (ret < 0) { + goto add_susp_cleanup; + } + sl_len += clen; + } + } + if (cew) { + if (sl_len + clen > 255) { + /* we need an addition SL entry */ + if (clen > 250) { + /* + * case 1, component too large to fit in a + * single SL entry. Thus, the component need + * to be divided anyway. + * Note than clen can be up to 255 + 2 = 257. + * + * First, we check how many bytes fit in current + * SL field + */ + int fit = 255 - sl_len - 2; + if (clen - 250 <= fit) { + /* + * the component can be divided between this + * and another SL entry + */ + ret = rrip_SL_append_comp(&n_comp, &comps, + prev, fit, 0x01); + if (ret < 0) { + goto add_susp_cleanup; + } + /* + * and another component, that will go in + * other SL entry + */ + ret = rrip_SL_append_comp(&n_comp, &comps, prev + + fit, clen - fit - 2, 0); + if (ret < 0) { + goto add_susp_cleanup; + } + ce_len += 255; /* this SL, full */ + sl_len = 5 + (clen - fit); + } else { + /* + * the component will need a 2rd SL entry in + * any case, so we prefer to don't write + * anything in this SL + */ + ret = rrip_SL_append_comp(&n_comp, &comps, + prev, 248, 0x01); + if (ret < 0) { + goto add_susp_cleanup; + } + ret = rrip_SL_append_comp(&n_comp, &comps, prev + + 248, strlen(prev + 248), 0x00); + if (ret < 0) { + goto add_susp_cleanup; + } + ce_len += sl_len + 255; + sl_len = 5 + (clen - 250) + 2; + } + } else { + /* case 2, create a new SL entry */ + ret = rrip_SL_append_comp(&n_comp, &comps, prev, + clen - 2, cflag); + if (ret < 0) { + goto add_susp_cleanup; + } + ce_len += sl_len; + sl_len = 5 + clen; + } + } else { + /* the component fit in the SL entry */ + ret = rrip_SL_append_comp(&n_comp, &comps, prev, + clen - 2, cflag); + if (ret < 0) { + goto add_susp_cleanup; + } + sl_len += clen; + } + } + + if (!cur || cur[1] == '\0') { + /* cur[1] can be \0 if dest ends with '/' */ + break; + } + prev = cur + 1; + cur = strchr(prev, '/'); + } + + if (cew) { + ce_len += sl_len; + } + } + + /* + * We we reach here: + * - We know if NM fill in the SUA (nm_type == 0) + * - If SL needs an to be written in CE (ce_len > 0) + * - The components for SL entry (or entries) + */ + + if (nm_type == 0) { + /* the full NM fills in SUA */ + ret = rrip_add_NM(t, info, name, strlen(name), 0, 0); + if (ret < 0) { + goto add_susp_cleanup; + } + } else { + /* + * Write the NM part that fits in SUA... Note that CE + * entry and NM in the continuation area is added below + */ + namelen = space - info->suf_len - 28 - 5; + ret = rrip_add_NM(t, info, name, namelen, 1, 0); + if (ret < 0) { + goto add_susp_cleanup; + } + } + + if (ce_len > 0) { + /* Add the CE entry */ + ret = susp_add_CE(t, ce_len, info); + if (ret < 0) { + goto add_susp_cleanup; + } + } + + if (nm_type == 1) { + /* + * ..and the part that goes to continuation area. + */ + ret = rrip_add_NM(t, info, name + namelen, strlen(name + namelen), + 0, 1); + if (ret < 0) { + goto add_susp_cleanup; + } + } + + if (n->type == ECMA119_SYMLINK) { + + /* add the SL entry (or entries) */ + ret = rrip_add_SL(t, info, comps, n_comp, (ce_len > 0)); + + /* free the components */ + for (i = 0; i < n_comp; i++) { + free(comps[i]); + } + free(comps); + + if (ret < 0) { + goto add_susp_cleanup; + } + } + + } else { + + /* "." or ".." entry */ + + /* write the NM entry */ + ret = rrip_add_NM(t, info, NULL, 0, 1 << type, 0); + if (ret < 0) { + goto add_susp_cleanup; + } + if (type == 1 && n->parent == NULL) { + /* + * "." for root directory + * we need to write SP and ER entries. The first fits in SUA, + * ER needs a Continuation Area, thus we also need a CE entry. + * Note that SP entry was already added above + */ + ret = susp_add_CE(t, 182, info); /* 182 is ER length */ + if (ret < 0) { + goto add_susp_cleanup; + } + ret = rrip_add_ER(t, info); + if (ret < 0) { + goto add_susp_cleanup; + } + } + } + + /* + * The System Use field inside the directory record must be padded if + * it is an odd number (ECMA-119, 9.1.13) + */ + info->suf_len += (info->suf_len % 2); + + free(name); + free(dest); + return ISO_SUCCESS; + + add_susp_cleanup: ; + free(name); + free(dest); + susp_info_free(info); + return ret; +} + +/** + * Write the given SUSP fields into buf. Note that Continuation Area + * fields are not written. + * If info does not contain any SUSP entry this function just return. + * After written, the info susp_fields array will be freed, and the counters + * updated propertly. + */ +void rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info, + uint8_t *buf) +{ + size_t i; + size_t pos = 0; + + if (info->n_susp_fields == 0) { + return; + } + + for (i = 0; i < info->n_susp_fields; i++) { + memcpy(buf + pos, info->susp_fields[i], info->susp_fields[i][2]); + pos += info->susp_fields[i][2]; + } + + /* free susp_fields */ + for (i = 0; i < info->n_susp_fields; ++i) { + free(info->susp_fields[i]); + } + free(info->susp_fields); + info->susp_fields = NULL; + info->n_susp_fields = 0; + info->suf_len = 0; +} + +/** + * Write the Continuation Area entries for the given struct susp_info, using + * the iso_write() function. + * After written, the ce_susp_fields array will be freed. + */ +int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info) +{ + size_t i; + uint8_t padding[BLOCK_SIZE]; + int ret= ISO_SUCCESS; + + if (info->n_ce_susp_fields == 0) { + return ret; + } + + for (i = 0; i < info->n_ce_susp_fields; i++) { + ret = iso_write(t, info->ce_susp_fields[i], + info->ce_susp_fields[i][2]); + if (ret < 0) { + goto write_ce_field_cleanup; + } + } + + /* pad continuation area until block size */ + i = BLOCK_SIZE - (info->ce_len % BLOCK_SIZE); + if (i > 0 && i < BLOCK_SIZE) { + memset(padding, 0, i); + ret = iso_write(t, padding, i); + } + + write_ce_field_cleanup: ; + /* free ce_susp_fields */ + for (i = 0; i < info->n_ce_susp_fields; ++i) { + free(info->ce_susp_fields[i]); + } + free(info->ce_susp_fields); + info->ce_susp_fields = NULL; + info->n_ce_susp_fields = 0; + info->ce_len = 0; + return ret; +} + diff --git a/libisofs/branches/thomas/libisofs/rockridge.h b/libisofs/branches/thomas/libisofs/rockridge.h new file mode 100644 index 00000000..c21a954d --- /dev/null +++ b/libisofs/branches/thomas/libisofs/rockridge.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * Copyright (c) 2007 Mario Danic + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/** + * This header defines the functions and structures needed to add RockRidge + * extensions to an ISO image. + * + * References: + * + * - SUSP (IEEE 1281). + * System Use Sharing Protocol, draft standard version 1.12. + * + * - RRIP (IEEE 1282) + * Rock Ridge Interchange Protocol, Draft Standard version 1.12. + * + * - ECMA-119 (ISO-9660) + * Volume and File Structure of CDROM for Information Interchange. + */ + +#ifndef LIBISO_ROCKRIDGE_H +#define LIBISO_ROCKRIDGE_H + +#include "ecma119.h" + +#define SUSP_SIG(entry, a, b) ((entry->sig[0] == a) && (entry->sig[1] == b)) + +/** + * This contains the information about the System Use Fields (SUSP, 4.1), + * that will be written in the System Use Areas, both in the ISO directory + * record System Use field (ECMA-119, 9.1.13) or in a Continuation Area as + * defined by SUSP. + */ +struct susp_info +{ + /** Number of SUSP fields in the System Use field */ + size_t n_susp_fields; + uint8_t **susp_fields; + + /** Length of the part of the SUSP area that fits in the dirent. */ + int suf_len; + + /** Length of the part of the SUSP area that will go in a CE area. */ + uint32_t ce_block; + uint32_t ce_len; + + size_t n_ce_susp_fields; + uint8_t **ce_susp_fields; +}; + +/* SUSP 5.1 */ +struct susp_CE { + uint8_t block[8]; + uint8_t offset[8]; + uint8_t len[8]; +}; + +/* SUSP 5.3 */ +struct susp_SP { + uint8_t be[1]; + uint8_t ef[1]; + uint8_t len_skp[1]; +}; + +/* SUSP 5.5 */ +struct susp_ER { + uint8_t len_id[1]; + uint8_t len_des[1]; + uint8_t len_src[1]; + uint8_t ext_ver[1]; + uint8_t ext_id[1]; /*< up to len_id bytes */ + /* ext_des, ext_src */ +}; + +/** POSIX file attributes (RRIP, 4.1.1) */ +struct rr_PX { + uint8_t mode[8]; + uint8_t links[8]; + uint8_t uid[8]; + uint8_t gid[8]; + uint8_t serial[8]; +}; + +/** Time stamps for a file (RRIP, 4.1.6) */ +struct rr_TF { + uint8_t flags[1]; + uint8_t t_stamps[1]; +}; + +/** Info for character and block device (RRIP, 4.1.2) */ +struct rr_PN { + uint8_t high[8]; + uint8_t low[8]; +}; + +/** Alternate name (RRIP, 4.1.4) */ +struct rr_NM { + uint8_t flags[1]; + uint8_t name[1]; +}; + +/** Link for a relocated directory (RRIP, 4.1.5.1) */ +struct rr_CL { + uint8_t child_loc[8]; +}; + +/** Sim link (RRIP, 4.1.3) */ +struct rr_SL { + uint8_t flags[1]; + uint8_t comps[1]; +}; + +/** + * Struct for a SUSP System User Entry (SUSP, 4.1) + */ +struct susp_sys_user_entry +{ + uint8_t sig[2]; + uint8_t len_sue[1]; + uint8_t version[1]; + union { + struct susp_CE CE; + struct susp_SP SP; + struct susp_ER ER; + struct rr_PX PX; + struct rr_TF TF; + struct rr_PN PN; + struct rr_NM NM; + struct rr_CL CL; + struct rr_SL SL; + } data; /* 5 to 4+len_sue */ +}; + +/** + * Compute the length needed for write all RR and SUSP entries for a given + * node. + * + * @param type + * 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".." + * for that node (i.e., it will refer to the parent) + * @param space + * Available space in the System Use Area for the directory record. + * @param ce + * Will be filled with the space needed in a CE + * @return + * The size needed for the RR entries in the System Use Area + */ +size_t rrip_calc_len(Ecma119Image *t, Ecma119Node *n, int type, size_t space, + size_t *ce); + +/** + * Fill a struct susp_info with the RR/SUSP entries needed for a given + * node. + * + * @param type + * 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".." + * for that node (i.e., it will refer to the parent) + * @param space + * Available space in the System Use Area for the directory record. + * @param info + * Pointer to the struct susp_info where the entries will be stored. + * If some entries need to go to a Continuation Area, they will be added + * to the existing ce_susp_fields, and ce_len will be incremented + * propertly. Please ensure ce_block is initialized propertly. + * @return + * 1 success, < 0 error + */ +int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type, + size_t space, struct susp_info *info); + +/** + * Write the given SUSP fields into buf. Note that Continuation Area + * fields are not written. + * If info does not contain any SUSP entry this function just return. + * After written, the info susp_fields array will be freed, and the counters + * updated propertly. + */ +void rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info, + uint8_t *buf); + +/** + * Write the Continuation Area entries for the given struct susp_info, using + * the iso_write() function. + * After written, the ce_susp_fields array will be freed. + */ +int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info); + +/** + * The SUSP iterator is used to iterate over the System User Entries + * of a ECMA-168 directory record. + * It takes care about Continuation Areas, handles the end of the different + * system user entries and skip padding areas. Thus, using an iteration + * we are accessing just to the meaning entries. + */ +typedef struct susp_iterator SuspIterator; + +SuspIterator * +susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record, + uint8_t len_skp, int msgid); + +/** + * Get the next SUSP System User Entry using given iterator. + * + * @param sue + * Pointer to the next susp entry. It refers to an internal buffer and + * it's not guaranteed to be allocated after calling susp_iter_next() + * again. Thus, if you need to keep some entry you have to do a copy. + * @return + * 1 on success, 0 if no more entries, < 0 error + */ +int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue); + +/** + * Free a given susp iterator. + */ +void susp_iter_free(SuspIterator *iter); + + +/** + * Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1). + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st); + +/** + * Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6) + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st); + +/** + * Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to + * the given name. You can pass a pointer to NULL as name. + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont); + +/** + * Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues. + * + * @param cont + * 0 not continue, 1 continue, 2 continue component + * @return + * 1 on success, < 0 on error + */ +int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont); + +/** + * Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2). + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st); + +#endif /* LIBISO_ROCKRIDGE_H */ diff --git a/libisofs/branches/thomas/libisofs/rockridge_read.c b/libisofs/branches/thomas/libisofs/rockridge_read.c new file mode 100644 index 00000000..02dbc869 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/rockridge_read.c @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/* + * This file contains functions related to the reading of SUSP and + * Rock Ridge extensions on an ECMA-119 image. + */ + +#include "libisofs.h" +#include "ecma119.h" +#include "util.h" +#include "rockridge.h" +#include "messages.h" + +#include +#include +#include + +struct susp_iterator +{ + uint8_t* base; + int pos; + int size; + IsoDataSource *src; + int msgid; + + /* block and offset for next continuation area */ + uint32_t ce_block; + uint32_t ce_off; + + /** Length of the next continuation area, 0 if no more CA are specified */ + uint32_t ce_len; + + uint8_t *buffer; /*< If there are continuation areas */ +}; + +SuspIterator* +susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record, + uint8_t len_skp, int msgid) +{ + int pad = (record->len_fi[0] + 1) % 2; + struct susp_iterator *iter = malloc(sizeof(struct susp_iterator)); + if (iter == NULL) { + return NULL; + } + + iter->base = record->file_id + record->len_fi[0] + pad; + iter->pos = len_skp; /* 0 in most cases */ + iter->size = record->len_dr[0] - record->len_fi[0] - 33 - pad; + iter->src = src; + iter->msgid = msgid; + + iter->ce_len = 0; + iter->buffer = NULL; + + return iter; +} + +int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue) +{ + struct susp_sys_user_entry *entry; + + entry = (struct susp_sys_user_entry*)(iter->base + iter->pos); + + if ( (iter->pos + 4 > iter->size) || (SUSP_SIG(entry, 'S', 'T'))) { + + /* + * End of the System Use Area or Continuation Area. + * Note that ST is not needed when the space left is less than 4. + * (IEEE 1281, SUSP. section 4) + */ + if (iter->ce_len) { + uint32_t block; + int nblocks; + + /* A CE has found, there is another continuation area */ + nblocks = DIV_UP(iter->ce_off + iter->ce_len, BLOCK_SIZE); + iter->buffer = realloc(iter->buffer, nblocks * BLOCK_SIZE); + + /* read all blocks needed to cache the full CE */ + for (block = 0; block < nblocks; ++block) { + int ret; + ret = iter->src->read_block(iter->src, iter->ce_block + block, + iter->buffer + block * BLOCK_SIZE); + if (ret < 0) { + return ret; + } + } + iter->base = iter->buffer + iter->ce_off; + iter->pos = 0; + iter->size = iter->ce_len; + iter->ce_len = 0; + entry = (struct susp_sys_user_entry*)iter->base; + } else { + return 0; + } + } + + if (entry->len_sue[0] == 0) { + /* a wrong image with this lead us to a infinity loop */ + iso_msg_submit(iter->msgid, ISO_WRONG_RR, 0, + "Damaged RR/SUSP information."); + return ISO_WRONG_RR; + } + + iter->pos += entry->len_sue[0]; + + if (SUSP_SIG(entry, 'C', 'E')) { + /* Continuation entry */ + if (iter->ce_len) { + int ret; + ret = iso_msg_submit(iter->msgid, ISO_UNSUPPORTED_SUSP, 0, + "More than one CE System user entry has found in a single " + "System Use field or continuation area. This breaks SUSP " + "standard and it's not supported. Ignoring last CE. Maybe " + "the image is damaged."); + if (ret < 0) { + return ret; + } + } else { + iter->ce_block = iso_read_bb(entry->data.CE.block, 4, NULL); + iter->ce_off = iso_read_bb(entry->data.CE.offset, 4, NULL); + iter->ce_len = iso_read_bb(entry->data.CE.len, 4, NULL); + } + + /* we don't want to return CE entry to the user */ + return susp_iter_next(iter, sue); + } else if (SUSP_SIG(entry, 'P', 'D')) { + /* skip padding */ + return susp_iter_next(iter, sue); + } + + *sue = entry; + return ISO_SUCCESS; +} + +void susp_iter_free(SuspIterator *iter) +{ + free(iter->buffer); + free(iter); +} + +/** + * Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1). + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st) +{ + if (px == NULL || st == NULL) { + return ISO_NULL_POINTER; + } + if (px->sig[0] != 'P' || px->sig[1] != 'X') { + return ISO_WRONG_ARG_VALUE; + } + + if (px->len_sue[0] != 44 && px->len_sue[0] != 36) { + return ISO_WRONG_RR; + } + + st->st_mode = iso_read_bb(px->data.PX.mode, 4, NULL); + st->st_nlink = iso_read_bb(px->data.PX.links, 4, NULL); + st->st_uid = iso_read_bb(px->data.PX.uid, 4, NULL); + st->st_gid = iso_read_bb(px->data.PX.gid, 4, NULL); + if (px->len_sue[0] == 44) { + /* this corresponds to RRIP 1.12, so we have inode serial number */ + st->st_ino = iso_read_bb(px->data.PX.serial, 4, NULL); + } + return ISO_SUCCESS; +} + +/** + * Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6) + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st) +{ + time_t time; + int s; + int nts = 0; + + if (tf == NULL || st == NULL) { + return ISO_NULL_POINTER; + } + if (tf->sig[0] != 'T' || tf->sig[1] != 'F') { + return ISO_WRONG_ARG_VALUE; + } + + if (tf->data.TF.flags[0] & (1 << 7)) { + /* long form */ + s = 17; + } else { + s = 7; + } + + /* 1. Creation time */ + if (tf->data.TF.flags[0] & (1 << 0)) { + + /* the creation is the recording time. we ignore this */ + /* TODO maybe it would be good to manage it in ms discs, where + * the recording time could be different than now!! */ + ++nts; + } + + /* 2. modify time */ + if (tf->data.TF.flags[0] & (1 << 1)) { + if (tf->len_sue[0] < 5 + (nts+1) * s) { + /* RR TF entry too short. */ + return ISO_WRONG_RR; + } + if (s == 7) { + time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); + } else { + time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); + } + st->st_mtime = time; + ++nts; + } + + /* 3. access time */ + if (tf->data.TF.flags[0] & (1 << 2)) { + if (tf->len_sue[0] < 5 + (nts+1) * s) { + /* RR TF entry too short. */ + return ISO_WRONG_RR; + } + if (s == 7) { + time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); + } else { + time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); + } + st->st_atime = time; + ++nts; + } + + /* 4. attributes time */ + if (tf->data.TF.flags[0] & (1 << 3)) { + if (tf->len_sue[0] < 5 + (nts+1) * s) { + /* RR TF entry too short. */ + return ISO_WRONG_RR; + } + if (s == 7) { + time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); + } else { + time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); + } + st->st_ctime = time; + ++nts; + } + + /* we ignore backup, expire and effect times */ + + return ISO_SUCCESS; +} + +/** + * Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to + * the given name. You can pass a pointer to NULL as name. + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont) +{ + if (nm == NULL || name == NULL) { + return ISO_NULL_POINTER; + } + if (nm->sig[0] != 'N' || nm->sig[1] != 'M') { + return ISO_WRONG_ARG_VALUE; + } + + if (nm->len_sue[0] == 5) { + if (nm->data.NM.flags[0] & 0x2) { + /* it is a "." entry */ + if (*name == NULL) { + return ISO_SUCCESS; + } else { + /* we can't have a previous not-NULL name */ + return ISO_WRONG_RR; + } + } + } + + if (nm->len_sue[0] <= 5) { + /* ".." entry is an error, as we will never call it */ + return ISO_WRONG_RR; + } + + /* concatenate the results */ + if (*cont) { + *name = realloc(*name, strlen(*name) + nm->len_sue[0] - 5 + 1); + strncat(*name, (char*)nm->data.NM.name, nm->len_sue[0] - 5); + } else { + *name = strcopy((char*)nm->data.NM.name, nm->len_sue[0] - 5); + } + if (*name == NULL) { + return ISO_OUT_OF_MEM; + } + + /* and set cond according to the value of CONTINUE flag */ + *cont = nm->data.NM.flags[0] & 0x01; + return ISO_SUCCESS; +} + +/** + * Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues. + * + * @param cont + * 0 not continue, 1 continue, 2 continue component + * @return + * 1 on success, < 0 on error + */ +int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont) +{ + int pos; + + if (sl == NULL || dest == NULL) { + return ISO_NULL_POINTER; + } + if (sl->sig[0] != 'S' || sl->sig[1] != 'L') { + return ISO_WRONG_ARG_VALUE; + } + + for (pos = 0; pos + 5 < sl->len_sue[0]; + pos += 2 + sl->data.SL.comps[pos + 1]) { + char *comp; + uint8_t len; + uint8_t flags = sl->data.SL.comps[pos]; + + if (flags & 0x2) { + /* current directory */ + len = 1; + comp = "."; + } else if (flags & 0x4) { + /* parent directory */ + len = 2; + comp = ".."; + } else if (flags & 0x8) { + /* root directory */ + len = 1; + comp = "/"; + } else if (flags & ~0x01) { + /* unsupported flag component */ + return ISO_UNSUPPORTED_RR; + } else { + len = sl->data.SL.comps[pos + 1]; + comp = (char*)&sl->data.SL.comps[pos + 2]; + } + + if (*cont == 1) { + /* new component */ + size_t size = strlen(*dest); + *dest = realloc(*dest, strlen(*dest) + len + 2); + if (*dest == NULL) { + return ISO_OUT_OF_MEM; + } + /* it is a new compoenent, add the '/' */ + if ((*dest)[size-1] != '/') { + (*dest)[size] = '/'; + (*dest)[size+1] = '\0'; + } + strncat(*dest, comp, len); + } else if (*cont == 2) { + /* the component continues */ + *dest = realloc(*dest, strlen(*dest) + len + 1); + if (*dest == NULL) { + return ISO_OUT_OF_MEM; + } + /* we don't have to add the '/' */ + strncat(*dest, comp, len); + } else { + *dest = strcopy(comp, len); + } + if (*dest == NULL) { + return ISO_OUT_OF_MEM; + } + /* do the component continue or not? */ + *cont = (flags & 0x01) ? 2 : 1; + } + + if (*cont == 2) { + /* TODO check that SL flag is set to continute too ?*/ + } else { + *cont = sl->data.SL.flags[0] & 0x1 ? 1 : 0; + } + + return ISO_SUCCESS; +} + +/** + * Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2). + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st) +{ + if (pn == NULL || pn == NULL) { + return ISO_NULL_POINTER; + } + if (pn->sig[0] != 'P' || pn->sig[1] != 'N') { + return ISO_WRONG_ARG_VALUE; + } + + if (pn->len_sue[0] != 20) { + return ISO_WRONG_RR; + } + + st->st_rdev = (dev_t)((dev_t)iso_read_bb(pn->data.PN.high, 4, NULL) << 32) + || (dev_t)iso_read_bb(pn->data.PN.low, 4, NULL); + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/stream.c b/libisofs/branches/thomas/libisofs/stream.c new file mode 100644 index 00000000..710ed8f1 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/stream.c @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "libisofs.h" +#include "stream.h" +#include "fsource.h" +#include "util.h" + +#include +#include + +ino_t serial_id = (ino_t)1; +ino_t mem_serial_id = (ino_t)1; + +typedef struct +{ + IsoFileSource *src; + + /* key for file identification inside filesystem */ + dev_t dev_id; + ino_t ino_id; + off_t size; /**< size of this file */ +} FSrcStreamData; + +static +int fsrc_open(IsoStream *stream) +{ + int ret; + struct stat info; + off_t esize; + IsoFileSource *src; + if (stream == NULL) { + return ISO_NULL_POINTER; + } + src = ((FSrcStreamData*)stream->data)->src; + ret = iso_file_source_stat(src, &info); + if (ret < 0) { + return ret; + } + ret = iso_file_source_open(src); + if (ret < 0) { + return ret; + } + esize = ((FSrcStreamData*)stream->data)->size; + if (info.st_size == esize) { + return ISO_SUCCESS; + } else { + return (esize > info.st_size) ? 3 : 2; + } +} + +static +int fsrc_close(IsoStream *stream) +{ + IsoFileSource *src; + if (stream == NULL) { + return ISO_NULL_POINTER; + } + src = ((FSrcStreamData*)stream->data)->src; + return iso_file_source_close(src); +} + +static +off_t fsrc_get_size(IsoStream *stream) +{ + FSrcStreamData *data; + data = (FSrcStreamData*)stream->data; + + return data->size; +} + +static +int fsrc_read(IsoStream *stream, void *buf, size_t count) +{ + IsoFileSource *src; + if (stream == NULL) { + return ISO_NULL_POINTER; + } + src = ((FSrcStreamData*)stream->data)->src; + return iso_file_source_read(src, buf, count); +} + +static +int fsrc_is_repeatable(IsoStream *stream) +{ + int ret; + struct stat info; + FSrcStreamData *data; + if (stream == NULL) { + return ISO_NULL_POINTER; + } + data = (FSrcStreamData*)stream->data; + + /* mode is not cached, this function is only useful for filters */ + ret = iso_file_source_stat(data->src, &info); + if (ret < 0) { + return ret; + } + if (S_ISREG(info.st_mode) || S_ISBLK(info.st_mode)) { + return 1; + } else { + return 0; + } +} + +static +void fsrc_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, + ino_t *ino_id) +{ + FSrcStreamData *data; + IsoFilesystem *fs; + + data = (FSrcStreamData*)stream->data; + fs = iso_file_source_get_filesystem(data->src); + + *fs_id = fs->get_id(fs); + *dev_id = data->dev_id; + *ino_id = data->ino_id; +} + +static +char *fsrc_get_name(IsoStream *stream) +{ + FSrcStreamData *data; + data = (FSrcStreamData*)stream->data; + return iso_file_source_get_path(data->src); +} + +static +void fsrc_free(IsoStream *stream) +{ + FSrcStreamData *data; + data = (FSrcStreamData*)stream->data; + iso_file_source_unref(data->src); + free(data); +} + +IsoStreamIface fsrc_stream_class = { + fsrc_open, + fsrc_close, + fsrc_get_size, + fsrc_read, + fsrc_is_repeatable, + fsrc_get_id, + fsrc_get_name, + fsrc_free +}; + +int iso_file_source_stream_new(IsoFileSource *src, IsoStream **stream) +{ + int r; + struct stat info; + IsoStream *str; + FSrcStreamData *data; + + if (src == NULL || stream == NULL) { + return ISO_NULL_POINTER; + } + + r = iso_file_source_stat(src, &info); + if (r < 0) { + return r; + } + if (S_ISDIR(info.st_mode)) { + return ISO_FILE_IS_DIR; + } + + /* check for read access to contents */ + r = iso_file_source_access(src); + if (r < 0) { + return r; + } + + str = malloc(sizeof(IsoStream)); + if (str == NULL) { + return ISO_OUT_OF_MEM; + } + data = malloc(sizeof(FSrcStreamData)); + if (str == NULL) { + free(str); + return ISO_OUT_OF_MEM; + } + + /* take the ref to IsoFileSource */ + data->src = src; + data->size = info.st_size; + + /* get the id numbers */ + { + IsoFilesystem *fs; + unsigned int fs_id; + fs = iso_file_source_get_filesystem(data->src); + + fs_id = fs->get_id(fs); + if (fs_id == 0) { + /* + * the filesystem implementation is unable to provide valid + * st_dev and st_ino fields. Use serial_id. + */ + data->dev_id = (dev_t) 0; + data->ino_id = serial_id++; + } else { + data->dev_id = info.st_dev; + data->ino_id = info.st_ino; + } + } + + str->refcount = 1; + str->data = data; + str->class = &fsrc_stream_class; + + *stream = str; + return ISO_SUCCESS; +} + + + +typedef struct +{ + uint8_t *buf; + ssize_t offset; /* -1 if stream closed */ + ino_t ino_id; + size_t size; +} MemStreamData; + +static +int mem_open(IsoStream *stream) +{ + MemStreamData *data; + if (stream == NULL) { + return ISO_NULL_POINTER; + } + data = (MemStreamData*)stream->data; + if (data->offset != -1) { + return ISO_FILE_ALREADY_OPENNED; + } + data->offset = 0; + return ISO_SUCCESS; +} + +static +int mem_close(IsoStream *stream) +{ + MemStreamData *data; + if (stream == NULL) { + return ISO_NULL_POINTER; + } + data = (MemStreamData*)stream->data; + if (data->offset == -1) { + return ISO_FILE_NOT_OPENNED; + } + data->offset = -1; + return ISO_SUCCESS; +} + +static +off_t mem_get_size(IsoStream *stream) +{ + MemStreamData *data; + data = (MemStreamData*)stream->data; + + return (off_t)data->size; +} + +static +int mem_read(IsoStream *stream, void *buf, size_t count) +{ + size_t len; + MemStreamData *data; + if (stream == NULL || buf == NULL) { + return ISO_NULL_POINTER; + } + if (count == 0) { + return ISO_WRONG_ARG_VALUE; + } + data = stream->data; + + if (data->offset == -1) { + return ISO_FILE_NOT_OPENNED; + } + + if (data->offset >= data->size) { + return 0; /* EOF */ + } + + len = MIN(count, data->size - data->offset); + memcpy(buf, data->buf + data->offset, len); + data->offset += len; + return len; +} + +static +int mem_is_repeatable(IsoStream *stream) +{ + return 1; +} + +static +void mem_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, + ino_t *ino_id) +{ + MemStreamData *data; + data = (MemStreamData*)stream->data; + *fs_id = ISO_MEM_FS_ID; + *dev_id = 0; + *ino_id = data->ino_id; +} + +static +char *mem_get_name(IsoStream *stream) +{ + return strdup("[MEMORY SOURCE]"); +} + +static +void mem_free(IsoStream *stream) +{ + MemStreamData *data; + data = (MemStreamData*)stream->data; + free(data->buf); + free(data); +} + +IsoStreamIface mem_stream_class = { + mem_open, + mem_close, + mem_get_size, + mem_read, + mem_is_repeatable, + mem_get_id, + mem_get_name, + mem_free +}; + +/** + * Create a stream for reading from a arbitrary memory buffer. + * When the Stream refcount reach 0, the buffer is free(3). + * + * @return + * 1 sucess, < 0 error + */ +int iso_memory_stream_new(unsigned char *buf, size_t size, IsoStream **stream) +{ + IsoStream *str; + MemStreamData *data; + + if (buf == NULL || stream == NULL) { + return ISO_NULL_POINTER; + } + + str = malloc(sizeof(IsoStream)); + if (str == NULL) { + return ISO_OUT_OF_MEM; + } + data = malloc(sizeof(MemStreamData)); + if (str == NULL) { + free(str); + return ISO_OUT_OF_MEM; + } + + /* fill data */ + data->buf = buf; + data->size = size; + data->offset = -1; + data->ino_id = mem_serial_id++; + + str->refcount = 1; + str->data = data; + str->class = &mem_stream_class; + + *stream = str; + return ISO_SUCCESS; +} + +void iso_stream_ref(IsoStream *stream) +{ + ++stream->refcount; +} + +void iso_stream_unref(IsoStream *stream) +{ + if (--stream->refcount == 0) { + stream->class->free(stream); + free(stream); + } +} + +inline +int iso_stream_open(IsoStream *stream) +{ + return stream->class->open(stream); +} + +inline +int iso_stream_close(IsoStream *stream) +{ + return stream->class->close(stream); +} + +inline +off_t iso_stream_get_size(IsoStream *stream) +{ + return stream->class->get_size(stream); +} + +inline +int iso_stream_read(IsoStream *stream, void *buf, size_t count) +{ + return stream->class->read(stream, buf, count); +} + +inline +int iso_stream_is_repeatable(IsoStream *stream) +{ + return stream->class->is_repeatable(stream); +} + +inline +void iso_stream_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, + ino_t *ino_id) +{ + stream->class->get_id(stream, fs_id, dev_id, ino_id); +} + +inline +char *iso_stream_get_name(IsoStream *stream) +{ + return stream->class->get_name(stream); +} diff --git a/libisofs/branches/thomas/libisofs/stream.h b/libisofs/branches/thomas/libisofs/stream.h new file mode 100644 index 00000000..1261b379 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/stream.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ +#ifndef LIBISO_STREAM_H_ +#define LIBISO_STREAM_H_ + +/* + * Definitions of streams. + */ +#include "fsource.h" + +/** + * serial number to be used when you can't get a valid id for a Stream by other + * means. If you use this, both fs_id and dev_id should be set to 0. + * This must be incremented each time you get a reference to it. + */ +extern ino_t serial_id; + +/* + * Some functions here will be moved to libisofs.h when we expose + * Streams. + */ + +typedef struct Iso_Stream IsoStream; + +typedef struct IsoStream_Iface +{ + /** + * Opens the stream. + * + * @return + * 1 on success, 2 file greater than expected, 3 file smaller than + * expected, < 0 on error + */ + int (*open)(IsoStream *stream); + + /** + * Close the Stream. + * @return 1 on success, < 0 on error + */ + int (*close)(IsoStream *stream); + + /** + * Get the size (in bytes) of the stream. This function should always + * return the same size, even if the underlying source size changes. + */ + off_t (*get_size)(IsoStream *stream); + + /** + * Attempts to read up to count bytes from the given stream into + * the buffer starting at buf. + * + * The stream must be open() before calling this, and close() when no + * more needed. + * + * @return + * number of bytes read, 0 if EOF, < 0 on error + */ + int (*read)(IsoStream *stream, void *buf, size_t count); + + /** + * Whether this Stram can be read several times, with the same results. + * For example, a regular file is repeatable, you can read it as many + * times as you want. However, a pipe isn't. + * + * This function doesn't take into account if the file has been modified + * between the two reads. + * + * @return + * 1 if stream is repeatable, 0 if not, < 0 on error + */ + int (*is_repeatable)(IsoStream *stream); + + /** + * Get an unique identifier for the IsoStream. + */ + void (*get_id)(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, + ino_t *ino_id); + + /** + * Get a name that identifies the Stream contents. It is used only for + * informational or debug purposes, so you can return anything you + * consider suitable for identification of the source, such as the path. + */ + char *(*get_name)(IsoStream *stream); + + /** + * Free implementation specific data. Should never be called by user. + * Use iso_stream_unref() instead. + */ + void (*free)(IsoStream *stream); +} IsoStreamIface; + +struct Iso_Stream +{ + IsoStreamIface *class; + int refcount; + void *data; +}; + +void iso_stream_ref(IsoStream *stream); +void iso_stream_unref(IsoStream *stream); + +int iso_stream_open(IsoStream *stream); + +int iso_stream_close(IsoStream *stream); + +off_t iso_stream_get_size(IsoStream *stream); + +int iso_stream_read(IsoStream *stream, void *buf, size_t count); + +int iso_stream_is_repeatable(IsoStream *stream); + +void iso_stream_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, + ino_t *ino_id); + +char *iso_stream_get_name(IsoStream *stream); + +/** + * Create a stream to read from a IsoFileSource. + * The stream will take the ref. to the IsoFileSource, so after a successfully + * exectution of this function, you musn't unref() the source, unless you + * take an extra ref. + * + * @return + * 1 sucess, < 0 error + * Possible errors: + * + */ +int iso_file_source_stream_new(IsoFileSource *src, IsoStream **stream); + +/** + * Create a stream for reading from a arbitrary memory buffer. + * When the Stream refcount reach 0, the buffer is free(3). + * + * @return + * 1 sucess, < 0 error + */ +int iso_memory_stream_new(unsigned char *buf, size_t size, IsoStream **stream); + +#endif /*STREAM_H_*/ diff --git a/libisofs/branches/thomas/libisofs/tree.c b/libisofs/branches/thomas/libisofs/tree.c new file mode 100644 index 00000000..5dc360ed --- /dev/null +++ b/libisofs/branches/thomas/libisofs/tree.c @@ -0,0 +1,751 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/* + * Functions that act on the iso tree. + */ + +#include "libisofs.h" +#include "node.h" +#include "image.h" +#include "fsource.h" +#include "builder.h" +#include "messages.h" +#include "tree.h" + +#include +#include +#include +#include +#include +#include + +/** + * Add a new directory to the iso tree. + * + * @param parent + * the dir where the new directory will be created + * @param name + * name for the new dir. If a node with same name already exists on + * parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. + * @param dir + * place where to store a pointer to the newly created dir. No extra + * ref is addded, so you will need to call iso_node_ref() if you really + * need it. You can pass NULL in this parameter if you don't need the + * pointer. + * @return + * number of nodes in dir if succes, < 0 otherwise + * Possible errors: + * ISO_NULL_POINTER, if parent or name are NULL + * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists + */ +int iso_tree_add_new_dir(IsoDir *parent, const char *name, IsoDir **dir) +{ + int ret; + char *n; + IsoDir *node; + IsoNode **pos; + time_t now; + + if (parent == NULL || name == NULL) { + return ISO_NULL_POINTER; + } + if (dir) { + *dir = NULL; + } + + /* find place where to insert and check if it exists */ + if (iso_dir_exists(parent, name, &pos)) { + /* a node with same name already exists */ + return ISO_NODE_NAME_NOT_UNIQUE; + } + + n = strdup(name); + ret = iso_node_new_dir(n, &node); + if (ret < 0) { + free(n); + return ret; + } + + /* permissions from parent */ + iso_node_set_permissions((IsoNode*)node, parent->node.mode); + iso_node_set_uid((IsoNode*)node, parent->node.uid); + iso_node_set_gid((IsoNode*)node, parent->node.gid); + iso_node_set_hidden((IsoNode*)node, parent->node.hidden); + + /* current time */ + now = time(NULL); + iso_node_set_atime((IsoNode*)node, now); + iso_node_set_ctime((IsoNode*)node, now); + iso_node_set_mtime((IsoNode*)node, now); + + if (dir) { + *dir = node; + } + + /* add to dir */ + return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER); +} + +/** + * Add a new symlink to the directory tree. Permissions are set to 0777, + * owner and hidden atts are taken from parent. You can modify any of them + * later. + * + * @param parent + * the dir where the new symlink will be created + * @param name + * name for the new dir. If a node with same name already exists on + * parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. + * @param dest + * destination of the link + * @param link + * place where to store a pointer to the newly created link. No extra + * ref is addded, so you will need to call iso_node_ref() if you really + * need it. You can pass NULL in this parameter if you don't need the + * pointer + * @return + * number of nodes in parent if success, < 0 otherwise + * Possible errors: + * ISO_NULL_POINTER, if parent, name or dest are NULL + * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists + * ISO_OUT_OF_MEM + */ +int iso_tree_add_new_symlink(IsoDir *parent, const char *name, + const char *dest, IsoSymlink **link) +{ + int ret; + char *n, *d; + IsoSymlink *node; + IsoNode **pos; + time_t now; + + if (parent == NULL || name == NULL || dest == NULL) { + return ISO_NULL_POINTER; + } + if (link) { + *link = NULL; + } + + /* find place where to insert */ + if (iso_dir_exists(parent, name, &pos)) { + /* a node with same name already exists */ + return ISO_NODE_NAME_NOT_UNIQUE; + } + + n = strdup(name); + d = strdup(dest); + ret = iso_node_new_symlink(n, d, &node); + if (ret < 0) { + free(n); + free(d); + return ret; + } + + /* permissions from parent */ + iso_node_set_permissions((IsoNode*)node, 0777); + iso_node_set_uid((IsoNode*)node, parent->node.uid); + iso_node_set_gid((IsoNode*)node, parent->node.gid); + iso_node_set_hidden((IsoNode*)node, parent->node.hidden); + + /* current time */ + now = time(NULL); + iso_node_set_atime((IsoNode*)node, now); + iso_node_set_ctime((IsoNode*)node, now); + iso_node_set_mtime((IsoNode*)node, now); + + if (link) { + *link = node; + } + + /* add to dir */ + return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER); +} + +/** + * Add a new special file to the directory tree. As far as libisofs concerns, + * an special file is a block device, a character device, a FIFO (named pipe) + * or a socket. You can choose the specific kind of file you want to add + * by setting mode propertly (see man 2 stat). + * + * Note that special files are only written to image when Rock Ridge + * extensions are enabled. Moreover, a special file is just a directory entry + * in the image tree, no data is written beyond that. + * + * Owner and hidden atts are taken from parent. You can modify any of them + * later. + * + * @param parent + * the dir where the new special file will be created + * @param name + * name for the new special file. If a node with same name already exists + * on parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. + * @param mode + * file type and permissions for the new node. Note that you can't + * specify any kind of file here, only special types are allowed. i.e, + * S_IFSOCK, S_IFBLK, S_IFCHR and S_IFIFO are valid types; S_IFLNK, + * S_IFREG and S_IFDIR aren't. + * @param dev + * device ID, equivalent to the st_rdev field in man 2 stat. + * @param special + * place where to store a pointer to the newly created special file. No + * extra ref is addded, so you will need to call iso_node_ref() if you + * really need it. You can pass NULL in this parameter if you don't need + * the pointer. + * @return + * number of nodes in parent if success, < 0 otherwise + * Possible errors: + * ISO_NULL_POINTER, if parent, name or dest are NULL + * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists + * ISO_OUT_OF_MEM + * + */ +int iso_tree_add_new_special(IsoDir *parent, const char *name, mode_t mode, + dev_t dev, IsoSpecial **special) +{ + int ret; + char *n; + IsoSpecial *node; + IsoNode **pos; + time_t now; + + if (parent == NULL || name == NULL) { + return ISO_NULL_POINTER; + } + if (S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)) { + return ISO_WRONG_ARG_VALUE; + } + if (special) { + *special = NULL; + } + + /* find place where to insert */ + if (iso_dir_exists(parent, name, &pos)) { + /* a node with same name already exists */ + return ISO_NODE_NAME_NOT_UNIQUE; + } + + n = strdup(name); + ret = iso_node_new_special(n, mode, dev, &node); + if (ret < 0) { + free(n); + return ret; + } + + /* atts from parent */ + iso_node_set_uid((IsoNode*)node, parent->node.uid); + iso_node_set_gid((IsoNode*)node, parent->node.gid); + iso_node_set_hidden((IsoNode*)node, parent->node.hidden); + + /* current time */ + now = time(NULL); + iso_node_set_atime((IsoNode*)node, now); + iso_node_set_ctime((IsoNode*)node, now); + iso_node_set_mtime((IsoNode*)node, now); + + if (special) { + *special = node; + } + + /* add to dir */ + return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER); +} + +/** + * Set whether to follow or not symbolic links when added a file from a source + * to IsoImage. + */ +void iso_tree_set_follow_symlinks(IsoImage *image, int follow) +{ + image->follow_symlinks = follow ? 1 : 0; +} + +/** + * Get current setting for follow_symlinks. + * + * @see iso_tree_set_follow_symlinks + */ +int iso_tree_get_follow_symlinks(IsoImage *image) +{ + return image->follow_symlinks; +} + +/** + * Set whether to skip or not hidden files when adding a directory recursibely. + * Default behavior is to not ignore them, i.e., to add hidden files to image. + */ +void iso_tree_set_ignore_hidden(IsoImage *image, int skip) +{ + image->ignore_hidden = skip ? 1 : 0; +} + +/** + * Get current setting for ignore_hidden. + * + * @see iso_tree_set_ignore_hidden + */ +int iso_tree_get_ignore_hidden(IsoImage *image) +{ + return image->ignore_hidden; +} + +void iso_tree_set_replace_mode(IsoImage *image, enum iso_replace_mode mode) +{ + image->replace = mode; +} + +enum iso_replace_mode iso_tree_get_replace_mode(IsoImage *image) +{ + return image->replace; +} + +/** + * Set whether to skip or not special files. Default behavior is to not skip + * them. Note that, despite of this setting, special files won't never be added + * to an image unless RR extensions were enabled. + * + * @param skip + * Bitmask to determine what kind of special files will be skipped: + * bit0: ignore FIFOs + * bit1: ignore Sockets + * bit2: ignore char devices + * bit3: ignore block devices + */ +void iso_tree_set_ignore_special(IsoImage *image, int skip) +{ + image->ignore_special = skip & 0x0F; +} + +/** + * Get current setting for ignore_special. + * + * @see iso_tree_set_ignore_special + */ +int iso_tree_get_ignore_special(IsoImage *image) +{ + return image->ignore_special; +} + +/** + * Set a callback function that libisofs will call for each file that is + * added to the given image by a recursive addition function. This includes + * image import. + * + * @param report + * pointer to a function that will be called just before a file will be + * added to the image. You can control whether the file will be in fact + * added or ignored. + * This function should return 1 to add the file, 0 to ignore it and + * continue, < 0 to abort the process + * NULL is allowed if you don't want any callback. + */ +void iso_tree_set_report_callback(IsoImage *image, + int (*report)(IsoImage*, IsoFileSource*)) +{ + image->report = report; +} + +/** + * Add a excluded path. These are paths that won't never added to image, + * and will be excluded even when adding recursively its parent directory. + * + * For example, in + * + * iso_tree_add_exclude(image, "/home/user/data/private"); + * iso_tree_add_dir_rec(image, root, "/home/user/data"); + * + * the directory /home/user/data/private won't be added to image. + * + * @return + * 1 on success, < 0 on error + */ +int iso_tree_add_exclude(IsoImage *image, const char *path) +{ + if (image == NULL || path == NULL) { + return ISO_NULL_POINTER; + } + image->excludes = realloc(image->excludes, ++image->nexcludes * + sizeof(void*)); + if (image->excludes == NULL) { + return ISO_OUT_OF_MEM; + } + image->excludes[image->nexcludes - 1] = strdup(path); + if (image->excludes[image->nexcludes - 1] == NULL) { + return ISO_OUT_OF_MEM; + } + return ISO_SUCCESS; +} + +/** + * Remove a previously added exclude. + * + * @see iso_tree_add_exclude + * @return + * 1 on success, 0 exclude do not exists, < 0 on error + */ +int iso_tree_remove_exclude(IsoImage *image, const char *path) +{ + size_t i, j; + + if (image == NULL || path == NULL) { + return ISO_NULL_POINTER; + } + + for (i = 0; i < image->nexcludes; ++i) { + if (strcmp(image->excludes[i], path) == 0) { + /* exclude found */ + free(image->excludes[i]); + --image->nexcludes; + for (j = i; j < image->nexcludes; ++j) { + image->excludes[j] = image->excludes[j+1]; + } + image->excludes = realloc(image->excludes, image->nexcludes * + sizeof(void*)); + return ISO_SUCCESS; + } + } + return 0; +} + +static +int iso_tree_add_node_builder(IsoImage *image, IsoDir *parent, + IsoFileSource *src, IsoNodeBuilder *builder, + IsoNode **node) +{ + int result; + IsoNode *new; + IsoNode **pos; + char *name; + + if (parent == NULL || src == NULL || builder == NULL) { + return ISO_NULL_POINTER; + } + if (node) { + *node = NULL; + } + + name = iso_file_source_get_name(src); + + /* find place where to insert */ + result = iso_dir_exists(parent, name, &pos); + free(name); + if (result) { + /* a node with same name already exists */ + return ISO_NODE_NAME_NOT_UNIQUE; + } + + result = builder->create_node(builder, image, src, &new); + if (result < 0) { + return result; + } + + if (node) { + *node = new; + } + + /* finally, add node to parent */ + return iso_dir_insert(parent, (IsoNode*)new, pos, ISO_REPLACE_NEVER); +} + +int iso_tree_add_node(IsoImage *image, IsoDir *parent, const char *path, + IsoNode **node) +{ + int result; + IsoFilesystem *fs; + IsoFileSource *file; + + if (image == NULL || parent == NULL || path == NULL) { + return ISO_NULL_POINTER; + } + + fs = image->fs; + result = fs->get_by_path(fs, path, &file); + if (result < 0) { + return result; + } + result = iso_tree_add_node_builder(image, parent, file, image->builder, + node); + /* free the file */ + iso_file_source_unref(file); + return result; +} + +static +int check_excludes(IsoImage *image, const char *path) +{ + int i; + + for (i = 0; i < image->nexcludes; ++i) { + char *exclude = image->excludes[i]; + if (exclude[0] == '/') { + /* absolute exclude, must completely match path */ + if (!fnmatch(exclude, path, FNM_PERIOD|FNM_PATHNAME)) { + return 1; + } + } else { + /* relative exclude, it is enought if a part of the path matches */ + char *pos = (char*)path; + while (pos != NULL) { + pos++; + if (!fnmatch(exclude, pos, FNM_PERIOD|FNM_PATHNAME)) { + return 1; + } + pos = strchr(pos, '/'); + } + } + } + return 0; +} + +static +int check_hidden(IsoImage *image, const char *name) +{ + return (image->ignore_hidden && name[0] == '.'); +} + +static +int check_special(IsoImage *image, mode_t mode) +{ + if (image->ignore_special != 0) { + switch(mode & S_IFMT) { + case S_IFBLK: + return image->ignore_special & 0x08 ? 1 : 0; + case S_IFCHR: + return image->ignore_special & 0x04 ? 1 : 0; + case S_IFSOCK: + return image->ignore_special & 0x02 ? 1 : 0; + case S_IFIFO: + return image->ignore_special & 0x01 ? 1 : 0; + default: + return 0; + } + } + return 0; +} + +/** + * Recursively add a given directory to the image tree. + * + * @return + * 1 continue, < 0 error (ISO_CANCELED stop) + */ +int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir) +{ + int ret; + IsoNodeBuilder *builder; + IsoFileSource *file; + IsoNode **pos; + struct stat info; + char *name, *path; + IsoNode *new; + enum iso_replace_mode replace; + + ret = iso_file_source_open(dir); + if (ret < 0) { + char *path = iso_file_source_get_path(dir); + /* instead of the probable error, we throw a sorry event */ + ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret, + "Can't open dir %s", path); + free(path); + return ret; + } + + builder = image->builder; + + /* iterate over all directory children */ + while (1) { + int skip = 0; + + ret = iso_file_source_readdir(dir, &file); + if (ret <= 0) { + if (ret < 0) { + /* error reading dir */ + ret = iso_msg_submit(image->id, ret, ret, "Error reading dir"); + } + break; + } + + path = iso_file_source_get_path(file); + name = strrchr(path, '/') + 1; + + if (image->follow_symlinks) { + ret = iso_file_source_stat(file, &info); + } else { + ret = iso_file_source_lstat(file, &info); + } + if (ret < 0) { + goto dir_rec_continue; + } + + if (check_excludes(image, path)) { + iso_msg_debug(image->id, "Skipping excluded file %s", path); + skip = 1; + } else if (check_hidden(image, name)) { + iso_msg_debug(image->id, "Skipping hidden file %s", path); + skip = 1; + } else if (check_special(image, info.st_mode)) { + iso_msg_debug(image->id, "Skipping special file %s", path); + skip = 1; + } + + if (skip) { + goto dir_rec_continue; + } + + replace = image->replace; + + /* find place where to insert */ + ret = iso_dir_exists(parent, name, &pos); + /* TODO + * if (ret && replace == ISO_REPLACE_ASK) { + * replace = /.... + * } + */ + + /* chek if we must insert or not */ + /* TODO check for other replace behavior */ + if (ret && (replace == ISO_REPLACE_NEVER)) { + /* skip file */ + goto dir_rec_continue; + } + + /* if we are here we must insert. Give user a chance for cancel */ + if (image->report) { + int r = image->report(image, file); + if (r <= 0) { + ret = (r < 0 ? ISO_CANCELED : ISO_SUCCESS); + goto dir_rec_continue; + } + } + ret = builder->create_node(builder, image, file, &new); + if (ret < 0) { + ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret, + "Error when adding file %s", path); + goto dir_rec_continue; + } + + /* ok, node has correctly created, we need to add it */ + ret = iso_dir_insert(parent, new, pos, replace); + if (ret < 0) { + iso_node_unref(new); + if (ret != ISO_NODE_NAME_NOT_UNIQUE) { + /* error */ + goto dir_rec_continue; + } else { + /* file ignored because a file with same node already exists */ + iso_msg_debug(image->id, "Skipping file %s. A node with same " + "file already exists", path); + ret = 0; + } + } else { + iso_msg_debug(image->id, "Added file %s", path); + } + + /* finally, if the node is a directory we need to recurse */ + if (new->type == LIBISO_DIR && S_ISDIR(info.st_mode)) { + ret = iso_add_dir_src_rec(image, (IsoDir*)new, file); + } + +dir_rec_continue:; + free(path); + iso_file_source_unref(file); + + /* check for error severity to decide what to do */ + if (ret < 0) { + ret = iso_msg_submit(image->id, ret, 0, NULL); + if (ret < 0) { + break; + } + } + } /* while */ + + iso_file_source_close(dir); + return ret < 0 ? ret : ISO_SUCCESS; +} + +int iso_tree_add_dir_rec(IsoImage *image, IsoDir *parent, const char *dir) +{ + int result; + struct stat info; + IsoFilesystem *fs; + IsoFileSource *file; + + if (image == NULL || parent == NULL || dir == NULL) { + return ISO_NULL_POINTER; + } + + fs = image->fs; + result = fs->get_by_path(fs, dir, &file); + if (result < 0) { + return result; + } + + /* we also allow dir path to be a symlink to a dir */ + result = iso_file_source_stat(file, &info); + if (result < 0) { + iso_file_source_unref(file); + return result; + } + + if (!S_ISDIR(info.st_mode)) { + iso_file_source_unref(file); + return ISO_FILE_IS_NOT_DIR; + } + result = iso_add_dir_src_rec(image, parent, file); + iso_file_source_unref(file); + return result; +} + +int iso_tree_path_to_node(IsoImage *image, const char *path, IsoNode **node) +{ + int result; + IsoNode *n; + IsoDir *dir; + char *ptr, *brk_info, *component; + + if (image == NULL || path == NULL) { + return ISO_NULL_POINTER; + } + + /* get the first child at the root of the image that is "/" */ + dir = image->root; + n = (IsoNode *)dir; + if (!strcmp(path, "/")) { + if (node) { + *node = n; + } + return ISO_SUCCESS; + } + + ptr = strdup(path); + result = 0; + + /* get the first component of the path */ + component = strtok_r(ptr, "/", &brk_info); + while (component) { + if (n->type != LIBISO_DIR) { + n = NULL; + break; + } + dir = (IsoDir *)n; + + result = iso_dir_get_node(dir, component, &n); + if (result != 1) { + n = NULL; + break; + } + + component = strtok_r(NULL, "/", &brk_info); + } + + free(ptr); + if (node) { + *node = n; + } + return result; +} diff --git a/libisofs/branches/thomas/libisofs/tree.h b/libisofs/branches/thomas/libisofs/tree.h new file mode 100644 index 00000000..9c5347c8 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/tree.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ +#ifndef LIBISO_IMAGE_TREE_H_ +#define LIBISO_IMAGE_TREE_H_ + +#include "image.h" + +/** + * Recursively add a given directory to the image tree. + * + * @return + * 1 continue, 0 stop, < 0 error + */ +int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir); + +#endif /*LIBISO_IMAGE_TREE_H_*/ diff --git a/libisofs/branches/thomas/libisofs/util.c b/libisofs/branches/thomas/libisofs/util.c new file mode 100644 index 00000000..6317a8ba --- /dev/null +++ b/libisofs/branches/thomas/libisofs/util.c @@ -0,0 +1,1264 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * Copyright (c) 2007 Mario Danic + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "util.h" +#include "libisofs.h" +#include "../version.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* if we don't have eaccess, we check file access by openning it */ +#ifndef HAVE_EACCESS +#include +#include +#include +#endif + +int int_pow(int base, int power) +{ + int result = 1; + while (--power >= 0) { + result *= base; + } + return result; +} + +int strconv(const char *str, const char *icharset, const char *ocharset, + char **output) +{ + size_t inbytes; + size_t outbytes; + size_t n; + iconv_t conv; + char *out; + char *src; + char *ret; + + inbytes = strlen(str); + outbytes = (inbytes + 1) * MB_LEN_MAX; + out = alloca(outbytes); + if (out == NULL) { + return ISO_OUT_OF_MEM; + } + + conv = iconv_open(ocharset, icharset); + if (conv == (iconv_t)(-1)) { + return ISO_CHARSET_CONV_ERROR; + } + src = (char *)str; + ret = (char *)out; + + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + if (n == -1) { + /* error */ + iconv_close(conv); + return ISO_CHARSET_CONV_ERROR; + } + *ret = '\0'; + iconv_close(conv); + + *output = malloc(ret - out + 1); + if (*output == NULL) { + return ISO_OUT_OF_MEM; + } + memcpy(*output, out, ret - out + 1); + return ISO_SUCCESS; +} + +int strnconv(const char *str, const char *icharset, const char *ocharset, + size_t len, char **output) +{ + size_t inbytes; + size_t outbytes; + size_t n; + iconv_t conv; + char *out; + char *src; + char *ret; + + inbytes = len; + outbytes = (inbytes + 1) * MB_LEN_MAX; + out = alloca(outbytes); + if (out == NULL) { + return ISO_OUT_OF_MEM; + } + + conv = iconv_open(ocharset, icharset); + if (conv == (iconv_t)(-1)) { + return ISO_CHARSET_CONV_ERROR; + } + src = (char *)str; + ret = (char *)out; + + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + if (n == -1) { + /* error */ + iconv_close(conv); + return ISO_CHARSET_CONV_ERROR; + } + *ret = '\0'; + iconv_close(conv); + + *output = malloc(ret - out + 1); + if (*output == NULL) { + return ISO_OUT_OF_MEM; + } + memcpy(*output, out, ret - out + 1); + return ISO_SUCCESS; +} + +/** + * Convert a str in a specified codeset to WCHAR_T. + * The result must be free() when no more needed + * + * @return + * 1 success, < 0 error + */ +static +int str2wchar(const char *icharset, const char *input, wchar_t **output) +{ + iconv_t conv; + size_t inbytes; + size_t outbytes; + char *ret; + char *src; + wchar_t *wstr; + size_t n; + + if (icharset == NULL || input == NULL || output == NULL) { + return ISO_NULL_POINTER; + } + + conv = iconv_open("WCHAR_T", icharset); + if (conv == (iconv_t)-1) { + return ISO_CHARSET_CONV_ERROR; + } + + inbytes = strlen(input); + outbytes = (inbytes + 1) * sizeof(wchar_t); + + /* we are sure that numchars <= inbytes */ + wstr = malloc(outbytes); + if (wstr == NULL) { + return ISO_OUT_OF_MEM; + } + ret = (char *)wstr; + src = (char *)input; + + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + while (n == -1) { + + if (errno == E2BIG) { + /* error, should never occur */ + iconv_close(conv); + free(wstr); + return ISO_CHARSET_CONV_ERROR; + } else { + wchar_t *wret; + + /* + * Invalid input string charset. + * This can happen if input is in fact encoded in a charset + * different than icharset. + * We can't do anything better than replace by "_" and continue. + */ + inbytes--; + src++; + + wret = (wchar_t*) ret; + *wret++ = (wchar_t) '_'; + ret = (char *) wret; + outbytes -= sizeof(wchar_t); + + if (!inbytes) + break; + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + } + } + iconv_close(conv); + + *( (wchar_t *)ret )='\0'; + *output = wstr; + return ISO_SUCCESS; +} + +int str2ascii(const char *icharset, const char *input, char **output) +{ + int result; + wchar_t *wsrc_; + char *ret; + char *ret_; + char *src; + iconv_t conv; + size_t numchars; + size_t outbytes; + size_t inbytes; + size_t n; + + if (icharset == NULL || input == NULL || output == NULL) { + return ISO_NULL_POINTER; + } + + /* convert the string to a wide character string. Note: outbytes + * is in fact the number of characters in the string and doesn't + * include the last NULL character. + */ + result = str2wchar(icharset, input, &wsrc_); + if (result < 0) { + return result; + } + src = (char *)wsrc_; + numchars = wcslen(wsrc_); + + inbytes = numchars * sizeof(wchar_t); + + ret_ = malloc(numchars + 1); + if (ret_ == NULL) { + return ISO_OUT_OF_MEM; + } + outbytes = numchars; + ret = ret_; + + /* initialize iconv */ + conv = iconv_open("ASCII", "WCHAR_T"); + if (conv == (iconv_t)-1) { + free(wsrc_); + free(ret_); + return ISO_CHARSET_CONV_ERROR; + } + + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + while (n == -1) { + /* The destination buffer is too small. Stops here. */ + if (errno == E2BIG) + break; + + /* An incomplete multi bytes sequence was found. We + * can't do anything here. That's quite unlikely. */ + if (errno == EINVAL) + break; + + /* The last possible error is an invalid multi bytes + * sequence. Just replace the character with a "_". + * Probably the character doesn't exist in ascii like + * "é, è, à, ç, ..." in French. */ + *ret++ = '_'; + outbytes--; + + if (!outbytes) + break; + + /* There was an error with one character but some other remain + * to be converted. That's probably a multibyte character. + * See above comment. */ + src += sizeof(wchar_t); + inbytes -= sizeof(wchar_t); + + if (!inbytes) + break; + + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + } + + iconv_close(conv); + + *ret='\0'; + free(wsrc_); + + *output = ret_; + return ISO_SUCCESS; +} + +static +void set_ucsbe(uint16_t *ucs, char c) +{ + char *v = (char*)ucs; + v[0] = (char)0; + v[1] = c; +} + +/** + * @return + * -1, 0, 1 if *ucs <, == or > than c + */ +static +int cmp_ucsbe(const uint16_t *ucs, char c) +{ + char *v = (char*)ucs; + if (v[0] != 0) { + return 1; + } else if (v[1] == c) { + return 0; + } else { + return (uint8_t)c > (uint8_t)v[1] ? -1 : 1; + } +} + +int str2ucs(const char *icharset, const char *input, uint16_t **output) +{ + int result; + wchar_t *wsrc_; + char *src; + char *ret; + char *ret_; + iconv_t conv; + size_t numchars; + size_t outbytes; + size_t inbytes; + size_t n; + + if (icharset == NULL || input == NULL || output == NULL) { + return ISO_NULL_POINTER; + } + + /* convert the string to a wide character string. Note: outbytes + * is in fact the number of characters in the string and doesn't + * include the last NULL character. + */ + result = str2wchar(icharset, input, &wsrc_); + if (result < 0) { + return result; + } + src = (char *)wsrc_; + numchars = wcslen(wsrc_); + + inbytes = numchars * sizeof(wchar_t); + + ret_ = malloc((numchars+1) * sizeof(uint16_t)); + if (ret_ == NULL) { + return ISO_OUT_OF_MEM; + } + outbytes = numchars * sizeof(uint16_t); + ret = ret_; + + /* initialize iconv */ + conv = iconv_open("UCS-2BE", "WCHAR_T"); + if (conv == (iconv_t)-1) { + free(wsrc_); + free(ret_); + return ISO_CHARSET_CONV_ERROR; + } + + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + while (n == -1) { + /* The destination buffer is too small. Stops here. */ + if (errno == E2BIG) + break; + + /* An incomplete multi bytes sequence was found. We + * can't do anything here. That's quite unlikely. */ + if (errno == EINVAL) + break; + + /* The last possible error is an invalid multi bytes + * sequence. Just replace the character with a "_". + * Probably the character doesn't exist in UCS */ + set_ucsbe((uint16_t*) ret, '_'); + ret += sizeof(uint16_t); + outbytes -= sizeof(uint16_t); + + if (!outbytes) + break; + + /* There was an error with one character but some other remain + * to be converted. That's probably a multibyte character. + * See above comment. */ + src += sizeof(wchar_t); + inbytes -= sizeof(wchar_t); + + if (!inbytes) + break; + + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + } + + iconv_close(conv); + + /* close the ucs string */ + set_ucsbe((uint16_t*) ret, '\0'); + free(wsrc_); + + *output = (uint16_t*)ret_; + return ISO_SUCCESS; +} + +static int valid_d_char(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_'); +} + +static int valid_a_char(char c) +{ + return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?') || + (c >= 'A' && c <= 'Z') || (c == '_'); +} + +static int valid_j_char(uint16_t c) +{ + return cmp_ucsbe(&c, ' ') != -1 && cmp_ucsbe(&c, '*') && cmp_ucsbe(&c, '/') + && cmp_ucsbe(&c, ':') && cmp_ucsbe(&c, ';') && cmp_ucsbe(&c, '?') + && cmp_ucsbe(&c, '\\'); +} + +static +char *iso_dirid(const char *src, int size) +{ + size_t len, i; + char name[32]; + + len = strlen(src); + if (len > size) { + len = size; + } + for (i = 0; i < len; i++) { + char c= toupper(src[i]); + name[i] = valid_d_char(c) ? c : '_'; + } + + name[len] = '\0'; + return strdup(name); +} + +char *iso_1_dirid(const char *src) +{ + return iso_dirid(src, 8); +} + +char *iso_2_dirid(const char *src) +{ + return iso_dirid(src, 31); +} + +char *iso_1_fileid(const char *src) +{ + char *dot; /* Position of the last dot in the filename, will be used + * to calculate lname and lext. */ + int lname, lext, pos, i; + char dest[13]; /* 13 = 8 (name) + 1 (.) + 3 (ext) + 1 (\0) */ + + if (src == NULL) { + return NULL; + } + dot = strrchr(src, '.'); + + lext = dot ? strlen(dot + 1) : 0; + lname = strlen(src) - lext - (dot ? 1 : 0); + + /* If we can't build a filename, return NULL. */ + if (lname == 0 && lext == 0) { + return NULL; + } + + pos = 0; + + /* Convert up to 8 characters of the filename. */ + for (i = 0; i < lname && i < 8; i++) { + char c= toupper(src[i]); + + dest[pos++] = valid_d_char(c) ? c : '_'; + } + + /* This dot is mandatory, even if there is no extension. */ + dest[pos++] = '.'; + + /* Convert up to 3 characters of the extension, if any. */ + for (i = 0; i < lext && i < 3; i++) { + char c= toupper(src[lname + 1 + i]); + + dest[pos++] = valid_d_char(c) ? c : '_'; + } + + dest[pos] = '\0'; + return strdup(dest); +} + +char *iso_2_fileid(const char *src) +{ + char *dot; + int lname, lext, lnname, lnext, pos, i; + char dest[32]; /* 32 = 30 (name + ext) + 1 (.) + 1 (\0) */ + + if (src == NULL) { + return NULL; + } + + dot = strrchr(src, '.'); + + /* + * Since the maximum length can be divided freely over the name and + * extension, we need to calculate their new lengths (lnname and + * lnext). If the original filename is too long, we start by trimming + * the extension, but keep a minimum extension length of 3. + */ + if (dot == NULL || *(dot + 1) == '\0') { + lname = strlen(src); + lnname = (lname > 30) ? 30 : lname; + lext = lnext = 0; + } else { + lext = strlen(dot + 1); + lname = strlen(src) - lext - 1; + lnext = (strlen(src) > 31 && lext > 3) ? (lname < 27 ? 30 - lname : 3) + : lext; + lnname = (strlen(src) > 31) ? 30 - lnext : lname; + } + + if (lnname == 0 && lnext == 0) { + return NULL; + } + + pos = 0; + + /* Convert up to lnname characters of the filename. */ + for (i = 0; i < lnname; i++) { + char c= toupper(src[i]); + + dest[pos++] = valid_d_char(c) ? c : '_'; + } + dest[pos++] = '.'; + + /* Convert up to lnext characters of the extension, if any. */ + for (i = 0; i < lnext; i++) { + char c= toupper(src[lname + 1 + i]); + + dest[pos++] = valid_d_char(c) ? c : '_'; + } + dest[pos] = '\0'; + return strdup(dest); +} + +/** + * Create a dir name suitable for an ISO image with relaxed constraints. + * + * @param size + * Max len for the name + * @param relaxed + * 0 only allow d-characters, 1 allow also lowe case chars, + * 2 allow all characters + */ +char *iso_r_dirid(const char *src, int size, int relaxed) +{ + size_t len, i; + char *dest; + + len = strlen(src); + if (len > size) { + len = size; + } + dest = malloc(len + 1); + for (i = 0; i < len; i++) { + char c= src[i]; + if (relaxed == 2) { + /* all chars are allowed */ + dest[i] = c; + } else if (valid_d_char(c)) { + /* it is a valid char */ + dest[i] = c; + } else { + c= toupper(src[i]); + if (valid_d_char(c)) { + if (relaxed) { + /* lower chars are allowed */ + dest[i] = src[i]; + } else { + dest[i] = c; + } + } else { + dest[i] = '_'; + } + } + } + + dest[len] = '\0'; + return dest; +} + +/** + * Create a file name suitable for an ISO image with relaxed constraints. + * + * @param len + * Max len for the name, without taken the "." into account. + * @param relaxed + * 0 only allow d-characters, 1 allow also lowe case chars, + * 2 allow all characters + * @param forcedot + * Whether to ensure that "." is added + */ +char *iso_r_fileid(const char *src, size_t len, int relaxed, int forcedot) +{ + char *dot; + int lname, lext, lnname, lnext, pos, i; + char *dest = alloca(len + 1 + 1); + + if (src == NULL) { + return NULL; + } + + dot = strrchr(src, '.'); + + /* + * Since the maximum length can be divided freely over the name and + * extension, we need to calculate their new lengths (lnname and + * lnext). If the original filename is too long, we start by trimming + * the extension, but keep a minimum extension length of 3. + */ + if (dot == NULL || *(dot + 1) == '\0') { + lname = strlen(src); + lnname = (lname > len) ? len : lname; + lext = lnext = 0; + } else { + lext = strlen(dot + 1); + lname = strlen(src) - lext - 1; + lnext = (strlen(src) > len + 1 && lext > 3) ? + (lname < len - 3 ? len - lname : 3) + : lext; + lnname = (strlen(src) > len + 1) ? len - lnext : lname; + } + + if (lnname == 0 && lnext == 0) { + return NULL; + } + + pos = 0; + + /* Convert up to lnname characters of the filename. */ + for (i = 0; i < lnname; i++) { + char c= src[i]; + if (relaxed == 2) { + /* all chars are allowed */ + dest[pos++] = c; + } else if (valid_d_char(c)) { + /* it is a valid char */ + dest[pos++] = c; + } else { + c= toupper(src[i]); + if (valid_d_char(c)) { + if (relaxed) { + /* lower chars are allowed */ + dest[pos++] = src[i]; + } else { + dest[pos++] = c; + } + } else { + dest[pos++] = '_'; + } + } + } + if (lnext > 0 || forcedot) { + dest[pos++] = '.'; + } + + /* Convert up to lnext characters of the extension, if any. */ + for (i = lname + 1; i < lname + 1 + lnext; i++) { + char c= src[i]; + if (relaxed == 2) { + /* all chars are allowed */ + dest[pos++] = c; + } else if (valid_d_char(c)) { + /* it is a valid char */ + dest[pos++] = c; + } else { + c= toupper(src[i]); + if (valid_d_char(c)) { + if (relaxed) { + /* lower chars are allowed */ + dest[pos++] = src[i]; + } else { + dest[pos++] = c; + } + } else { + dest[pos++] = '_'; + } + } + } + dest[pos] = '\0'; + return strdup(dest); +} + +uint16_t *iso_j_file_id(const uint16_t *src) +{ + uint16_t *dot; + size_t lname, lext, lnname, lnext, pos, i; + uint16_t dest[66]; /* 66 = 64 (name + ext) + 1 (.) + 1 (\0) */ + + if (src == NULL) { + return NULL; + } + + dot = ucsrchr(src, '.'); + + /* + * Since the maximum length can be divided freely over the name and + * extension, we need to calculate their new lengths (lnname and + * lnext). If the original filename is too long, we start by trimming + * the extension, but keep a minimum extension length of 3. + */ + if (dot == NULL || cmp_ucsbe(dot + 1, '\0') == 0) { + lname = ucslen(src); + lnname = (lname > 64) ? 64 : lname; + lext = lnext = 0; + } else { + lext = ucslen(dot + 1); + lname = ucslen(src) - lext - 1; + lnext = (ucslen(src) > 65 && lext > 3) ? (lname < 61 ? 64 - lname : 3) + : lext; + lnname = (ucslen(src) > 65) ? 64 - lnext : lname; + } + + if (lnname == 0 && lnext == 0) { + return NULL; + } + + pos = 0; + + /* Convert up to lnname characters of the filename. */ + for (i = 0; i < lnname; i++) { + uint16_t c = src[i]; + if (valid_j_char(c)) { + dest[pos++] = c; + } else { + set_ucsbe(dest + pos, '_'); + pos++; + } + } + set_ucsbe(dest + pos, '.'); + pos++; + + /* Convert up to lnext characters of the extension, if any. */ + for (i = 0; i < lnext; i++) { + uint16_t c = src[lname + 1 + i]; + if (valid_j_char(c)) { + dest[pos++] = c; + } else { + set_ucsbe(dest + pos, '_'); + pos++; + } + } + set_ucsbe(dest + pos, '\0'); + return ucsdup(dest); +} + +uint16_t *iso_j_dir_id(const uint16_t *src) +{ + size_t len, i; + uint16_t dest[65]; /* 65 = 64 + 1 (\0) */ + + if (src == NULL) { + return NULL; + } + + len = ucslen(src); + if (len > 64) { + len = 64; + } + for (i = 0; i < len; i++) { + uint16_t c = src[i]; + if (valid_j_char(c)) { + dest[i] = c; + } else { + set_ucsbe(dest + i, '_'); + } + } + set_ucsbe(dest + len, '\0'); + return ucsdup(dest); +} + +size_t ucslen(const uint16_t *str) +{ + size_t i; + + for (i = 0; str[i]; i++) + ; + return i; +} + +uint16_t *ucsrchr(const uint16_t *str, char c) +{ + size_t len = ucslen(str); + + while (len-- > 0) { + if (cmp_ucsbe(str + len, c) == 0) { + return (uint16_t*)(str + len); + } + } + return NULL; +} + +uint16_t *ucsdup(const uint16_t *str) +{ + uint16_t *ret; + size_t len = ucslen(str); + + ret = malloc(2 * (len + 1)); + if (ret != NULL) { + memcpy(ret, str, 2 * (len + 1)); + } + return ret; +} + +/** + * Although each character is 2 bytes, we actually compare byte-by-byte + * (thats what the spec says). + */ +int ucscmp(const uint16_t *s1, const uint16_t *s2) +{ + const char *s = (const char*)s1; + const char *t = (const char*)s2; + size_t len1 = ucslen(s1); + size_t len2 = ucslen(s2); + size_t i, len = MIN(len1, len2) * 2; + + for (i = 0; i < len; i++) { + if (s[i] < t[i]) { + return -1; + } else if (s[i] > t[i]) { + return 1; + } + } + + if (len1 < len2) + return -1; + else if (len1 > len2) + return 1; + return 0; +} + +uint16_t *ucscpy(uint16_t *dest, const uint16_t *src) +{ + size_t n = ucslen(src) + 1; + memcpy(dest, src, n*2); + return dest; +} + +uint16_t *ucsncpy(uint16_t *dest, const uint16_t *src, size_t n) +{ + n = MIN(n, ucslen(src) + 1); + memcpy(dest, src, n*2); + return dest; +} + +int str2d_char(const char *icharset, const char *input, char **output) +{ + int ret; + char *ascii; + size_t len, i; + + if (output == NULL) { + return ISO_OUT_OF_MEM; + } + + /** allow NULL input */ + if (input == NULL) { + *output = NULL; + return 0; + } + + /* this checks for NULL parameters */ + ret = str2ascii(icharset, input, &ascii); + if (ret < 0) { + *output = NULL; + return ret; + } + + len = strlen(ascii); + + for (i = 0; i < len; ++i) { + char c= toupper(ascii[i]); + ascii[i] = valid_d_char(c) ? c : '_'; + } + + *output = ascii; + return ISO_SUCCESS; +} + +int str2a_char(const char *icharset, const char *input, char **output) +{ + int ret; + char *ascii; + size_t len, i; + + if (output == NULL) { + return ISO_OUT_OF_MEM; + } + + /** allow NULL input */ + if (input == NULL) { + *output = NULL; + return 0; + } + + /* this checks for NULL parameters */ + ret = str2ascii(icharset, input, &ascii); + if (ret < 0) { + *output = NULL; + return ret; + } + + len = strlen(ascii); + + for (i = 0; i < len; ++i) { + char c= toupper(ascii[i]); + ascii[i] = valid_a_char(c) ? c : '_'; + } + + *output = ascii; + return ISO_SUCCESS; +} + +void iso_lsb(uint8_t *buf, uint32_t num, int bytes) +{ + int i; + + for (i = 0; i < bytes; ++i) + buf[i] = (num >> (8 * i)) & 0xff; +} + +void iso_msb(uint8_t *buf, uint32_t num, int bytes) +{ + int i; + + for (i = 0; i < bytes; ++i) + buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff; +} + +void iso_bb(uint8_t *buf, uint32_t num, int bytes) +{ + iso_lsb(buf, num, bytes); + iso_msb(buf+bytes, num, bytes); +} + +uint32_t iso_read_lsb(const uint8_t *buf, int bytes) +{ + int i; + uint32_t ret = 0; + + for (i=0; i 52 || tzoffset < -48 || always_gmt) { + /* absurd timezone offset, represent time in GMT */ + gmtime_r(&t, &tm); + tzoffset = 0; + } + buf[0] = tm.tm_year; + buf[1] = tm.tm_mon + 1; + buf[2] = tm.tm_mday; + buf[3] = tm.tm_hour; + buf[4] = tm.tm_min; + buf[5] = tm.tm_sec; + buf[6] = tzoffset; +} + +void iso_datetime_17(unsigned char *buf, time_t t, int always_gmt) +{ + static int tzsetup = 0; + static int tzoffset; + struct tm tm; + + if (t == (time_t) - 1) { + /* unspecified time */ + memset(buf, '0', 16); + buf[16] = 0; + return; + } + + if (!tzsetup) { + tzset(); + tzsetup = 1; + } + + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; /* some Linuxes change tm_isdst only if it is -1 */ + localtime_r(&t, &tm); + + localtime_r(&t, &tm); + +#ifdef HAVE_TM_GMTOFF + tzoffset = tm.tm_gmtoff / 60 / 15; +#else + if (tm.tm_isdst < 0) + tm.tm_isdst = 0; + tzoffset = ( - timezone / 60 / 15 ) + 4 * tm.tm_isdst; +#endif + + if (tzoffset > 52 || tzoffset < -48 || always_gmt) { + /* absurd timezone offset, represent time in GMT */ + gmtime_r(&t, &tm); + tzoffset = 0; + } + + sprintf((char*)&buf[0], "%04d", tm.tm_year + 1900); + sprintf((char*)&buf[4], "%02d", tm.tm_mon + 1); + sprintf((char*)&buf[6], "%02d", tm.tm_mday); + sprintf((char*)&buf[8], "%02d", tm.tm_hour); + sprintf((char*)&buf[10], "%02d", tm.tm_min); + sprintf((char*)&buf[12], "%02d", MIN(59, tm.tm_sec)); + memcpy(&buf[14], "00", 2); + buf[16] = tzoffset; + +} + +#ifndef HAVE_TIMEGM +static +time_t timegm(struct tm *tm) +{ + time_t ret; + char *tz; + + tz = getenv("TZ"); + setenv("TZ", "", 1); + tzset(); + ret = mktime(tm); + if (tz) + setenv("TZ", tz, 1); + else + unsetenv("TZ"); + tzset(); + return ret; +} +#endif + +time_t iso_datetime_read_7(const uint8_t *buf) +{ + struct tm tm; + + tm.tm_year = buf[0]; + tm.tm_mon = buf[1] - 1; + tm.tm_mday = buf[2]; + tm.tm_hour = buf[3]; + tm.tm_min = buf[4]; + tm.tm_sec = buf[5]; + return timegm(&tm) - ((int8_t)buf[6]) * 60 * 15; +} + +time_t iso_datetime_read_17(const uint8_t *buf) +{ + struct tm tm; + + sscanf((char*)&buf[0], "%4d", &tm.tm_year); + sscanf((char*)&buf[4], "%2d", &tm.tm_mon); + sscanf((char*)&buf[6], "%2d", &tm.tm_mday); + sscanf((char*)&buf[8], "%2d", &tm.tm_hour); + sscanf((char*)&buf[10], "%2d", &tm.tm_min); + sscanf((char*)&buf[12], "%2d", &tm.tm_sec); + tm.tm_year -= 1900; + tm.tm_mon -= 1; + + return timegm(&tm) - ((int8_t)buf[6]) * 60 * 15; +} + +/** + * Check whether the caller process has read access to the given local file. + * + * @return + * 1 on success (i.e, the process has read access), < 0 on error + * (including ISO_FILE_ACCESS_DENIED on access denied to the specified file + * or any directory on the path). + */ +int iso_eaccess(const char *path) +{ + int access; + + /* use non standard eaccess when available, open() otherwise */ +#ifdef HAVE_EACCESS + access = !eaccess(path, R_OK); +#else + int fd = open(path, O_RDONLY); + if (fd != -1) { + close(fd); + access = 1; + } else { + access = 0; + } +#endif + + if (!access) { + int err; + + /* error, choose an appropriate return code */ + switch (errno) { + case EACCES: + err = ISO_FILE_ACCESS_DENIED; + break; + case ENOTDIR: + case ENAMETOOLONG: + case ELOOP: + err = ISO_FILE_BAD_PATH; + break; + case ENOENT: + err = ISO_FILE_DOESNT_EXIST; + break; + case EFAULT: + case ENOMEM: + err = ISO_OUT_OF_MEM; + break; + default: + err = ISO_FILE_ERROR; + break; + } + return err; + } + return ISO_SUCCESS; +} + +char *strcopy(const char *buf, size_t len) +{ + char *str; + + str = malloc((len + 1) * sizeof(char)); + if (str == NULL) { + return NULL; + } + strncpy(str, buf, len); + str[len] = '\0'; + + /* remove trailing spaces */ + for (len = len-1; str[len] == ' ' && len > 0; --len) + str[len] = '\0'; + + return str; +} + +/** + * Copy up to \p max characters from \p src to \p dest. If \p src has less than + * \p max characters, we pad dest with " " characters. + */ +void strncpy_pad(char *dest, const char *src, size_t max) +{ + size_t len, i; + + if (src != NULL) { + len = MIN(strlen(src), max); + } else { + len = 0; + } + + for (i = 0; i < len; ++i) + dest[i] = src[i]; + for (i = len; i < max; ++i) + dest[i] = ' '; +} + +char *ucs2str(const char *buf, size_t len) +{ + size_t outbytes, inbytes; + char *str, *src, *out; + iconv_t conv; + size_t n; + + inbytes = len; + + outbytes = (inbytes+1) * MB_LEN_MAX; + + /* ensure enought space */ + out = alloca(outbytes); + + /* convert to local charset */ + setlocale(LC_CTYPE, ""); + conv = iconv_open(nl_langinfo(CODESET), "UCS-2BE"); + if (conv == (iconv_t)(-1)) { + return NULL; + } + src = (char *)buf; + str = (char *)out; + + n = iconv(conv, &src, &inbytes, &str, &outbytes); + if (n == -1) { + /* error */ + iconv_close(conv); + return NULL; + } + iconv_close(conv); + *str = '\0'; + + /* remove trailing spaces */ + for (len = strlen(out) - 1; out[len] == ' ' && len > 0; --len) + out[len] = '\0'; + return strdup(out); +} + +void iso_lib_version(int *major, int *minor, int *micro) +{ + *major = LIBISOFS_MAJOR_VERSION; + *minor = LIBISOFS_MINOR_VERSION; + *micro = LIBISOFS_MICRO_VERSION; +} + +int iso_lib_is_compatible(int major, int minor, int micro) +{ + int cmajor, cminor, cmicro; + + /* for now, the rule is that library is compitable if requested + * version is lower */ + iso_lib_version(&cmajor, &cminor, &cmicro); + + return cmajor > major + || (cmajor == major + && (cminor > minor + || (cminor == minor && cmicro >= micro))); +} diff --git a/libisofs/branches/thomas/libisofs/util.h b/libisofs/branches/thomas/libisofs/util.h new file mode 100644 index 00000000..5a9616c5 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/util.h @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#ifndef LIBISO_UTIL_H_ +#define LIBISO_UTIL_H_ + +#include +#include + +#ifndef MAX +# define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef MIN +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#define DIV_UP(n,div) ((n + div - 1) / div) +#define ROUND_UP(n,mul) (DIV_UP(n, mul) * mul) + +int int_pow(int base, int power); + +/** + * Convert the charset encoding of a given string. + * + * @param input + * Input string + * @param icharset + * Input charset. Must be supported by iconv + * @param ocharset + * Output charset. Must be supported by iconv + * @param output + * Location where the pointer to the ouput string will be stored + * @return + * 1 on success, < 0 on error + */ +int strconv(const char *input, const char *icharset, const char *ocharset, + char **output); + +int strnconv(const char *str, const char *icharset, const char *ocharset, + size_t len, char **output); + +/** + * Convert a given string from any input charset to ASCII + * + * @param icharset + * Input charset. Must be supported by iconv + * @param input + * Input string + * @param output + * Location where the pointer to the ouput string will be stored + * @return + * 1 on success, < 0 on error + */ +int str2ascii(const char *icharset, const char *input, char **output); + +/** + * Convert a given string from any input charset to UCS-2BE charset, + * used for Joliet file identifiers. + * + * @param icharset + * Input charset. Must be supported by iconv + * @param input + * Input string + * @param output + * Location where the pointer to the ouput string will be stored + * @return + * 1 on success, < 0 on error + */ +int str2ucs(const char *icharset, const char *input, uint16_t **output); + +/** + * Create a level 1 directory identifier. + * + * @param src + * The identifier, in ASCII encoding. + */ +char *iso_1_dirid(const char *src); + +/** + * Create a level 2 directory identifier. + * + * @param src + * The identifier, in ASCII encoding. + */ +char *iso_2_dirid(const char *src); + +/** + * Create a dir name suitable for an ISO image with relaxed constraints. + * + * @param src + * The identifier, in ASCII encoding. + * @param size + * Max len for the name + * @param relaxed + * 0 only allow d-characters, 1 allow also lowe case chars, + * 2 allow all characters + */ +char *iso_r_dirid(const char *src, int size, int relaxed); + +/** + * Create a level 1 file identifier that consists of a name, in 8.3 + * format. + * Note that version number is not added to the file name + * + * @param src + * The identifier, in ASCII encoding. + */ +char *iso_1_fileid(const char *src); + +/** + * Create a level 2 file identifier. + * Note that version number is not added to the file name + * + * @param src + * The identifier, in ASCII encoding. + */ +char *iso_2_fileid(const char *src); + +/** + * Create a file name suitable for an ISO image with relaxed constraints. + * + * @param src + * The identifier, in ASCII encoding. + * @param len + * Max len for the name, without taken the "." into account. + * @param relaxed + * 0 only allow d-characters, 1 allow also lowe case chars, + * 2 allow all characters + * @param forcedot + * Whether to ensure that "." is added + */ +char *iso_r_fileid(const char *src, size_t len, int relaxed, int forcedot); + +/** + * Create a Joliet file identifier that consists of name and extension. The + * combined name and extension length will not exceed 128 bytes, and the + * name and extension will be separated (.). All characters consist of + * 2 bytes and the resulting string is NULL-terminated by a 2-byte NULL. + * + * Note that version number and (;1) is not appended. + * + * @return + * NULL if the original name and extension both are of length 0. + */ +uint16_t *iso_j_file_id(const uint16_t *src); + +/** + * Create a Joliet directory identifier that consists of name and optionally + * extension. The combined name and extension length will not exceed 128 bytes, + * and the name and extension will be separated (.). All characters consist of + * 2 bytes and the resulting string is NULL-terminated by a 2-byte NULL. + * + * @return + * NULL if the original name and extension both are of length 0. + */ +uint16_t *iso_j_dir_id(const uint16_t *src); + +/** + * Like strlen, but for Joliet strings. + */ +size_t ucslen(const uint16_t *str); + +/** + * Like strrchr, but for Joliet strings. + */ +uint16_t *ucsrchr(const uint16_t *str, char c); + +/** + * Like strdup, but for Joliet strings. + */ +uint16_t *ucsdup(const uint16_t *str); + +/** + * Like strcmp, but for Joliet strings. + */ +int ucscmp(const uint16_t *s1, const uint16_t *s2); + +/** + * Like strcpy, but for Joliet strings. + */ +uint16_t *ucscpy(uint16_t *dest, const uint16_t *src); + +/** + * Like strncpy, but for Joliet strings. + * @param n + * Maximum number of characters to copy (2 bytes per char). + */ +uint16_t *ucsncpy(uint16_t *dest, const uint16_t *src, size_t n); + +/** + * Convert a given input string to d-chars. + * @return + * 1 on succes, < 0 error, 0 if input was null (output is set to null) + */ +int str2d_char(const char *icharset, const char *input, char **output); +int str2a_char(const char *icharset, const char *input, char **output); + +void iso_lsb(uint8_t *buf, uint32_t num, int bytes); +void iso_msb(uint8_t *buf, uint32_t num, int bytes); +void iso_bb(uint8_t *buf, uint32_t num, int bytes); + +uint32_t iso_read_lsb(const uint8_t *buf, int bytes); +uint32_t iso_read_msb(const uint8_t *buf, int bytes); + +/** + * if error != NULL it will be set to 1 if LSB and MSB integers don't match. + */ +uint32_t iso_read_bb(const uint8_t *buf, int bytes, int *error); + +/** + * Records the date/time into a 7 byte buffer (ECMA-119, 9.1.5) + * + * @param buf + * Buffer where the date will be written + * @param t + * The time to be written + * @param always_gmt + * Always write the date in GMT and not in local time. + */ +void iso_datetime_7(uint8_t *buf, time_t t, int always_gmt); + +/** Records the date/time into a 17 byte buffer (ECMA-119, 8.4.26.1) */ +void iso_datetime_17(uint8_t *buf, time_t t, int always_gmt); + +time_t iso_datetime_read_7(const uint8_t *buf); +time_t iso_datetime_read_17(const uint8_t *buf); + +/** + * Check whether the caller process has read access to the given local file. + * + * @return + * 1 on success (i.e, the process has read access), < 0 on error + * (including ISO_FILE_ACCESS_DENIED on access denied to the specified file + * or any directory on the path). + */ +int iso_eaccess(const char *path); + +/** + * Copy up to \p len chars from \p buf and return this newly allocated + * string. The new string is null-terminated. + */ +char *strcopy(const char *buf, size_t len); + +/** + * Copy up to \p max characters from \p src to \p dest. If \p src has less than + * \p max characters, we pad dest with " " characters. + */ +void strncpy_pad(char *dest, const char *src, size_t max); + +/** + * Convert a Joliet string with a length of \p len bytes to a new string + * in local charset. + */ +char *ucs2str(const char *buf, size_t len); + +typedef struct iso_rbtree IsoRBTree; +typedef struct iso_htable IsoHTable; + +typedef unsigned int (*hash_funtion_t)(const void *key); +typedef int (*compare_function_t)(const void *a, const void *b); +typedef void (*hfree_data_t)(void *key, void *data); + +/** + * Create a new binary tree. libisofs binary trees allow you to add any data + * passing it as a pointer. You must provide a function suitable for compare + * two elements. + * + * @param compare + * A function to compare two keys. It takes a pointer to both keys + * and return 0, -1 or 1 if the first key is equal, less or greater + * than the second one. + * @param tree + * Location where the tree structure will be stored. + */ +int iso_rbtree_new(int (*compare)(const void*, const void*), IsoRBTree **tree); + +/** + * Destroy a given tree. + * + * Note that only the structure itself is deleted. To delete the elements, you + * should provide a valid free_data function. It will be called for each + * element of the tree, so you can use it to free any related data. + */ +void iso_rbtree_destroy(IsoRBTree *tree, void (*free_data)(void *)); + +/** + * Inserts a given element in a Red-Black tree. + * + * @param tree + * the tree where to insert + * @param data + * element to be inserted on the tree. It can't be NULL + * @param item + * if not NULL, it will point to a location where the tree element ptr + * will be stored. If data was inserted, *item == data. If data was + * already on the tree, *item points to the previously inserted object + * that is equal to data. + * @return + * 1 success, 0 element already inserted, < 0 error + */ +int iso_rbtree_insert(IsoRBTree *tree, void *data, void **item); + +/** + * Get the number of elements in a given tree. + */ +size_t iso_rbtree_get_size(IsoRBTree *tree); + +/** + * Get an array view of the elements of the tree. + * + * @param include_item + * Function to select which elements to include in the array. It that takes + * a pointer to an element and returns 1 if the element should be included, + * 0 if not. If you want to add all elements to the array, you can pass a + * NULL pointer. + * @param size + * If not null, will be filled with the number of elements in the array, + * without counting the final NULL item. + * @return + * A sorted array with the contents of the tree, or NULL if there is not + * enought memory to allocate the array. You should free(3) the array when + * no more needed. Note that the array is NULL-terminated, and thus it + * has size + 1 length. + */ +void **iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *), + size_t *size); + +/** + * Create a new hash table. + * + * @param size + * Number of slots in table. + * @param hash + * Function used to generate + */ +int iso_htable_create(size_t size, hash_funtion_t hash, + compare_function_t compare, IsoHTable **table); + +/** + * Put an element in a Hash Table. The element will be identified by + * the given key, that you should use to retrieve the element again. + * + * This function allow duplicates, i.e., two items with the same key. In those + * cases, the value returned by iso_htable_get() is undefined. If you don't + * want to allow duplicates, use iso_htable_put() instead; + * + * Both the key and data pointers will be stored internally, so you should + * free the objects they point to. Use iso_htable_remove() to delete an + * element from the table. + */ +int iso_htable_add(IsoHTable *table, void *key, void *data); + +/** + * Like iso_htable_add(), but this doesn't allow dulpicates. + * + * @return + * 1 success, 0 if an item with the same key already exists, < 0 error + */ +int iso_htable_put(IsoHTable *table, void *key, void *data); + +/** + * Retrieve an element from the given table. + * + * @param table + * Hash table + * @param key + * Key of the element that will be removed + * @param data + * Will be filled with the element found. Remains untouched if no + * element with the given key is found. + * @return + * 1 if found, 0 if not, < 0 on error + */ +int iso_htable_get(IsoHTable *table, void *key, void **data); + +/** + * Remove an item with the given key from the table. In tables that allow + * duplicates, it is undefined the element that will be deleted. + * + * @param table + * Hash table + * @param key + * Key of the element that will be removed + * @param free_data + * Function that will be called passing as parameters both the key and + * the element that will be deleted. The user can use it to free the + * element. You can pass NULL if you don't want to delete the item itself. + * @return + * 1 success, 0 no element exists with the given key, < 0 error + */ +int iso_htable_remove(IsoHTable *table, void *key, hfree_data_t free_data); + +/** + * Like remove, but instead of checking for key equality using the compare + * function, it just compare the key pointers. If the table allows duplicates, + * and you provide different keys (i.e. different pointers) to elements + * with same key (i.e. same content), this function ensure the exact element + * is removed. + * + * It has the problem that you must provide the same key pointer, and not just + * a key whose contents are equal. Moreover, if you use the same key (same + * pointer) to identify several objects, what of those are removed is + * undefined. + * + * @param table + * Hash table + * @param key + * Key of the element that will be removed + * @param free_data + * Function that will be called passing as parameters both the key and + * the element that will be deleted. The user can use it to free the + * element. You can pass NULL if you don't want to delete the item itself. + * @return + * 1 success, 0 no element exists with the given key, < 0 error + */ +int iso_htable_remove_ptr(IsoHTable *table, void *key, hfree_data_t free_data); + +/** + * Destroy the given hash table. + * + * Note that you're responsible to actually destroy the elements by providing + * a valid free_data function. You can pass NULL if you only want to delete + * the hash structure. + */ +void iso_htable_destroy(IsoHTable *table, hfree_data_t free_data); + +/** + * Hash function suitable for keys that are char strings. + */ +unsigned int iso_str_hash(const void *key); + +#endif /*LIBISO_UTIL_H_*/ diff --git a/libisofs/branches/thomas/libisofs/util_htable.c b/libisofs/branches/thomas/libisofs/util_htable.c new file mode 100644 index 00000000..684ce6e9 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/util_htable.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "util.h" +#include "libisofs.h" + +#include +#include + +/* + * Hash table implementation + */ + +struct iso_hnode +{ + void *key; + void *data; + + /** next node for chaining */ + struct iso_hnode *next; +}; + +struct iso_htable +{ + struct iso_hnode **table; + + size_t size; /**< number of items in table */ + size_t cap; /**< number of slots in table */ + + hash_funtion_t hash; + compare_function_t compare; +}; + +static +struct iso_hnode *iso_hnode_new(void *key, void *data) +{ + struct iso_hnode *node = malloc(sizeof(struct iso_hnode)); + if (node == NULL) + return NULL; + + node->data = data; + node->key = key; + node->next = NULL; + return node; +} + +/** + * Put an element in a Hash Table. The element will be identified by + * the given key, that you should use to retrieve the element again. + * + * This function allow duplicates, i.e., two items with the same key. In those + * cases, the value returned by iso_htable_get() is undefined. If you don't + * want to allow duplicates, use iso_htable_put() instead; + * + * Both the key and data pointers will be stored internally, so you should + * free the objects they point to. Use iso_htable_remove() to delete an + * element from the table. + */ +int iso_htable_add(IsoHTable *table, void *key, void *data) +{ + struct iso_hnode *node; + struct iso_hnode *new; + unsigned int hash; + + if (table == NULL || key == NULL) { + return ISO_NULL_POINTER; + } + + new = iso_hnode_new(key, data); + if (new == NULL) { + return ISO_OUT_OF_MEM; + } + + hash = table->hash(key) % table->cap; + node = table->table[hash]; + + table->size++; + new->next = node; + table->table[hash] = new; + return ISO_SUCCESS; +} + +/** + * Like iso_htable_add(), but this doesn't allow dulpicates. + * + * @return + * 1 success, 0 if an item with the same key already exists, < 0 error + */ +int iso_htable_put(IsoHTable *table, void *key, void *data) +{ + struct iso_hnode *node; + struct iso_hnode *new; + unsigned int hash; + + if (table == NULL || key == NULL) { + return ISO_NULL_POINTER; + } + + hash = table->hash(key) % table->cap; + node = table->table[hash]; + + while (node) { + if (!table->compare(key, node->key)) { + return 0; + } + node = node->next; + } + + new = iso_hnode_new(key, data); + if (new == NULL) { + return ISO_OUT_OF_MEM; + } + + table->size++; + new->next = table->table[hash]; + table->table[hash] = new; + return ISO_SUCCESS; +} + +/** + * Retrieve an element from the given table. + * + * @param table + * Hash table + * @param key + * Key of the element that will be removed + * @param data + * Will be filled with the element found. Remains untouched if no + * element with the given key is found. + * @return + * 1 if found, 0 if not, < 0 on error + */ +int iso_htable_get(IsoHTable *table, void *key, void **data) +{ + struct iso_hnode *node; + unsigned int hash; + + if (table == NULL || key == NULL) { + return ISO_NULL_POINTER; + } + + hash = table->hash(key) % table->cap; + node = table->table[hash]; + while (node) { + if (!table->compare(key, node->key)) { + if (data) { + *data = node->data; + } + return 1; + } + node = node->next; + } + return 0; +} + +/** + * Remove an item with the given key from the table. In tables that allow + * duplicates, it is undefined the element that will be deleted. + * + * @param table + * Hash table + * @param key + * Key of the element that will be removed + * @param free_data + * Function that will be called passing as parameters both the key and + * the element that will be deleted. The user can use it to free the + * element. You can pass NULL if you don't want to delete the item itself. + * @return + * 1 success, 0 no element exists with the given key, < 0 error + */ +int iso_htable_remove(IsoHTable *table, void *key, hfree_data_t free_data) +{ + struct iso_hnode *node, *prev; + unsigned int hash; + + if (table == NULL || key == NULL) { + return ISO_NULL_POINTER; + } + + hash = table->hash(key) % table->cap; + node = table->table[hash]; + prev = NULL; + while (node) { + if (!table->compare(key, node->key)) { + if (free_data) + free_data(node->key, node->data); + if (prev) { + prev->next = node->next; + } else { + table->table[hash] = node->next; + } + free(node); + table->size--; + return 1; + } + prev = node; + node = node->next; + } + return 0; +} + +/** + * Like remove, but instead of checking for key equality using the compare + * function, it just compare the key pointers. If the table allows duplicates, + * and you provide different keys (i.e. different pointers) to elements + * with same key (i.e. same content), this function ensure the exact element + * is removed. + * + * It has the problem that you must provide the same key pointer, and not just + * a key whose contents are equal. Moreover, if you use the same key (same + * pointer) to identify several objects, what of those are removed is + * undefined. + * + * @param table + * Hash table + * @param key + * Key of the element that will be removed + * @param free_data + * Function that will be called passing as parameters both the key and + * the element that will be deleted. The user can use it to free the + * element. You can pass NULL if you don't want to delete the item itself. + * @return + * 1 success, 0 no element exists with the given key, < 0 error + */ +int iso_htable_remove_ptr(IsoHTable *table, void *key, hfree_data_t free_data) +{ + struct iso_hnode *node, *prev; + unsigned int hash; + + if (table == NULL || key == NULL) { + return ISO_NULL_POINTER; + } + + hash = table->hash(key) % table->cap; + node = table->table[hash]; + prev = NULL; + while (node) { + if (key == node->key) { + if (free_data) + free_data(node->key, node->data); + if (prev) { + prev->next = node->next; + } else { + table->table[hash] = node->next; + } + free(node); + table->size--; + return 1; + } + prev = node; + node = node->next; + } + return 0; +} + +/** + * Hash function suitable for keys that are char strings. + */ +unsigned int iso_str_hash(const void *key) +{ + int i, len; + const char *p = key; + unsigned int h = 2166136261u; + + len = strlen(p); + for (i = 0; i < len; i++) + h = (h * 16777619 ) ^ p[i]; + + return h; +} + +/** + * Destroy the given hash table. + * + * Note that you're responsible to actually destroy the elements by providing + * a valid free_data function. You can pass NULL if you only want to delete + * the hash structure. + */ +void iso_htable_destroy(IsoHTable *table, hfree_data_t free_data) +{ + size_t i; + struct iso_hnode *node, *tmp; + + if (table == NULL) { + return; + } + + for (i = 0; i < table->cap; ++i) { + node = table->table[i]; + while (node) { + tmp = node->next; + if (free_data) + free_data(node->key, node->data); + free(node); + node = tmp; + } + } + free(table->table); + free(table); +} + +/** + * Create a new hash table. + * + * @param size + * Number of slots in table. + * @param hash + * Function used to generate + */ +int iso_htable_create(size_t size, hash_funtion_t hash, + compare_function_t compare, IsoHTable **table) +{ + IsoHTable *t; + + if (table == NULL) { + return ISO_OUT_OF_MEM; + } + + t = malloc(sizeof(IsoHTable)); + if (t == NULL) { + return ISO_OUT_OF_MEM; + } + t->table = calloc(size, sizeof(void*)); + if (t->table == NULL) { + free(t); + return ISO_OUT_OF_MEM; + } + t->cap = size; + t->size = 0; + t->hash = hash; + t->compare = compare; + + *table = t; + return ISO_SUCCESS; +} diff --git a/libisofs/branches/thomas/libisofs/util_rbtree.c b/libisofs/branches/thomas/libisofs/util_rbtree.c new file mode 100644 index 00000000..7a30fc20 --- /dev/null +++ b/libisofs/branches/thomas/libisofs/util_rbtree.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "util.h" +#include "libisofs.h" + +#include + +/* + * This implementation of Red-Black tree is based on the public domain + * implementation of Julienne Walker. + */ + +struct iso_rbnode +{ + void *data; + struct iso_rbnode *ch[2]; + unsigned int red :1; +}; + +struct iso_rbtree +{ + struct iso_rbnode *root; + size_t size; + int (*compare)(const void *a, const void *b); +}; + +/** + * Create a new binary tree. libisofs binary trees allow you to add any data + * passing it as a pointer. You must provide a function suitable for compare + * two elements. + * + * @param compare + * A function to compare two elements. It takes a pointer to both elements + * and return 0, -1 or 1 if the first element is equal, less or greater + * than the second one. + * @param tree + * Location where the tree structure will be stored. + */ +int iso_rbtree_new(int (*compare)(const void*, const void*), IsoRBTree **tree) +{ + if (compare == NULL || tree == NULL) { + return ISO_NULL_POINTER; + } + *tree = calloc(1, sizeof(IsoRBTree)); + if (*tree == NULL) { + return ISO_OUT_OF_MEM; + } + (*tree)->compare = compare; + return ISO_SUCCESS; +} + +static +void rbtree_destroy_aux(struct iso_rbnode *root, void (*free_data)(void *)) +{ + if (root == NULL) { + return; + } + if (free_data != NULL) { + free_data(root->data); + } + rbtree_destroy_aux(root->ch[0], free_data); + rbtree_destroy_aux(root->ch[1], free_data); + free(root); +} + +/** + * Destroy a given tree. + * + * Note that only the structure itself is deleted. To delete the elements, you + * should provide a valid free_data function. It will be called for each + * element of the tree, so you can use it to free any related data. + */ +void iso_rbtree_destroy(IsoRBTree *tree, void (*free_data)(void *)) +{ + if (tree == NULL) { + return; + } + rbtree_destroy_aux(tree->root, free_data); + free(tree); +} + +static inline +int is_red(struct iso_rbnode *root) +{ + return root != NULL && root->red; +} + +static +struct iso_rbnode *iso_rbtree_single(struct iso_rbnode *root, int dir) +{ + struct iso_rbnode *save = root->ch[!dir]; + + root->ch[!dir] = save->ch[dir]; + save->ch[dir] = root; + + root->red = 1; + save->red = 0; + return save; +} + +static +struct iso_rbnode *iso_rbtree_double(struct iso_rbnode *root, int dir) +{ + root->ch[!dir] = iso_rbtree_single(root->ch[!dir], !dir); + return iso_rbtree_single(root, dir); +} + +static +struct iso_rbnode *iso_rbnode_new(void *data) +{ + struct iso_rbnode *rn = malloc(sizeof(struct iso_rbnode)); + + if (rn != NULL) { + rn->data = data; + rn->red = 1; + rn->ch[0] = NULL; + rn->ch[1] = NULL; + } + + return rn; +} + +/** + * Inserts a given element in a Red-Black tree. + * + * @param tree + * the tree where to insert + * @param data + * element to be inserted on the tree. It can't be NULL + * @param item + * if not NULL, it will point to a location where the tree element ptr + * will be stored. If data was inserted, *item == data. If data was + * already on the tree, *item points to the previously inserted object + * that is equal to data. + * @return + * 1 success, 0 element already inserted, < 0 error + */ +int iso_rbtree_insert(IsoRBTree *tree, void *data, void **item) +{ + struct iso_rbnode *new; + int added = 0; /* has a new node been added? */ + + if (tree == NULL || data == NULL) { + return ISO_NULL_POINTER; + } + + if (tree->root == NULL) { + /* Empty tree case */ + tree->root = iso_rbnode_new(data); + if (tree->root == NULL) { + return ISO_OUT_OF_MEM; + } + new = data; + added = 1; + } else { + struct iso_rbnode head = { 0 }; /* False tree root */ + + struct iso_rbnode *g, *t; /* Grandparent & parent */ + struct iso_rbnode *p, *q; /* Iterator & parent */ + int dir = 0, last = 0; + int comp; + + /* Set up helpers */ + t = &head; + g = p = NULL; + q = t->ch[1] = tree->root; + + /* Search down the tree */ + while (1) { + if (q == NULL) { + /* Insert new node at the bottom */ + p->ch[dir] = q = iso_rbnode_new(data); + if (q == NULL) { + return ISO_OUT_OF_MEM; + } + added = 1; + } else if (is_red(q->ch[0]) && is_red(q->ch[1])) { + /* Color flip */ + q->red = 1; + q->ch[0]->red = 0; + q->ch[1]->red = 0; + } + + /* Fix red violation */ + if (is_red(q) && is_red(p)) { + int dir2 = (t->ch[1] == g); + + if (q == p->ch[last]) { + t->ch[dir2] = iso_rbtree_single(g, !last); + } else { + t->ch[dir2] = iso_rbtree_double(g, !last); + } + } + + comp = tree->compare(q->data, data); + + /* Stop if found */ + if (comp == 0) { + new = q->data; + break; + } + + last = dir; + dir = (comp < 0); + + /* Update helpers */ + if (g != NULL) + t = g; + g = p, p = q; + q = q->ch[dir]; + } + + /* Update root */ + tree->root = head.ch[1]; + } + + /* Make root black */ + tree->root->red = 0; + + if (item != NULL) { + *item = new; + } + if (added) { + /* a new element has been added */ + tree->size++; + return 1; + } else { + return 0; + } +} + +/** + * Get the number of elements in a given tree. + */ +size_t iso_rbtree_get_size(IsoRBTree *tree) +{ + return tree->size; +} + +static +size_t rbtree_to_array_aux(struct iso_rbnode *root, void **array, size_t pos, + int (*include_item)(void *)) +{ + if (root == NULL) { + return pos; + } + pos = rbtree_to_array_aux(root->ch[0], array, pos, include_item); + if (include_item == NULL || include_item(root->data)) { + array[pos++] = root->data; + } + pos = rbtree_to_array_aux(root->ch[1], array, pos, include_item); + return pos; +} + +/** + * Get an array view of the elements of the tree. + * + * @param include_item + * Function to select which elements to include in the array. It that takes + * a pointer to an element and returns 1 if the element should be included, + * 0 if not. If you want to add all elements to the array, you can pass a + * NULL pointer. + * @return + * A sorted array with the contents of the tree, or NULL if there is not + * enought memory to allocate the array. You should free(3) the array when + * no more needed. Note that the array is NULL-terminated, and thus it + * has size + 1 length. + */ +void ** iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *), + size_t *size) +{ + size_t pos; + void **array; + + array = malloc((tree->size + 1) * sizeof(void*)); + if (array == NULL) { + return NULL; + } + + /* fill array */ + pos = rbtree_to_array_aux(tree->root, array, 0, include_item); + array[pos] = NULL; + + array = realloc(array, (pos + 1) * sizeof(void*)); + if (size) { + *size = pos; + } + return array; +} + diff --git a/libisofs/branches/thomas/libisofs/writer.h b/libisofs/branches/thomas/libisofs/writer.h new file mode 100644 index 00000000..8d357f8e --- /dev/null +++ b/libisofs/branches/thomas/libisofs/writer.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ +#ifndef LIBISO_IMAGE_WRITER_H_ +#define LIBISO_IMAGE_WRITER_H_ + +#include "ecma119.h" + +struct Iso_Image_Writer +{ + /** + * + */ + int (*compute_data_blocks)(IsoImageWriter *writer); + + int (*write_vol_desc)(IsoImageWriter *writer); + + int (*write_data)(IsoImageWriter *writer); + + int (*free_data)(IsoImageWriter *writer); + + void *data; + Ecma119Image *target; +}; + +/** + * This is the function all Writers shoudl call to write data to image. + * Currently, it is just a wrapper for write(2) Unix system call. + * + * It is implemented in ecma119.c + * + * @return + * 1 on sucess, < 0 error + */ +int iso_write(Ecma119Image *target, void *buf, size_t count); + +int ecma119_writer_create(Ecma119Image *target); + +#endif /*LIBISO_IMAGE_WRITER_H_*/ diff --git a/libisofs/branches/thomas/test/mocked_fsrc.c b/libisofs/branches/thomas/test/mocked_fsrc.c new file mode 100644 index 00000000..13c6b45d --- /dev/null +++ b/libisofs/branches/thomas/test/mocked_fsrc.c @@ -0,0 +1,378 @@ +/* + * + * + */ + +#include "fsource.h" +#include "mocked_fsrc.h" +#include "libisofs.h" + +#include +#include +#include +#include +#include +#include +#include + +static +struct mock_file *path_to_node(IsoFilesystem *fs, const char *path); + +static +char *get_path_aux(struct mock_file *file) +{ + if (file->parent == NULL) { + return strdup(""); + } else { + char *path = get_path_aux(file->parent); + int pathlen = strlen(path); + path = realloc(path, pathlen + strlen(file->name) + 2); + path[pathlen] = '/'; + path[pathlen + 1] = '\0'; + return strcat(path, file->name); + } +} + +static +char* mfs_get_path(IsoFileSource *src) +{ + struct mock_file *data; + data = src->data; + return get_path_aux(data); +} + +static +char* mfs_get_name(IsoFileSource *src) +{ + struct mock_file *data; + data = src->data; + return strdup(data->name); +} + +static +int mfs_lstat(IsoFileSource *src, struct stat *info) +{ + struct mock_file *data; + + if (src == NULL || info == NULL) { + return ISO_NULL_POINTER; + } + data = src->data; + + *info = data->atts; + return ISO_SUCCESS; +} + +static +int mfs_stat(IsoFileSource *src, struct stat *info) +{ + struct mock_file *node; + if (src == NULL || info == NULL) { + return ISO_NULL_POINTER; + } + node = src->data; + + while ( S_ISLNK(node->atts.st_mode) ) { + /* the destination is stated */ + node = path_to_node(node->fs, (char *)node->content); + if (node == NULL) { + return ISO_FILE_ERROR; + } + } + + *info = node->atts; + return ISO_SUCCESS; +} + +static +int mfs_access(IsoFileSource *src) +{ + // TODO not implemented + return ISO_ERROR; +} + +static +int mfs_open(IsoFileSource *src) +{ + // TODO not implemented + return ISO_ERROR; +} + +static +int mfs_close(IsoFileSource *src) +{ + // TODO not implemented + return ISO_ERROR; +} + +static +int mfs_read(IsoFileSource *src, void *buf, size_t count) +{ + // TODO not implemented + return ISO_ERROR; +} + +static +int mfs_readdir(IsoFileSource *src, IsoFileSource **child) +{ + // TODO not implemented + return ISO_ERROR; +} + +static +int mfs_readlink(IsoFileSource *src, char *buf, size_t bufsiz) +{ + struct mock_file *data; + + if (src == NULL || buf == NULL) { + return ISO_NULL_POINTER; + } + + if (bufsiz <= 0) { + return ISO_WRONG_ARG_VALUE; + } + data = src->data; + + if (!S_ISLNK(data->atts.st_mode)) { + return ISO_FILE_IS_NOT_SYMLINK; + } + strncpy(buf, data->content, bufsiz); + buf[bufsiz-1] = '\0'; + return ISO_SUCCESS; +} + +static +IsoFilesystem* mfs_get_filesystem(IsoFileSource *src) +{ + struct mock_file *data; + data = src->data; + return data->fs; +} + +static +void mfs_free(IsoFileSource *src) +{ + /* nothing to do */ +} + +IsoFileSourceIface mfs_class = { + 0, + mfs_get_path, + mfs_get_name, + mfs_lstat, + mfs_stat, + mfs_access, + mfs_open, + mfs_close, + mfs_read, + mfs_readdir, + mfs_readlink, + mfs_get_filesystem, + mfs_free +}; + +/** + * + * @return + * 1 success, < 0 error + */ +static +int mocked_file_source_new(struct mock_file *data, IsoFileSource **src) +{ + IsoFileSource *mocked_src; + + if (src == NULL || data == NULL) { + return ISO_NULL_POINTER; + } + + /* allocate memory */ + mocked_src = malloc(sizeof(IsoFileSource)); + if (mocked_src == NULL) { + free(data); + return ISO_OUT_OF_MEM; + } + + /* fill struct */ + mocked_src->refcount = 1; + mocked_src->data = data; + mocked_src->class = &mfs_class; + + /* take a ref to filesystem */ + //iso_filesystem_ref(fs); + + /* return */ + *src = mocked_src; + return ISO_SUCCESS; +} + +static +struct mock_file *path_to_node(IsoFilesystem *fs, const char *path) +{ + struct mock_file *node; + struct mock_file *dir; + char *ptr, *brk_info, *component; + + /* get the first child at the root of the volume + * that is "/" */ + dir = fs->data; + node = dir; + if (!strcmp(path, "/")) + return node; + + ptr = strdup(path); + + /* get the first component of the path */ + component = strtok_r(ptr, "/", &brk_info); + while (component) { + size_t i; + + if ( !S_ISDIR(node->atts.st_mode) ) { + node=NULL; + break; + } + dir = node; + + node=NULL; + if (!dir->content) { + break; + } + + i = 0; + while (((struct mock_file**)dir->content)[i]) { + if (!strcmp(component, ((struct mock_file**)dir->content)[i]->name)) { + node = ((struct mock_file**)dir->content)[i]; + break; + } + ++i; + } + + /* see if a node could be found */ + if (node==NULL) { + break; + } + component = strtok_r(NULL, "/", &brk_info); + } + free(ptr); + return node; +} + +static +void add_node(struct mock_file *parent, struct mock_file *node) +{ + int i; + + i = 0; + if (parent->content) { + while (((struct mock_file**)parent->content)[i]) { + ++i; + } + } + parent->content = realloc(parent->content, (i+2) * sizeof(void*)); + ((struct mock_file**)parent->content)[i] = node; + ((struct mock_file**)parent->content)[i+1] = NULL; +} + +struct mock_file *test_mocked_fs_get_root(IsoFilesystem *fs) +{ + return fs->data; +} + +int test_mocked_fs_add_dir(const char *name, struct mock_file *p, + struct stat atts, struct mock_file **node) +{ + struct mock_file *dir = calloc(1, sizeof(struct mock_file)); + dir->fs = p->fs; + dir->atts = atts; + dir->name = strdup(name); + dir->parent = p; + add_node(p, dir); + + *node = dir; + return ISO_SUCCESS; +} + +int test_mocked_fs_add_symlink(const char *name, struct mock_file *p, + struct stat atts, const char *dest, struct mock_file **node) +{ + struct mock_file *link = calloc(1, sizeof(struct mock_file)); + link->fs = p->fs; + link->atts = atts; + link->name = strdup(name); + link->parent = p; + add_node(p, link); + link->content = strdup(dest); + *node = link; + return ISO_SUCCESS; +} + +static +int mocked_get_root(IsoFilesystem *fs, IsoFileSource **root) +{ + if (fs == NULL || root == NULL) { + return ISO_NULL_POINTER; + } + return mocked_file_source_new(fs->data, root); +} + +static +int mocked_get_by_path(IsoFilesystem *fs, const char *path, IsoFileSource **file) +{ + struct mock_file *f; + if (fs == NULL || path == NULL || file == NULL) { + return ISO_NULL_POINTER; + } + f = path_to_node(fs, path); + return mocked_file_source_new(f, file); +} + +static +void free_mocked_file(struct mock_file *file) +{ + if (S_ISDIR(file->atts.st_mode)) { + if (file->content) { + int i = 0; + while (((struct mock_file**)file->content)[i]) { + free_mocked_file(((struct mock_file**)file->content)[i]); + ++i; + } + } + } + free(file->content); + free(file->name); + free(file); +} + +static +void mocked_fs_free(IsoFilesystem *fs) +{ + free_mocked_file((struct mock_file *)fs->data); +} + +int test_mocked_filesystem_new(IsoFilesystem **fs) +{ + struct mock_file *root; + IsoFilesystem *filesystem; + + if (fs == NULL) { + return ISO_NULL_POINTER; + } + + root = calloc(1, sizeof(struct mock_file)); + root->atts.st_atime = time(NULL); + root->atts.st_ctime = time(NULL); + root->atts.st_mtime = time(NULL); + root->atts.st_uid = 0; + root->atts.st_gid = 0; + root->atts.st_mode = S_IFDIR | 0777; + + filesystem = malloc(sizeof(IsoFilesystem)); + filesystem->refcount = 1; + root->fs = filesystem; + filesystem->data = root; + filesystem->get_root = mocked_get_root; + filesystem->get_by_path = mocked_get_by_path; + filesystem->free = mocked_fs_free; + *fs = filesystem; + return ISO_SUCCESS; +} + diff --git a/libisofs/branches/thomas/test/mocked_fsrc.h b/libisofs/branches/thomas/test/mocked_fsrc.h new file mode 100644 index 00000000..8e92c61c --- /dev/null +++ b/libisofs/branches/thomas/test/mocked_fsrc.h @@ -0,0 +1,31 @@ +/* + * Mocked objects to simulate an input filesystem. + */ + +#ifndef MOCKED_FSRC_H_ +#define MOCKED_FSRC_H_ + +struct mock_file { + IsoFilesystem *fs; + struct mock_file *parent; + struct stat atts; + char *name; + + /* for links, link dest. For dirs, children */ + void *content; +}; + +/** + * A mocked fs. + */ +int test_mocked_filesystem_new(IsoFilesystem **fs); + +struct mock_file *test_mocked_fs_get_root(IsoFilesystem *fs); + +int test_mocked_fs_add_dir(const char *name, struct mock_file *parent, + struct stat atts, struct mock_file **dir); + +int test_mocked_fs_add_symlink(const char *name, struct mock_file *p, + struct stat atts, const char *dest, struct mock_file **node); + +#endif /*MOCKED_FSRC_H_*/ diff --git a/libisofs/branches/thomas/test/test.c b/libisofs/branches/thomas/test/test.c new file mode 100644 index 00000000..dd9d0a4f --- /dev/null +++ b/libisofs/branches/thomas/test/test.c @@ -0,0 +1,26 @@ +#include "test.h" + +static void create_test_suite() +{ + add_node_suite(); + add_image_suite(); + add_tree_suite(); + add_util_suite(); + add_rockridge_suite(); + add_stream_suite(); +} + +int main(int argc, char **argv) +{ + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + create_test_suite(); + + /* Run all tests using the console interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_cleanup_registry(); + return CU_get_error(); +} diff --git a/libisofs/branches/thomas/test/test.h b/libisofs/branches/thomas/test/test.h new file mode 100644 index 00000000..6e5e76a5 --- /dev/null +++ b/libisofs/branches/thomas/test/test.h @@ -0,0 +1,14 @@ +#ifndef TEST_H_ +#define TEST_H_ + +#include +#include "libisofs.h" + +void add_node_suite(); +void add_image_suite(); +void add_tree_suite(); +void add_util_suite(); +void add_rockridge_suite(); +void add_stream_suite(); + +#endif /*TEST_H_*/ diff --git a/libisofs/branches/thomas/test/test_image.c b/libisofs/branches/thomas/test/test_image.c new file mode 100644 index 00000000..fd0fef6c --- /dev/null +++ b/libisofs/branches/thomas/test/test_image.c @@ -0,0 +1,354 @@ +/* + * Unit test for image.h + */ + + +#include "libisofs.h" +#include "test.h" +#include "image.h" + +#include +#include +#include +#include +#include +#include + + +static void test_iso_image_new() +{ + int ret; + IsoImage *image; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NOT_NULL(image); + CU_ASSERT_EQUAL(image->refcount, 1); + CU_ASSERT_PTR_NOT_NULL(image->root); + + CU_ASSERT_STRING_EQUAL(image->volume_id, "volume_id"); + CU_ASSERT_STRING_EQUAL(image->volset_id, "volume_id"); + + CU_ASSERT_PTR_NULL(image->publisher_id); + CU_ASSERT_PTR_NULL(image->data_preparer_id); + CU_ASSERT_PTR_NULL(image->system_id); + CU_ASSERT_PTR_NULL(image->application_id); + CU_ASSERT_PTR_NULL(image->copyright_file_id); + CU_ASSERT_PTR_NULL(image->abstract_file_id); + CU_ASSERT_PTR_NULL(image->biblio_file_id); + + //CU_ASSERT_PTR_NULL(image->bootcat); + + iso_image_unref(image); +} + +static void test_iso_image_set_volume_id() +{ + int ret; + IsoImage *image; + char *volid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_STRING_EQUAL(image->volume_id, "volume_id"); + + volid = "new volume id"; + iso_image_set_volume_id(image, volid); + CU_ASSERT_STRING_EQUAL(image->volume_id, "new volume id"); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL(image->volume_id, volid); + iso_image_unref(image); +} + +static void test_iso_image_get_volume_id() +{ + int ret; + IsoImage *image; + char *volid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_STRING_EQUAL(iso_image_get_volume_id(image), "volume_id"); + + volid = "new volume id"; + iso_image_set_volume_id(image, volid); + CU_ASSERT_STRING_EQUAL( iso_image_get_volume_id(image), "new volume id" ); + + iso_image_unref(image); +} + +static void test_iso_image_set_publisher_id() +{ + int ret; + IsoImage *image; + char *pubid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(image->publisher_id); + + pubid = "new publisher id"; + iso_image_set_publisher_id(image, pubid); + CU_ASSERT_STRING_EQUAL( image->publisher_id, "new publisher id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( image->publisher_id, pubid ); + iso_image_unref(image); +} + +static void test_iso_image_get_publisher_id() +{ + int ret; + IsoImage *image; + char *pubid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(image->publisher_id); + + pubid = "new publisher id"; + iso_image_set_publisher_id(image, pubid); + CU_ASSERT_STRING_EQUAL(iso_image_get_publisher_id(image), "new publisher id"); + + iso_image_unref(image); +} + +static void test_iso_image_set_data_preparer_id() +{ + int ret; + IsoImage *image; + char *dpid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(image->data_preparer_id); + + dpid = "new data preparer id"; + iso_image_set_data_preparer_id(image, dpid); + CU_ASSERT_STRING_EQUAL(image->data_preparer_id, "new data preparer id"); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL(image->data_preparer_id, dpid); + iso_image_unref(image); +} + +static void test_iso_image_get_data_preparer_id() +{ + int ret; + IsoImage *image; + char *dpid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(image->data_preparer_id); + + dpid = "new data preparer id"; + iso_image_set_data_preparer_id(image, dpid); + CU_ASSERT_STRING_EQUAL( iso_image_get_data_preparer_id(image), "new data preparer id" ); + + iso_image_unref(image); +} + +static void test_iso_image_set_system_id() +{ + int ret; + IsoImage *image; + char *sysid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(image->system_id); + + sysid = "new system id"; + iso_image_set_system_id(image, sysid); + CU_ASSERT_STRING_EQUAL( image->system_id, "new system id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( image->system_id, sysid ); + iso_image_unref(image); +} + +static void test_iso_image_get_system_id() +{ + int ret; + IsoImage *image; + char *sysid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(iso_image_get_system_id(image)); + + sysid = "new system id"; + iso_image_set_system_id(image, sysid); + CU_ASSERT_STRING_EQUAL( iso_image_get_system_id(image), "new system id" ); + + iso_image_unref(image); +} + +static void test_iso_image_set_application_id() +{ + int ret; + IsoImage *image; + char *appid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(image->application_id); + + appid = "new application id"; + iso_image_set_application_id(image, appid); + CU_ASSERT_STRING_EQUAL( image->application_id, "new application id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( image->application_id, appid ); + iso_image_unref(image); +} + +static void test_iso_image_get_application_id() +{ + int ret; + IsoImage *image; + char *appid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(iso_image_get_application_id(image)); + + appid = "new application id"; + iso_image_set_application_id(image, appid); + CU_ASSERT_STRING_EQUAL( iso_image_get_application_id(image), "new application id" ); + + iso_image_unref(image); +} + +static void test_iso_image_set_copyright_file_id() +{ + int ret; + IsoImage *image; + char *copid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(image->copyright_file_id); + + copid = "new copyright id"; + iso_image_set_copyright_file_id(image, copid); + CU_ASSERT_STRING_EQUAL( image->copyright_file_id, "new copyright id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( image->copyright_file_id, copid ); + iso_image_unref(image); +} + +static void test_iso_image_get_copyright_file_id() +{ + int ret; + IsoImage *image; + char *copid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(iso_image_get_copyright_file_id(image)); + + copid = "new copyright id"; + iso_image_set_copyright_file_id(image, copid); + CU_ASSERT_STRING_EQUAL( iso_image_get_copyright_file_id(image), "new copyright id" ); + + iso_image_unref(image); +} + +static void test_iso_image_set_abstract_file_id() +{ + int ret; + IsoImage *image; + char *absid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(image->abstract_file_id); + + absid = "new abstract id"; + iso_image_set_abstract_file_id(image, absid); + CU_ASSERT_STRING_EQUAL( image->abstract_file_id, "new abstract id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( image->abstract_file_id, absid ); + iso_image_unref(image); +} + +static void test_iso_image_get_abstract_file_id() +{ + int ret; + IsoImage *image; + char *absid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(iso_image_get_abstract_file_id(image)); + + absid = "new abstract id"; + iso_image_set_abstract_file_id(image, absid); + CU_ASSERT_STRING_EQUAL(iso_image_get_abstract_file_id(image), "new abstract id"); + + iso_image_unref(image); +} + +static void test_iso_image_set_biblio_file_id() +{ + int ret; + IsoImage *image; + char *bibid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(image->biblio_file_id); + + bibid = "new biblio id"; + iso_image_set_biblio_file_id(image, bibid); + CU_ASSERT_STRING_EQUAL( image->biblio_file_id, "new biblio id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( image->biblio_file_id, bibid ); + iso_image_unref(image); +} + +static void test_iso_image_get_biblio_file_id() +{ + int ret; + IsoImage *image; + char *bibid; + + ret = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_PTR_NULL(iso_image_get_biblio_file_id(image)); + + bibid = "new biblio id"; + iso_image_set_biblio_file_id(image, bibid); + CU_ASSERT_STRING_EQUAL(iso_image_get_biblio_file_id(image), "new biblio id"); + + iso_image_unref(image); +} + +void add_image_suite() +{ + CU_pSuite pSuite = CU_add_suite("imageSuite", NULL, NULL); + + CU_add_test(pSuite, "iso_image_new()", test_iso_image_new); + CU_add_test(pSuite, "iso_image_set_volume_id()", test_iso_image_set_volume_id); + CU_add_test(pSuite, "iso_image_get_volume_id()", test_iso_image_get_volume_id); + CU_add_test(pSuite, "iso_image_set_publisher_id()", test_iso_image_set_publisher_id); + CU_add_test(pSuite, "iso_image_get_publisher_id()", test_iso_image_get_publisher_id); + CU_add_test(pSuite, "iso_image_set_data_preparer_id()", test_iso_image_set_data_preparer_id); + CU_add_test(pSuite, "iso_image_get_data_preparer_id()", test_iso_image_get_data_preparer_id); + CU_add_test(pSuite, "iso_image_set_system_id()", test_iso_image_set_system_id); + CU_add_test(pSuite, "iso_image_get_system_id()", test_iso_image_get_system_id); + CU_add_test(pSuite, "iso_image_set_application_id()", test_iso_image_set_application_id); + CU_add_test(pSuite, "iso_image_get_application_id()", test_iso_image_get_application_id); + CU_add_test(pSuite, "iso_image_set_copyright_file_id()", test_iso_image_set_copyright_file_id); + CU_add_test(pSuite, "iso_image_get_copyright_file_id()", test_iso_image_get_copyright_file_id); + CU_add_test(pSuite, "iso_image_set_abstract_file_id()", test_iso_image_set_abstract_file_id); + CU_add_test(pSuite, "iso_image_get_abstract_file_id()", test_iso_image_get_abstract_file_id); + CU_add_test(pSuite, "iso_image_set_biblio_file_id()", test_iso_image_set_biblio_file_id); + CU_add_test(pSuite, "iso_image_get_biblio_file_id()", test_iso_image_get_biblio_file_id); +} diff --git a/libisofs/branches/thomas/test/test_node.c b/libisofs/branches/thomas/test/test_node.c new file mode 100644 index 00000000..6c2084a5 --- /dev/null +++ b/libisofs/branches/thomas/test/test_node.c @@ -0,0 +1,690 @@ +/* + * Unit test for node.h + */ + +#include "libisofs.h" +#include "node.h" +#include "test.h" + +#include + +static +void test_iso_node_new_root() +{ + int ret; + IsoDir *dir; + + ret = iso_node_new_root(&dir); + CU_ASSERT_EQUAL(ret, ISO_SUCCESS); + + CU_ASSERT_EQUAL(dir->node.refcount, 1); + CU_ASSERT_EQUAL(dir->node.type, LIBISO_DIR); + CU_ASSERT_EQUAL(dir->node.mode, S_IFDIR | 0555); + CU_ASSERT_EQUAL(dir->node.uid, 0); + CU_ASSERT_EQUAL(dir->node.gid, 0); + CU_ASSERT_PTR_NULL(dir->node.name); + CU_ASSERT_EQUAL(dir->node.hidden, 0); + CU_ASSERT_PTR_EQUAL(dir->node.parent, dir); + CU_ASSERT_PTR_NULL(dir->node.next); + CU_ASSERT_EQUAL(dir->nchildren, 0); + CU_ASSERT_PTR_NULL(dir->children); + + iso_node_unref((IsoNode*)dir); +} + +static +void test_iso_node_new_dir() +{ + int ret; + IsoDir *dir; + char *name; + + name = strdup("name1"); + ret = iso_node_new_dir(name, &dir); + CU_ASSERT_EQUAL(ret, ISO_SUCCESS); + CU_ASSERT_EQUAL(dir->node.refcount, 1); + CU_ASSERT_EQUAL(dir->node.type, LIBISO_DIR); + CU_ASSERT_EQUAL(dir->node.mode, S_IFDIR); + CU_ASSERT_EQUAL(dir->node.uid, 0); + CU_ASSERT_EQUAL(dir->node.gid, 0); + CU_ASSERT_EQUAL(dir->node.atime, 0); + CU_ASSERT_EQUAL(dir->node.mtime, 0); + CU_ASSERT_EQUAL(dir->node.ctime, 0); + CU_ASSERT_STRING_EQUAL(dir->node.name, "name1"); + CU_ASSERT_EQUAL(dir->node.hidden, 0); + CU_ASSERT_PTR_NULL(dir->node.parent); + CU_ASSERT_PTR_NULL(dir->node.next); + CU_ASSERT_EQUAL(dir->nchildren, 0); + CU_ASSERT_PTR_NULL(dir->children); + + iso_node_unref((IsoNode*)dir); + + /* try with invalid names */ + ret = iso_node_new_dir("H/DHS/s", &dir); + CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); + ret = iso_node_new_dir(".", &dir); + CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); + ret = iso_node_new_dir("..", &dir); + CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); + ret = iso_node_new_dir(NULL, &dir); + CU_ASSERT_EQUAL(ret, ISO_NULL_POINTER); +} + +static +void test_iso_node_new_symlink() +{ + int ret; + IsoSymlink *link; + char *name, *dest; + + name = strdup("name1"); + dest = strdup("/home"); + ret = iso_node_new_symlink(name, dest, &link); + CU_ASSERT_EQUAL(ret, ISO_SUCCESS); + CU_ASSERT_EQUAL(link->node.refcount, 1); + CU_ASSERT_EQUAL(link->node.type, LIBISO_SYMLINK); + CU_ASSERT_EQUAL(link->node.mode, S_IFLNK); + CU_ASSERT_EQUAL(link->node.uid, 0); + CU_ASSERT_EQUAL(link->node.gid, 0); + CU_ASSERT_EQUAL(link->node.atime, 0); + CU_ASSERT_EQUAL(link->node.mtime, 0); + CU_ASSERT_EQUAL(link->node.ctime, 0); + CU_ASSERT_STRING_EQUAL(link->node.name, "name1"); + CU_ASSERT_EQUAL(link->node.hidden, 0); + CU_ASSERT_PTR_NULL(link->node.parent); + CU_ASSERT_PTR_NULL(link->node.next); + CU_ASSERT_STRING_EQUAL(link->dest, "/home"); + + iso_node_unref((IsoNode*)link); + + /* try with invalid names */ + ret = iso_node_new_symlink("H/DHS/s", "/home", &link); + CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); + ret = iso_node_new_symlink(".", "/home", &link); + CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); + ret = iso_node_new_symlink("..", "/home", &link); + CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); +} + +static +void test_iso_node_set_permissions() +{ + IsoNode *node; + node = malloc(sizeof(IsoNode)); + + node->mode = S_IFDIR | 0777; + + /* set permissions propertly */ + iso_node_set_permissions(node, 0555); + CU_ASSERT_EQUAL(node->mode, S_IFDIR | 0555); + iso_node_set_permissions(node, 0640); + CU_ASSERT_EQUAL(node->mode, S_IFDIR | 0640); + + /* try to change file type via this call */ + iso_node_set_permissions(node, S_IFBLK | 0440); + CU_ASSERT_EQUAL(node->mode, S_IFDIR | 0440); + + free(node); +} + +static +void test_iso_node_get_permissions() +{ + IsoNode *node; + mode_t mode; + + node = malloc(sizeof(IsoNode)); + node->mode = S_IFDIR | 0777; + + mode = iso_node_get_permissions(node); + CU_ASSERT_EQUAL(mode, 0777); + + iso_node_set_permissions(node, 0640); + mode = iso_node_get_permissions(node); + CU_ASSERT_EQUAL(mode, 0640); + + iso_node_set_permissions(node, S_IFBLK | 0440); + mode = iso_node_get_permissions(node); + CU_ASSERT_EQUAL(mode, 0440); + + free(node); +} + +static +void test_iso_node_get_mode() +{ + IsoNode *node; + mode_t mode; + + node = malloc(sizeof(IsoNode)); + node->mode = S_IFDIR | 0777; + + mode = iso_node_get_mode(node); + CU_ASSERT_EQUAL(mode, S_IFDIR | 0777); + + iso_node_set_permissions(node, 0640); + mode = iso_node_get_mode(node); + CU_ASSERT_EQUAL(mode, S_IFDIR | 0640); + + iso_node_set_permissions(node, S_IFBLK | 0440); + mode = iso_node_get_mode(node); + CU_ASSERT_EQUAL(mode, S_IFDIR | 0440); + + free(node); +} + +static +void test_iso_node_set_uid() +{ + IsoNode *node; + node = malloc(sizeof(IsoNode)); + + node->uid = 0; + + iso_node_set_uid(node, 23); + CU_ASSERT_EQUAL(node->uid, 23); + iso_node_set_uid(node, 0); + CU_ASSERT_EQUAL(node->uid, 0); + + free(node); +} + +static +void test_iso_node_get_uid() +{ + IsoNode *node; + uid_t uid; + + node = malloc(sizeof(IsoNode)); + node->uid = 0; + + uid = iso_node_get_uid(node); + CU_ASSERT_EQUAL(uid, 0); + + iso_node_set_uid(node, 25); + uid = iso_node_get_uid(node); + CU_ASSERT_EQUAL(uid, 25); + + free(node); +} + +static +void test_iso_node_set_gid() +{ + IsoNode *node; + node = malloc(sizeof(IsoNode)); + + node->gid = 0; + + iso_node_set_gid(node, 23); + CU_ASSERT_EQUAL(node->gid, 23); + iso_node_set_gid(node, 0); + CU_ASSERT_EQUAL(node->gid, 0); + + free(node); +} + +static +void test_iso_node_get_gid() +{ + IsoNode *node; + gid_t gid; + + node = malloc(sizeof(IsoNode)); + node->gid = 0; + + gid = iso_node_get_gid(node); + CU_ASSERT_EQUAL(gid, 0); + + iso_node_set_gid(node, 25); + gid = iso_node_get_gid(node); + CU_ASSERT_EQUAL(gid, 25); + + free(node); +} + +static +void test_iso_dir_add_node() +{ + int result; + IsoDir *dir; + IsoNode *node1, *node2, *node3, *node4, *node5; + + /* init dir with default values, not all field need to be initialized */ + dir = malloc(sizeof(IsoDir)); + dir->children = NULL; + dir->nchildren = 0; + + /* 1st node to be added */ + node1 = calloc(1, sizeof(IsoNode)); + node1->name = "Node1"; + + /* addition of node to an empty dir */ + result = iso_dir_add_node(dir, node1, 0); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_EQUAL(dir->nchildren, 1); + CU_ASSERT_PTR_EQUAL(dir->children, node1); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_EQUAL(node1->parent, dir); + + /* addition of a node, to be inserted before */ + node2 = calloc(1, sizeof(IsoNode)); + node2->name = "A node to be added first"; + + result = iso_dir_add_node(dir, node2, 0); + CU_ASSERT_EQUAL(result, 2); + CU_ASSERT_EQUAL(dir->nchildren, 2); + CU_ASSERT_PTR_EQUAL(dir->children, node2); + CU_ASSERT_PTR_EQUAL(node2->next, node1); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_EQUAL(node2->parent, dir); + + /* addition of a node, to be inserted last */ + node3 = calloc(1, sizeof(IsoNode)); + node3->name = "This node will be inserted last"; + + result = iso_dir_add_node(dir, node3, 0); + CU_ASSERT_EQUAL(result, 3); + CU_ASSERT_EQUAL(dir->nchildren, 3); + CU_ASSERT_PTR_EQUAL(dir->children, node2); + CU_ASSERT_PTR_EQUAL(node2->next, node1); + CU_ASSERT_PTR_EQUAL(node1->next, node3); + CU_ASSERT_PTR_NULL(node3->next); + CU_ASSERT_PTR_EQUAL(node3->parent, dir); + + /* force some failures */ + result = iso_dir_add_node(NULL, node3, 0); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + result = iso_dir_add_node(dir, NULL, 0); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + + result = iso_dir_add_node(dir, (IsoNode*)dir, 0); + CU_ASSERT_EQUAL(result, ISO_WRONG_ARG_VALUE); + + /* a node with same name */ + node4 = calloc(1, sizeof(IsoNode)); + node4->name = "This node will be inserted last"; + result = iso_dir_add_node(dir, node4, 0); + CU_ASSERT_EQUAL(result, ISO_NODE_NAME_NOT_UNIQUE); + CU_ASSERT_EQUAL(dir->nchildren, 3); + CU_ASSERT_PTR_EQUAL(dir->children, node2); + CU_ASSERT_PTR_EQUAL(node2->next, node1); + CU_ASSERT_PTR_EQUAL(node1->next, node3); + CU_ASSERT_PTR_NULL(node3->next); + CU_ASSERT_PTR_NULL(node4->parent); + + /* a node already added to another dir should fail */ + node5 = calloc(1, sizeof(IsoNode)); + node5->name = "other node"; + node5->parent = (IsoDir*)node4; + result = iso_dir_add_node(dir, node5, 0); + CU_ASSERT_EQUAL(result, ISO_NODE_ALREADY_ADDED); + + free(node1); + free(node2); + free(node3); + free(node4); + free(node5); + free(dir); +} + +static +void test_iso_dir_get_node() +{ + int result; + IsoDir *dir; + IsoNode *node1, *node2, *node3; + IsoNode *node; + + /* init dir with default values, not all field need to be initialized */ + dir = malloc(sizeof(IsoDir)); + dir->children = NULL; + dir->nchildren = 0; + + /* try to find a node in an empty dir */ + result = iso_dir_get_node(dir, "a inexistent name", &node); + CU_ASSERT_EQUAL(result, 0); + CU_ASSERT_PTR_NULL(node); + + /* add a node */ + node1 = calloc(1, sizeof(IsoNode)); + node1->name = "Node1"; + result = iso_dir_add_node(dir, node1, 0); + + /* try to find a node not existent */ + result = iso_dir_get_node(dir, "a inexistent name", &node); + CU_ASSERT_EQUAL(result, 0); + CU_ASSERT_PTR_NULL(node); + + /* and an existing one */ + result = iso_dir_get_node(dir, "Node1", &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node1); + + /* add another node */ + node2 = calloc(1, sizeof(IsoNode)); + node2->name = "A node to be added first"; + result = iso_dir_add_node(dir, node2, 0); + + /* try to find a node not existent */ + result = iso_dir_get_node(dir, "a inexistent name", &node); + CU_ASSERT_EQUAL(result, 0); + CU_ASSERT_PTR_NULL(node); + + /* and the two existing */ + result = iso_dir_get_node(dir, "Node1", &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node1); + result = iso_dir_get_node(dir, "A node to be added first", &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node2); + + /* insert another node */ + node3 = calloc(1, sizeof(IsoNode)); + node3->name = "This node will be inserted last"; + result = iso_dir_add_node(dir, node3, 0); + + /* get again */ + result = iso_dir_get_node(dir, "a inexistent name", &node); + CU_ASSERT_EQUAL(result, 0); + CU_ASSERT_PTR_NULL(node); + result = iso_dir_get_node(dir, "This node will be inserted last", &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node3); + + /* force some failures */ + result = iso_dir_get_node(NULL, "asas", &node); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + result = iso_dir_get_node(dir, NULL, &node); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + + /* and try with null node */ + result = iso_dir_get_node(dir, "asas", NULL); + CU_ASSERT_EQUAL(result, 0); + result = iso_dir_get_node(dir, "This node will be inserted last", NULL); + CU_ASSERT_EQUAL(result, 1); + + free(node1); + free(node2); + free(node3); + free(dir); +} + +void test_iso_dir_get_children() +{ + int result; + IsoDirIter *iter; + IsoDir *dir; + IsoNode *node, *node1, *node2, *node3; + + /* init dir with default values, not all field need to be initialized */ + dir = malloc(sizeof(IsoDir)); + dir->children = NULL; + dir->nchildren = 0; + + result = iso_dir_get_children(dir, &iter); + CU_ASSERT_EQUAL(result, 1); + + /* item should have no items */ + result = iso_dir_iter_has_next(iter); + CU_ASSERT_EQUAL(result, 0); + result = iso_dir_iter_next(iter, &node); + CU_ASSERT_EQUAL(result, 0); + CU_ASSERT_PTR_NULL(node); + iso_dir_iter_free(iter); + + /* 1st node to be added */ + node1 = calloc(1, sizeof(IsoNode)); + node1->name = "Node1"; + result = iso_dir_add_node(dir, node1, 0); + CU_ASSERT_EQUAL(dir->nchildren, 1); + + /* test iteration again */ + result = iso_dir_get_children(dir, &iter); + CU_ASSERT_EQUAL(result, 1); + + /* iter should have a single item... */ + result = iso_dir_iter_has_next(iter); + CU_ASSERT_EQUAL(result, 1); + result = iso_dir_iter_next(iter, &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node1); + + /* ...and no more */ + result = iso_dir_iter_has_next(iter); + CU_ASSERT_EQUAL(result, 0); + result = iso_dir_iter_next(iter, &node); + CU_ASSERT_EQUAL(result, 0); + CU_ASSERT_PTR_NULL(node); + iso_dir_iter_free(iter); + + /* add another node */ + node2 = calloc(1, sizeof(IsoNode)); + node2->name = "A node to be added first"; + result = iso_dir_add_node(dir, node2, 0); + CU_ASSERT_EQUAL(result, 2); + + result = iso_dir_get_children(dir, &iter); + CU_ASSERT_EQUAL(result, 1); + + /* iter should have two items... */ + result = iso_dir_iter_has_next(iter); + CU_ASSERT_EQUAL(result, 1); + result = iso_dir_iter_next(iter, &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node2); + result = iso_dir_iter_has_next(iter); + CU_ASSERT_EQUAL(result, 1); + result = iso_dir_iter_next(iter, &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node1); + + /* ...and no more */ + result = iso_dir_iter_has_next(iter); + CU_ASSERT_EQUAL(result, 0); + result = iso_dir_iter_next(iter, &node); + CU_ASSERT_EQUAL(result, 0); + CU_ASSERT_PTR_NULL(node); + iso_dir_iter_free(iter); + + /* addition of a 3rd node, to be inserted last */ + node3 = calloc(1, sizeof(IsoNode)); + node3->name = "This node will be inserted last"; + result = iso_dir_add_node(dir, node3, 0); + CU_ASSERT_EQUAL(result, 3); + + result = iso_dir_get_children(dir, &iter); + CU_ASSERT_EQUAL(result, 1); + + /* iter should have 3 items... */ + result = iso_dir_iter_has_next(iter); + CU_ASSERT_EQUAL(result, 1); + result = iso_dir_iter_next(iter, &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node2); + result = iso_dir_iter_has_next(iter); + CU_ASSERT_EQUAL(result, 1); + result = iso_dir_iter_next(iter, &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node1); + result = iso_dir_iter_has_next(iter); + CU_ASSERT_EQUAL(result, 1); + result = iso_dir_iter_next(iter, &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node3); + + /* ...and no more */ + result = iso_dir_iter_has_next(iter); + CU_ASSERT_EQUAL(result, 0); + result = iso_dir_iter_next(iter, &node); + CU_ASSERT_EQUAL(result, 0); + CU_ASSERT_PTR_NULL(node); + iso_dir_iter_free(iter); + + free(node1); + free(node2); + free(node3); + free(dir); +} + +void test_iso_node_take() +{ + int result; + IsoDir *dir; + IsoNode *node1, *node2, *node3; + + /* init dir with default values, not all field need to be initialized */ + dir = malloc(sizeof(IsoDir)); + dir->children = NULL; + dir->nchildren = 0; + + /* 1st node to be added */ + node1 = calloc(1, sizeof(IsoNode)); + node1->name = "Node1"; + + /* addition of node to an empty dir */ + result = iso_dir_add_node(dir, node1, 0); + CU_ASSERT_EQUAL(result, 1); + + /* and take it */ + result = iso_node_take(node1); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_EQUAL(dir->nchildren, 0); + CU_ASSERT_PTR_NULL(dir->children); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_NULL(node1->parent); + + /* insert it again */ + result = iso_dir_add_node(dir, node1, 0); + CU_ASSERT_EQUAL(result, 1); + + /* addition of a 2nd node, to be inserted before */ + node2 = calloc(1, sizeof(IsoNode)); + node2->name = "A node to be added first"; + result = iso_dir_add_node(dir, node2, 0); + CU_ASSERT_EQUAL(result, 2); + + /* take first child */ + result = iso_node_take(node2); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_EQUAL(dir->nchildren, 1); + CU_ASSERT_PTR_EQUAL(dir->children, node1); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_EQUAL(node1->parent, dir); + CU_ASSERT_PTR_NULL(node2->next); + CU_ASSERT_PTR_NULL(node2->parent); + + /* insert node 2 again */ + result = iso_dir_add_node(dir, node2, 0); + CU_ASSERT_EQUAL(result, 2); + + /* now take last child */ + result = iso_node_take(node1); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_EQUAL(dir->nchildren, 1); + CU_ASSERT_PTR_EQUAL(dir->children, node2); + CU_ASSERT_PTR_NULL(node2->next); + CU_ASSERT_PTR_EQUAL(node2->parent, dir); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_NULL(node1->parent); + + /* insert again node1... */ + result = iso_dir_add_node(dir, node1, 0); + CU_ASSERT_EQUAL(result, 2); + + /* ...and a 3rd child, to be inserted last */ + node3 = calloc(1, sizeof(IsoNode)); + node3->name = "This node will be inserted last"; + result = iso_dir_add_node(dir, node3, 0); + CU_ASSERT_EQUAL(result, 3); + + /* and take the node in the middle */ + result = iso_node_take(node1); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_EQUAL(dir->nchildren, 2); + CU_ASSERT_PTR_EQUAL(dir->children, node2); + CU_ASSERT_PTR_EQUAL(node2->next, node3); + CU_ASSERT_PTR_EQUAL(node2->parent, dir); + CU_ASSERT_PTR_NULL(node3->next); + CU_ASSERT_PTR_EQUAL(node3->parent, dir); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_NULL(node1->parent); + + free(node1); + free(node2); + free(node3); + free(dir); +} + +static +void test_iso_node_set_name() +{ + int result; + IsoDir *dir; + IsoNode *node1, *node2; + + /* init dir with default values, not all field need to be initialized */ + dir = malloc(sizeof(IsoDir)); + dir->children = NULL; + dir->nchildren = 0; + + /* cretae a node */ + node1 = calloc(1, sizeof(IsoNode)); + node1->name = strdup("Node1"); + + /* check name change */ + result = iso_node_set_name(node1, "New name"); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_STRING_EQUAL(node1->name, "New name"); + + /* add node dir */ + result = iso_dir_add_node(dir, node1, 0); + CU_ASSERT_EQUAL(result, 1); + + /* check name change */ + result = iso_node_set_name(node1, "Another name"); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_STRING_EQUAL(node1->name, "Another name"); + + /* addition of a 2nd node */ + node2 = calloc(1, sizeof(IsoNode)); + node2->name = strdup("A node to be added first"); + result = iso_dir_add_node(dir, node2, 0); + CU_ASSERT_EQUAL(result, 2); + + result = iso_node_set_name(node2, "New name"); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_STRING_EQUAL(node2->name, "New name"); + + /* and now try to give an existing name */ + result = iso_node_set_name(node2, "Another name"); + CU_ASSERT_EQUAL(result, ISO_NODE_NAME_NOT_UNIQUE); + CU_ASSERT_STRING_EQUAL(node2->name, "New name"); + + free(node1->name); + free(node2->name); + free(node1); + free(node2); + free(dir); +} + +void add_node_suite() +{ + CU_pSuite pSuite = CU_add_suite("Node Test Suite", NULL, NULL); + + CU_add_test(pSuite, "iso_node_new_root()", test_iso_node_new_root); + CU_add_test(pSuite, "iso_node_new_dir()", test_iso_node_new_dir); + CU_add_test(pSuite, "iso_node_new_symlink()", test_iso_node_new_symlink); + CU_add_test(pSuite, "iso_node_set_permissions()", test_iso_node_set_permissions); + CU_add_test(pSuite, "iso_node_get_permissions()", test_iso_node_get_permissions); + CU_add_test(pSuite, "iso_node_get_mode()", test_iso_node_get_mode); + CU_add_test(pSuite, "iso_node_set_uid()", test_iso_node_set_uid); + CU_add_test(pSuite, "iso_node_get_uid()", test_iso_node_get_uid); + CU_add_test(pSuite, "iso_node_set_gid()", test_iso_node_set_gid); + CU_add_test(pSuite, "iso_node_get_gid()", test_iso_node_get_gid); + CU_add_test(pSuite, "iso_dir_add_node()", test_iso_dir_add_node); + CU_add_test(pSuite, "iso_dir_get_node()", test_iso_dir_get_node); + CU_add_test(pSuite, "iso_dir_get_children()", test_iso_dir_get_children); + CU_add_test(pSuite, "iso_node_take()", test_iso_node_take); + CU_add_test(pSuite, "iso_node_set_name()", test_iso_node_set_name); +} diff --git a/libisofs/branches/thomas/test/test_rockridge.c b/libisofs/branches/thomas/test/test_rockridge.c new file mode 100644 index 00000000..0a3d411f --- /dev/null +++ b/libisofs/branches/thomas/test/test_rockridge.c @@ -0,0 +1,1395 @@ +/* + * Unit test for util.h + * + * This test utiliy functions + */ +#include "test.h" +#include "ecma119_tree.h" +#include "rockridge.h" +#include "node.h" + +#include + +static void test_rrip_calc_len_file() +{ + IsoFile *file; + Ecma119Node *node; + Ecma119Image t; + size_t sua_len = 0, ce_len = 0; + + memset(&t, 0, sizeof(Ecma119Image)); + t.input_charset = "UTF-8"; + t.output_charset = "UTF-8"; + + file = malloc(sizeof(IsoFile)); + CU_ASSERT_PTR_NOT_NULL_FATAL(file); + file->msblock = 0; + file->sort_weight = 0; + file->stream = NULL; /* it is not needed here */ + file->node.type = LIBISO_FILE; + + node = malloc(sizeof(Ecma119Node)); + CU_ASSERT_PTR_NOT_NULL_FATAL(node); + node->node = (IsoNode*)file; + node->parent = (Ecma119Node*)0x55555555; /* just to make it not NULL */ + node->info.file = NULL; /* it is not needed here */ + node->type = ECMA119_FILE; + + /* Case 1. Name fit in System Use field */ + file->node.name = "a small name.txt"; + node->iso_name = "A_SMALL_.TXT"; + + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 0); + CU_ASSERT_EQUAL(sua_len, 44 + (5 + 16) + (5 + 3*7) + 1); + + /* Case 2. Name fits exactly */ + file->node.name = "a big name, with 133 characters, that it is the max " + "that fits in System Use field of the directory record " + "PADPADPADADPADPADPADPAD.txt"; + node->iso_name = "A_BIG_NA.TXT"; + + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 0); + /* note that 254 is the max length of a directory record, as it needs to + * be an even number */ + CU_ASSERT_EQUAL(sua_len, 254 - 46); + + /* case 3. A name just 1 character too big to fit in SUA */ + file->node.name = "a big name, with 133 characters, that it is the max " + "that fits in System Use field of the directory record " + "PADPADPADADPADPADPADPAD1.txt"; + node->iso_name = "A_BIG_NA.TXT"; + + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + /* 28 (the chars moved to include the CE entry) + 5 (header of NM in CE) + + * 1 (the char that originally didn't fit) */ + CU_ASSERT_EQUAL(ce_len, 28 + 5 + 1); + /* note that 254 is the max length of a directory record, as it needs to + * be an even number */ + CU_ASSERT_EQUAL(sua_len, 254 - 46); + + /* case 4. A 255 characters name */ + file->node.name = "a big name, with 255 characters, that it is the max " + "that a POSIX filename can have. PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" + "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" + "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP"; + node->iso_name = "A_BIG_NA.TXT"; + + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + /* 150 + 5 (header + characters that don't fit in sua) */ + CU_ASSERT_EQUAL(ce_len, 150 + 5); + /* note that 254 is the max length of a directory record, as it needs to + * be an even number */ + CU_ASSERT_EQUAL(sua_len, 254 - 46); + + free(node); + free(file); +} + +static void test_rrip_calc_len_symlink() +{ + IsoSymlink *link; + Ecma119Node *node; + Ecma119Image t; + size_t sua_len = 0, ce_len = 0; + + memset(&t, 0, sizeof(Ecma119Image)); + t.input_charset = "UTF-8"; + t.output_charset = "UTF-8"; + + link = malloc(sizeof(IsoSymlink)); + CU_ASSERT_PTR_NOT_NULL_FATAL(link); + link->node.type = LIBISO_SYMLINK; + + node = malloc(sizeof(Ecma119Node)); + CU_ASSERT_PTR_NOT_NULL_FATAL(node); + node->node = (IsoNode*)link; + node->parent = (Ecma119Node*)0x55555555; /* just to make it not NULL */ + node->type = ECMA119_SYMLINK; + + /* Case 1. Name and dest fit in System Use field */ + link->node.name = "a small name.txt"; + link->dest = "/three/components"; + node->iso_name = "A_SMALL_.TXT"; + + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 0); + CU_ASSERT_EQUAL(sua_len, 44 + (5 + 16) + (5 + 3*7) + 1 + + (5 + 2 + (2+5) + (2+10)) ); + + /* case 2. name + dest fits exactly */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "./and/../a/./big/destination/with/10/components"; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 0); + CU_ASSERT_EQUAL(sua_len, 254 - 46); + + /* case 3. name fits, dest is one byte larger to fit */ + /* 3.a extra byte in dest */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "./and/../a/./big/destination/with/10/componentsk"; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 60); + CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); + + /* 3.b extra byte in name */ + link->node.name = "this name will have 75 characters as it is the max " + "that fits in the SUx.txt"; + link->dest = "./and/../a/./big/destination/with/10/components"; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 59); + CU_ASSERT_EQUAL(sua_len, 44 + (5 + 75) + (5 + 3*7) + 28); + + /* case 4. name seems to fit, but SL no, and when CE is added NM + * doesn't fit too */ + /* 4.a it just fits */ + link->node.name = "this name will have 105 characters as it is just the " + "max that fits in the SU once we add the CE entry.txt"; + link->dest = "./and/../a/./big/destination/with/10/components"; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 59); + CU_ASSERT_EQUAL(sua_len, 254 - 46); + + /* 4.b it just fits, the the component ends in '/' */ + link->node.name = "this name will have 105 characters as it is just the " + "max that fits in the SU once we add the CE entry.txt"; + link->dest = "./and/../a/./big/destination/with/10/components/"; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 59); + CU_ASSERT_EQUAL(sua_len, 254 - 46); + + /* 4.c extra char in name, that forces it to be divided */ + link->node.name = "this name will have 105 characters as it is just the " + "max that fits in the SU once we add the CE entryc.txt"; + link->dest = "./and/../a/./big/destination/with/10/components"; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 59 + 6); + CU_ASSERT_EQUAL(sua_len, 254 - 46); + + /* 5 max destination length to fit in a single SL entry (250) */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "./and/../a/./very/big/destination/with/10/components/that/" + "conforms/the/max/that/fits/in/a/single/SL/as/it/takes/" + "just/two/hundred/and/fifty/bytes/bytes/bytes/bytes/bytes" + "/bytes/bytes/bytes/bytes/bytes/bytes/../bytes"; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 255); + CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); + + /* 6 min destination length to need two SL entries (251) */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "./and/../a/./very/big/destination/with/10/components/that/" + "conforms/the/max/that/fits/in/a/single/SL/as/it/takes/" + "just/two/hundred/and/fifty/bytes/bytes/bytes/bytes/bytes" + "/bytes/bytes/bytes/bytes/bytes/bytes/../bytess"; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 261); + CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); + + /* 7 destination with big component that need to be splited + * in two SL entries */ + /* 7.a just fits in one */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "very big component with 248 characters, that is the max that" + " fits in a single SL entry. Take care that SL header takes 5 " + "bytes, and component header another 2, one for size, another" + " for flags. This last characters are just padding to get 248 " + "bytes."; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 255); + CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); + + /* 7.b doesn't fits by one character */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "very big component with 249 characters, that is the min that" + " doesn't fit in a single SL entry. Take care that SL header " + "takes 5 bytes, and component header another 2, one for size," + " another for flags. This last characters are just padding to" + " get 249."; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 255 + (5+2+1)); + CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); + + /* 7.c several components before, such as it has just the right len + * to fit in the SL entry plus another one */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "the/first/components/take just 245 characters/and thus the " + "first SL entry will have/255 - 5 - 245 - 2 (component " + "header) = 3/ just the space for another component with a " + "single character/This makes that last component fit in exactly 2 " + "SLs/very big component with 249 characters, that is the min " + "that doesn't fit in a single SL entry. Take care that SL " + "header takes 5 bytes, and component header another 2, one " + "for size, another for flags. This last characters are just " + "padding to get 249."; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 255 + 255); + CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); + + /* + * 7.d several components before, and then a big component that doesn't + * fit in the 1st SL entry and another one. That case needs a 3rd SL entry, + * but instead of divide the component in 2 entries, we put it in 2, + * without completelly fill the first one. + */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "the/first/components/take just 245 characters/and thus the " + "first SL entry will have/255 - 5 - 245 - 2 (component " + "header) = 3/ just the space for another component with a " + "single character/This makes that last component fit in exactly 2 " + "SLs/very big component with 250 characters, that is the min " + "that does not fit in a single SL entry. Take care that SL " + "header takes 5 bytes, and component header another 2, one " + "for size, another for flags. This last characters are just " + "padding to get 249."; + node->iso_name = "THIS_NAM.TXT"; + sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); + CU_ASSERT_EQUAL(ce_len, 252 + 255 + 9); + CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); + + free(link); + free(node); +} + +static +void susp_info_free(struct susp_info *susp) +{ + size_t i; + + for (i = 0; i < susp->n_susp_fields; ++i) { + free(susp->susp_fields[i]); + } + free(susp->susp_fields); + + for (i = 0; i < susp->n_ce_susp_fields; ++i) { + free(susp->ce_susp_fields[i]); + } + free(susp->ce_susp_fields); +} + +static +void test_rrip_get_susp_fields_file() +{ + IsoFile *file; + Ecma119Node *node; + int ret; + struct susp_info susp; + Ecma119Image t; + uint8_t *entry; + + memset(&t, 0, sizeof(Ecma119Image)); + t.input_charset = "UTF-8"; + t.output_charset = "UTF-8"; + + file = malloc(sizeof(IsoFile)); + CU_ASSERT_PTR_NOT_NULL_FATAL(file); + file->msblock = 0; + file->sort_weight = 0; + file->stream = NULL; /* it is not needed here */ + file->node.type = LIBISO_FILE; + file->node.mode = S_IFREG | 0555; + file->node.uid = 235; + file->node.gid = 654; + file->node.mtime = 675757578; + file->node.atime = 546462546; + file->node.ctime = 323245342; + + node = malloc(sizeof(Ecma119Node)); + CU_ASSERT_PTR_NOT_NULL_FATAL(node); + node->node = (IsoNode*)file; + node->parent = (Ecma119Node*)0x55555555; /* just to make it not NULL */ + node->info.file = NULL; /* it is not needed here */ + node->type = ECMA119_FILE; + node->nlink = 1; + node->ino = 0x03447892; + + /* Case 1. Name fit in System Use field */ + file->node.name = "a small name.txt"; + node->iso_name = "A_SMALL_.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 0); + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 0); + CU_ASSERT_EQUAL(susp.n_susp_fields, 3); /* PX + TF + NM */ + CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 16) + (5 + 3*7) + 1); + + /* PX is the first entry */ + entry = susp.susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'P'); + CU_ASSERT_EQUAL(entry[1], 'X'); + CU_ASSERT_EQUAL(entry[2], 44); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), S_IFREG | 0555); + CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), S_IFREG | 0555); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 1); + CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 1); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 235); + CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 235); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 28, 4), 654); + CU_ASSERT_EQUAL(iso_read_msb(entry + 32, 4), 654); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 36, 4), 0x03447892); + CU_ASSERT_EQUAL(iso_read_msb(entry + 40, 4), 0x03447892); + + /* TF is the second entry */ + entry = susp.susp_fields[1]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'T'); + CU_ASSERT_EQUAL(entry[1], 'F'); + CU_ASSERT_EQUAL(entry[2], 5 + 3*7); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0x0E); + CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 5), 675757578); + CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 12), 546462546); + CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 19), 323245342); + + /* NM is the last entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 16); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_NSTRING_EQUAL(entry + 5, "a small name.txt", 16); + + susp_info_free(&susp); + + /* Case 2. Name fits exactly */ + file->node.name = "a big name, with 133 characters, that it is the max " + "that fits in System Use field of the directory record " + "PADPADPADADPADPADPADPAD.txt"; + node->iso_name = "A_BIG_NA.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 0); + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 0); + CU_ASSERT_EQUAL(susp.suf_len, 254 - 46); + + CU_ASSERT_EQUAL(susp.n_susp_fields, 3); /* PX + TF + NM */ + + /* NM is the last entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 133); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_NSTRING_EQUAL(entry + 5, "a big name, with 133 characters, that " + "it is the max that fits in System Use field of the " + "directory record PADPADPADADPADPADPADPAD.txt", 133); + + susp_info_free(&susp); + + /* case 3. A name just 1 character too big to fit in SUA */ + file->node.name = "a big name, with 133 characters, that it is the max " + "that fits in System Use field of the directory record " + "PADPADPADADPADPADPADPAD1.txt"; + node->iso_name = "A_BIG_NA.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 28 + 5 + 1); + CU_ASSERT_EQUAL(susp.suf_len, 254 - 46); + + CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* NM */ + + /* test NM entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 105); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 1); /* CONTINUE */ + CU_ASSERT_NSTRING_EQUAL(entry + 5, "a big name, with 133 characters, that " + "it is the max that fits in System Use field of the " + "directory record", 105); + + /* and CE entry */ + entry = susp.susp_fields[3]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'C'); + CU_ASSERT_EQUAL(entry[1], 'E'); + CU_ASSERT_EQUAL(entry[2], 28); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 34); + CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 34); + + /* and check Continuation area */ + entry = susp.ce_susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 29); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_NSTRING_EQUAL(entry + 5, " PADPADPADADPADPADPADPAD1.txt", 29); + + susp_info_free(&susp); + + /* case 4. A 255 characters name */ + file->node.name = "a big name, with 255 characters, that it is the max " + "that a POSIX filename can have. PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" + "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" + "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP"; + node->iso_name = "A_BIG_NA.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + susp.ce_block = 12; + susp.ce_len = 456; + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 150 + 5 + 456); + CU_ASSERT_EQUAL(susp.suf_len, 254 - 46); + + CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* NM */ + + /* test NM entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 105); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 1); /* CONTINUE */ + CU_ASSERT_NSTRING_EQUAL(entry + 5, "a big name, with 255 characters, that " + "it is the max that a POSIX filename can have. PPP" + "PPPPPPPPPPPPPPPPPP", 105); + + /* and CE entry */ + entry = susp.susp_fields[3]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'C'); + CU_ASSERT_EQUAL(entry[1], 'E'); + CU_ASSERT_EQUAL(entry[2], 28); + CU_ASSERT_EQUAL(entry[3], 1); + + /* block, offset, size */ + CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 12); + CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 12); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 456); + CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 456); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 155); + CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 155); + + /* and check Continuation area */ + entry = susp.ce_susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 150); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_NSTRING_EQUAL(entry + 5, "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" + "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" + "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" + "PPPPPPPPPPPPPP", 150); + + susp_info_free(&susp); + + free(node); + free(file); +} + +static void test_rrip_get_susp_fields_symlink() +{ + IsoSymlink *link; + Ecma119Node *node; + Ecma119Image t; + int ret; + struct susp_info susp; + uint8_t *entry; + + memset(&t, 0, sizeof(Ecma119Image)); + t.input_charset = "UTF-8"; + t.output_charset = "UTF-8"; + + link = malloc(sizeof(IsoSymlink)); + CU_ASSERT_PTR_NOT_NULL_FATAL(link); + link->node.type = LIBISO_SYMLINK; + link->node.mode = S_IFREG | 0555; + link->node.uid = 235; + link->node.gid = 654; + link->node.mtime = 675757578; + link->node.atime = 546462546; + link->node.ctime = 323245342; + + node = malloc(sizeof(Ecma119Node)); + CU_ASSERT_PTR_NOT_NULL_FATAL(node); + node->node = (IsoNode*)link; + node->parent = (Ecma119Node*)0x55555555; /* just to make it not NULL */ + node->type = ECMA119_SYMLINK; + node->nlink = 1; + node->ino = 0x03447892; + + /* Case 1. Name and dest fit in System Use field */ + link->node.name = "a small name.txt"; + link->dest = "/three/components"; + node->iso_name = "A_SMALL_.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 0); + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 0); + CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + SL */ + CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 16) + (5 + 3*7) + 1 + + (5 + 2 + (2 + 5) + (2 + 10))); + + /* PX is the first entry */ + entry = susp.susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'P'); + CU_ASSERT_EQUAL(entry[1], 'X'); + CU_ASSERT_EQUAL(entry[2], 44); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), S_IFREG | 0555); + CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), S_IFREG | 0555); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 1); + CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 1); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 235); + CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 235); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 28, 4), 654); + CU_ASSERT_EQUAL(iso_read_msb(entry + 32, 4), 654); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 36, 4), 0x03447892); + CU_ASSERT_EQUAL(iso_read_msb(entry + 40, 4), 0x03447892); + + /* TF is the second entry */ + entry = susp.susp_fields[1]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'T'); + CU_ASSERT_EQUAL(entry[1], 'F'); + CU_ASSERT_EQUAL(entry[2], 5 + 3*7); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0x0E); + CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 5), 675757578); + CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 12), 546462546); + CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 19), 323245342); + + /* NM is the 3rd entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 16); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_NSTRING_EQUAL(entry + 5, "a small name.txt", 16); + + /* SL is the last entry */ + entry = susp.susp_fields[3]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'S'); + CU_ASSERT_EQUAL(entry[1], 'L'); + CU_ASSERT_EQUAL(entry[2], 5 + 2 + (2 + 5) + (2 + 10)); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + + /* first component */ + CU_ASSERT_EQUAL(entry[5], 0x8); /* root */ + CU_ASSERT_EQUAL(entry[6], 0); + + /* 2nd component */ + CU_ASSERT_EQUAL(entry[7], 0); + CU_ASSERT_EQUAL(entry[8], 5); + CU_ASSERT_NSTRING_EQUAL(entry + 9, "three", 5); + + /* 3rd component */ + CU_ASSERT_EQUAL(entry[14], 0); + CU_ASSERT_EQUAL(entry[15], 10); + CU_ASSERT_NSTRING_EQUAL(entry + 16, "components", 10); + + susp_info_free(&susp); + + /* case 2. name + dest fits exactly */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "./and/../a/./big/destination/with/10/components"; + node->iso_name = "THIS_NAM.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 0); + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 0); + CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + SL */ + CU_ASSERT_EQUAL(susp.suf_len, 254 - 46); + + /* NM is the 3rd entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 74); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 74 characters as " + "it is the max that fits in the SU.txt", 74); + + /* SL is the last entry */ + entry = susp.susp_fields[3]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'S'); + CU_ASSERT_EQUAL(entry[1], 'L'); + CU_ASSERT_EQUAL(entry[2], 5 + 2 + 5 + 2 + 3 + 2 + 5 + 13 + 6 + 4 + 12); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + + /* first component */ + CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[6], 0); + + /* 2nd component */ + CU_ASSERT_EQUAL(entry[7], 0); + CU_ASSERT_EQUAL(entry[8], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); + + /* 3rd component */ + CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ + CU_ASSERT_EQUAL(entry[13], 0); + + /* 4th component */ + CU_ASSERT_EQUAL(entry[14], 0); + CU_ASSERT_EQUAL(entry[15], 1); + CU_ASSERT_EQUAL(entry[16], 'a'); + + /* 5th component */ + CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[18], 0); + + /* 6th component */ + CU_ASSERT_EQUAL(entry[19], 0); + CU_ASSERT_EQUAL(entry[20], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); + + /* 7th component */ + CU_ASSERT_EQUAL(entry[24], 0); + CU_ASSERT_EQUAL(entry[25], 11); + CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); + + /* 8th component */ + CU_ASSERT_EQUAL(entry[37], 0); + CU_ASSERT_EQUAL(entry[38], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); + + /* 9th component */ + CU_ASSERT_EQUAL(entry[43], 0); + CU_ASSERT_EQUAL(entry[44], 2); + CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); + + /* 10th component */ + CU_ASSERT_EQUAL(entry[47], 0); + CU_ASSERT_EQUAL(entry[48], 10); + CU_ASSERT_NSTRING_EQUAL(entry + 49, "components", 10); + + susp_info_free(&susp); + + /* case 3. name fits, dest is one byte larger to fit */ + /* 3.a extra byte in dest */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "./and/../a/./big/destination/with/10/componentsk"; + node->iso_name = "THIS_NAM.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 60); + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* SL */ + CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ + CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); + + /* PX is the first entry */ + entry = susp.susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'P'); + CU_ASSERT_EQUAL(entry[1], 'X'); + CU_ASSERT_EQUAL(entry[2], 44); + + /* TF is the second entry */ + entry = susp.susp_fields[1]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'T'); + CU_ASSERT_EQUAL(entry[1], 'F'); + CU_ASSERT_EQUAL(entry[2], 5 + 3*7); + + /* NM is the 3rd entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 74); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 74 characters as " + "it is the max that fits in the SU.txt", 74); + + /* and CE entry is last */ + entry = susp.susp_fields[3]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'C'); + CU_ASSERT_EQUAL(entry[1], 'E'); + CU_ASSERT_EQUAL(entry[2], 28); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 60); + CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 60); + + + /* finally, SL is the single entry in CE */ + entry = susp.ce_susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'S'); + CU_ASSERT_EQUAL(entry[1], 'L'); + CU_ASSERT_EQUAL(entry[2], 60); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + + /* first component */ + CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[6], 0); + + /* 2nd component */ + CU_ASSERT_EQUAL(entry[7], 0); + CU_ASSERT_EQUAL(entry[8], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); + + /* 3rd component */ + CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ + CU_ASSERT_EQUAL(entry[13], 0); + + /* 4th component */ + CU_ASSERT_EQUAL(entry[14], 0); + CU_ASSERT_EQUAL(entry[15], 1); + CU_ASSERT_EQUAL(entry[16], 'a'); + + /* 5th component */ + CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[18], 0); + + /* 6th component */ + CU_ASSERT_EQUAL(entry[19], 0); + CU_ASSERT_EQUAL(entry[20], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); + + /* 7th component */ + CU_ASSERT_EQUAL(entry[24], 0); + CU_ASSERT_EQUAL(entry[25], 11); + CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); + + /* 8th component */ + CU_ASSERT_EQUAL(entry[37], 0); + CU_ASSERT_EQUAL(entry[38], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); + + /* 9th component */ + CU_ASSERT_EQUAL(entry[43], 0); + CU_ASSERT_EQUAL(entry[44], 2); + CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); + + /* 10th component */ + CU_ASSERT_EQUAL(entry[47], 0); + CU_ASSERT_EQUAL(entry[48], 11); + CU_ASSERT_NSTRING_EQUAL(entry + 49, "componentsk", 11); + + susp_info_free(&susp); + + /* 3.b extra byte in name */ + link->node.name = "this name will have 75 characters as it is the max " + "that fits in the SUx.txt"; + link->dest = "./and/../a/./big/destination/with/10/components"; + node->iso_name = "THIS_NAM.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 59); + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* SL */ + CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ + CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 75) + (5 + 3*7) + 28); + + /* NM is the 3rd entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 75); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 75 characters as it " + "is the max that fits in the SUx.txt", 75); + + /* and CE entry is last */ + entry = susp.susp_fields[3]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'C'); + CU_ASSERT_EQUAL(entry[1], 'E'); + CU_ASSERT_EQUAL(entry[2], 28); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 59); + CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 59); + + + /* finally, SL is the single entry in CE */ + entry = susp.ce_susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'S'); + CU_ASSERT_EQUAL(entry[1], 'L'); + CU_ASSERT_EQUAL(entry[2], 59); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + + /* first component */ + CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[6], 0); + + /* 2nd component */ + CU_ASSERT_EQUAL(entry[7], 0); + CU_ASSERT_EQUAL(entry[8], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); + + /* 3rd component */ + CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ + CU_ASSERT_EQUAL(entry[13], 0); + + /* 4th component */ + CU_ASSERT_EQUAL(entry[14], 0); + CU_ASSERT_EQUAL(entry[15], 1); + CU_ASSERT_EQUAL(entry[16], 'a'); + + /* 5th component */ + CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[18], 0); + + /* 6th component */ + CU_ASSERT_EQUAL(entry[19], 0); + CU_ASSERT_EQUAL(entry[20], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); + + /* 7th component */ + CU_ASSERT_EQUAL(entry[24], 0); + CU_ASSERT_EQUAL(entry[25], 11); + CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); + + /* 8th component */ + CU_ASSERT_EQUAL(entry[37], 0); + CU_ASSERT_EQUAL(entry[38], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); + + /* 9th component */ + CU_ASSERT_EQUAL(entry[43], 0); + CU_ASSERT_EQUAL(entry[44], 2); + CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); + + /* 10th component */ + CU_ASSERT_EQUAL(entry[47], 0); + CU_ASSERT_EQUAL(entry[48], 10); + CU_ASSERT_NSTRING_EQUAL(entry + 49, "components", 10); + + susp_info_free(&susp); + + /* case 4. name seems to fit, but SL no, and when CE is added NM + * doesn't fit too */ + /* 4.a it just fits */ + link->node.name = "this name will have 105 characters as it is just the " + "max that fits in the SU once we add the CE entry.txt"; + link->dest = "./and/../a/./big/destination/with/10/components"; + node->iso_name = "THIS_NAM.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 59); + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* SL */ + CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ + CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 3*7) + (5 + 105) + 28); + + /* NM is the 3rd entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 105); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 105 characters as " + "it is just the max that fits in the SU once we " + "add the CE entry.txt", 105); + + /* and CE entry is last */ + entry = susp.susp_fields[3]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'C'); + CU_ASSERT_EQUAL(entry[1], 'E'); + CU_ASSERT_EQUAL(entry[2], 28); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 59); + CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 59); + + /* finally, SL is the single entry in CE */ + entry = susp.ce_susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'S'); + CU_ASSERT_EQUAL(entry[1], 'L'); + CU_ASSERT_EQUAL(entry[2], 59); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + + /* first component */ + CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[6], 0); + + /* 2nd component */ + CU_ASSERT_EQUAL(entry[7], 0); + CU_ASSERT_EQUAL(entry[8], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); + + /* 3rd component */ + CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ + CU_ASSERT_EQUAL(entry[13], 0); + + /* 4th component */ + CU_ASSERT_EQUAL(entry[14], 0); + CU_ASSERT_EQUAL(entry[15], 1); + CU_ASSERT_EQUAL(entry[16], 'a'); + + /* 5th component */ + CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[18], 0); + + /* 6th component */ + CU_ASSERT_EQUAL(entry[19], 0); + CU_ASSERT_EQUAL(entry[20], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); + + /* 7th component */ + CU_ASSERT_EQUAL(entry[24], 0); + CU_ASSERT_EQUAL(entry[25], 11); + CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); + + /* 8th component */ + CU_ASSERT_EQUAL(entry[37], 0); + CU_ASSERT_EQUAL(entry[38], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); + + /* 9th component */ + CU_ASSERT_EQUAL(entry[43], 0); + CU_ASSERT_EQUAL(entry[44], 2); + CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); + + /* 10th component */ + CU_ASSERT_EQUAL(entry[47], 0); + CU_ASSERT_EQUAL(entry[48], 10); + CU_ASSERT_NSTRING_EQUAL(entry + 49, "components", 10); + + susp_info_free(&susp); + + /* 4.b it just fits, the the component ends in '/' */ + link->node.name = "this name will have 105 characters as it is just the " + "max that fits in the SU once we add the CE entry.txt"; + link->dest = "./and/../a/./big/destination/with/10/components/"; + node->iso_name = "THIS_NAM.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 59); + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* SL */ + CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ + CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 3*7) + (5 + 105) + 28); + + /* NM is the 3rd entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 105); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 105 characters as " + "it is just the max that fits in the SU once we " + "add the CE entry.txt", 105); + + /* and CE entry is last */ + entry = susp.susp_fields[3]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'C'); + CU_ASSERT_EQUAL(entry[1], 'E'); + CU_ASSERT_EQUAL(entry[2], 28); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 59); + CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 59); + + /* finally, SL is the single entry in CE */ + entry = susp.ce_susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'S'); + CU_ASSERT_EQUAL(entry[1], 'L'); + CU_ASSERT_EQUAL(entry[2], 59); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + + /* first component */ + CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[6], 0); + + /* 2nd component */ + CU_ASSERT_EQUAL(entry[7], 0); + CU_ASSERT_EQUAL(entry[8], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); + + /* 3rd component */ + CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ + CU_ASSERT_EQUAL(entry[13], 0); + + /* 4th component */ + CU_ASSERT_EQUAL(entry[14], 0); + CU_ASSERT_EQUAL(entry[15], 1); + CU_ASSERT_EQUAL(entry[16], 'a'); + + /* 5th component */ + CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[18], 0); + + /* 6th component */ + CU_ASSERT_EQUAL(entry[19], 0); + CU_ASSERT_EQUAL(entry[20], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); + + /* 7th component */ + CU_ASSERT_EQUAL(entry[24], 0); + CU_ASSERT_EQUAL(entry[25], 11); + CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); + + /* 8th component */ + CU_ASSERT_EQUAL(entry[37], 0); + CU_ASSERT_EQUAL(entry[38], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); + + /* 9th component */ + CU_ASSERT_EQUAL(entry[43], 0); + CU_ASSERT_EQUAL(entry[44], 2); + CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); + + /* 10th component */ + CU_ASSERT_EQUAL(entry[47], 0); + CU_ASSERT_EQUAL(entry[48], 10); + CU_ASSERT_NSTRING_EQUAL(entry + 49, "components", 10); + + susp_info_free(&susp); + + /* 4.c extra char in name, that forces it to be divided */ + link->node.name = "this name will have 106 characters as it is just the " + "max that fits in the SU once we add the CE entryc.txt"; + link->dest = "./and/../a/./big/destination/with/10/components"; + node->iso_name = "THIS_NAM.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 6 + 59); + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 2); /* NM + SL */ + CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ + CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 3*7) + (5 + 105) + 28); + + /* NM is the 3rd entry */ + entry = susp.susp_fields[2]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 105); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0x1); /* continue */ + CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 106 characters as " + "it is just the max that fits in the SU once we " + "add the CE entryc.tx", 105); + + /* and CE entry is last */ + entry = susp.susp_fields[3]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'C'); + CU_ASSERT_EQUAL(entry[1], 'E'); + CU_ASSERT_EQUAL(entry[2], 28); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); + CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); + CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 59 + 6); + CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 59 + 6); + + /* NM is the 1st entry in CE */ + entry = susp.ce_susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'N'); + CU_ASSERT_EQUAL(entry[1], 'M'); + CU_ASSERT_EQUAL(entry[2], 5 + 1); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + CU_ASSERT_EQUAL(entry[5], 't'); + + /* finally, SL is the single entry in CE */ + entry = susp.ce_susp_fields[1]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'S'); + CU_ASSERT_EQUAL(entry[1], 'L'); + CU_ASSERT_EQUAL(entry[2], 59); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + + /* first component */ + CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[6], 0); + + /* 2nd component */ + CU_ASSERT_EQUAL(entry[7], 0); + CU_ASSERT_EQUAL(entry[8], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); + + /* 3rd component */ + CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ + CU_ASSERT_EQUAL(entry[13], 0); + + /* 4th component */ + CU_ASSERT_EQUAL(entry[14], 0); + CU_ASSERT_EQUAL(entry[15], 1); + CU_ASSERT_EQUAL(entry[16], 'a'); + + /* 5th component */ + CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[18], 0); + + /* 6th component */ + CU_ASSERT_EQUAL(entry[19], 0); + CU_ASSERT_EQUAL(entry[20], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); + + /* 7th component */ + CU_ASSERT_EQUAL(entry[24], 0); + CU_ASSERT_EQUAL(entry[25], 11); + CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); + + /* 8th component */ + CU_ASSERT_EQUAL(entry[37], 0); + CU_ASSERT_EQUAL(entry[38], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); + + /* 9th component */ + CU_ASSERT_EQUAL(entry[43], 0); + CU_ASSERT_EQUAL(entry[44], 2); + CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); + + /* 10th component */ + CU_ASSERT_EQUAL(entry[47], 0); + CU_ASSERT_EQUAL(entry[48], 10); + CU_ASSERT_NSTRING_EQUAL(entry + 49, "components", 10); + + susp_info_free(&susp); + + /* 5 max destination length to fit in a single SL entry (250) */ + link->node.name = "this name will have 74 characters as it is the max " + "that fits in the SU.txt"; + link->dest = "./and/../a/./very/big/destination/with/10/components/that/" + "conforms/the/max/that/fits/in/a single SL/entry as it takes " + "just two hundred and/fifty bytes bytes bytes bytes/bytes" + " bytes bytes bytes bytes bytes bytes bytes bytes/../bytes"; + node->iso_name = "THIS_NAM.TXT"; + + memset(&susp, 0, sizeof(struct susp_info)); + ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_EQUAL(susp.ce_len, 255); + CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* SL */ + CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ + CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 3*7) + (5 + 74) + 1 + 28); + + /* just check the SL entry */ + entry = susp.ce_susp_fields[0]; + CU_ASSERT_PTR_NOT_NULL(entry); + CU_ASSERT_EQUAL(entry[0], 'S'); + CU_ASSERT_EQUAL(entry[1], 'L'); + CU_ASSERT_EQUAL(entry[2], 255); + CU_ASSERT_EQUAL(entry[3], 1); + CU_ASSERT_EQUAL(entry[4], 0); + + /* first component */ + CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[6], 0); + + /* 2nd component */ + CU_ASSERT_EQUAL(entry[7], 0); + CU_ASSERT_EQUAL(entry[8], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); + + /* 3rd component */ + CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ + CU_ASSERT_EQUAL(entry[13], 0); + + /* 4th component */ + CU_ASSERT_EQUAL(entry[14], 0); + CU_ASSERT_EQUAL(entry[15], 1); + CU_ASSERT_EQUAL(entry[16], 'a'); + + /* 5th component */ + CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ + CU_ASSERT_EQUAL(entry[18], 0); + + /* 6th component */ + CU_ASSERT_EQUAL(entry[19], 0); + CU_ASSERT_EQUAL(entry[20], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 21, "very", 4); + + /* 7th component */ + CU_ASSERT_EQUAL(entry[25], 0); + CU_ASSERT_EQUAL(entry[26], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 27, "big", 3); + + /* 8th component */ + CU_ASSERT_EQUAL(entry[30], 0); + CU_ASSERT_EQUAL(entry[31], 11); + CU_ASSERT_NSTRING_EQUAL(entry + 32, "destination", 11); + + /* 9th component */ + CU_ASSERT_EQUAL(entry[43], 0); + CU_ASSERT_EQUAL(entry[44], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 45, "with", 4); + + /* 10th component */ + CU_ASSERT_EQUAL(entry[49], 0); + CU_ASSERT_EQUAL(entry[50], 2); + CU_ASSERT_NSTRING_EQUAL(entry + 51, "10", 2); + + /* 11th component */ + CU_ASSERT_EQUAL(entry[53], 0); + CU_ASSERT_EQUAL(entry[54], 10); + CU_ASSERT_NSTRING_EQUAL(entry + 55, "components", 10); + + /* 12th component */ + CU_ASSERT_EQUAL(entry[65], 0); + CU_ASSERT_EQUAL(entry[66], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 67, "that", 4); + + /* 13th component */ + CU_ASSERT_EQUAL(entry[71], 0); + CU_ASSERT_EQUAL(entry[72], 8); + CU_ASSERT_NSTRING_EQUAL(entry + 73, "conforms", 8); + + /* 14th component */ + CU_ASSERT_EQUAL(entry[81], 0); + CU_ASSERT_EQUAL(entry[82], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 83, "the", 3); + + /* 15th component */ + CU_ASSERT_EQUAL(entry[86], 0); + CU_ASSERT_EQUAL(entry[87], 3); + CU_ASSERT_NSTRING_EQUAL(entry + 88, "max", 3); + + /* 16th component */ + CU_ASSERT_EQUAL(entry[91], 0); + CU_ASSERT_EQUAL(entry[92], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 93, "that", 4); + + /* 17th component */ + CU_ASSERT_EQUAL(entry[97], 0); + CU_ASSERT_EQUAL(entry[98], 4); + CU_ASSERT_NSTRING_EQUAL(entry + 99, "fits", 4); + + /* 18th component */ + CU_ASSERT_EQUAL(entry[103], 0); + CU_ASSERT_EQUAL(entry[104], 2); + CU_ASSERT_NSTRING_EQUAL(entry + 105, "in", 2); + + /* 19th component */ + CU_ASSERT_EQUAL(entry[107], 0); + CU_ASSERT_EQUAL(entry[108], 11); + CU_ASSERT_NSTRING_EQUAL(entry + 109, "a single SL", 11); + + /* 20th component */ + CU_ASSERT_EQUAL(entry[120], 0); + CU_ASSERT_EQUAL(entry[121], 38); + CU_ASSERT_NSTRING_EQUAL(entry + 122, "entry as it takes " + "just two hundred and", 38); + + /* 21th component */ + CU_ASSERT_EQUAL(entry[160], 0); + CU_ASSERT_EQUAL(entry[161], 29); + CU_ASSERT_NSTRING_EQUAL(entry + 162, "fifty bytes bytes bytes bytes", 29); + + /* 22th component */ + CU_ASSERT_EQUAL(entry[191], 0); + CU_ASSERT_EQUAL(entry[192], 53); + CU_ASSERT_NSTRING_EQUAL(entry + 193, "bytes bytes bytes bytes bytes bytes" + " bytes bytes bytes", 53); + + /* 23th component */ + CU_ASSERT_EQUAL(entry[246], 0x4); /* parent */ + CU_ASSERT_EQUAL(entry[247], 0); + + /* 24th component */ + CU_ASSERT_EQUAL(entry[248], 0); + CU_ASSERT_EQUAL(entry[249], 5); + CU_ASSERT_NSTRING_EQUAL(entry + 250, "bytes", 5); + + susp_info_free(&susp); + + free(node); + free(link); +} + +void add_rockridge_suite() +{ + CU_pSuite pSuite = CU_add_suite("RockRidge Suite", NULL, NULL); + + CU_add_test(pSuite, "rrip_calc_len(file)", test_rrip_calc_len_file); + CU_add_test(pSuite, "rrip_calc_len(symlink)", test_rrip_calc_len_symlink); + CU_add_test(pSuite, "rrip_get_susp_fields(file)", test_rrip_get_susp_fields_file); + CU_add_test(pSuite, "rrip_get_susp_fields(symlink)", test_rrip_get_susp_fields_symlink); +} diff --git a/libisofs/branches/thomas/test/test_stream.c b/libisofs/branches/thomas/test/test_stream.c new file mode 100644 index 00000000..35e14666 --- /dev/null +++ b/libisofs/branches/thomas/test/test_stream.c @@ -0,0 +1,155 @@ +/* + * Unit test for util.h + * + * This test utiliy functions + */ +#include "test.h" +#include "stream.h" + +#include + +static +void test_mem_new() +{ + int ret; + IsoStream *stream; + unsigned char *buf; + + buf = malloc(3000); + ret = iso_memory_stream_new(buf, 3000, &stream); + CU_ASSERT_EQUAL(ret, 1); + iso_stream_unref(stream); + + ret = iso_memory_stream_new(NULL, 3000, &stream); + CU_ASSERT_EQUAL(ret, ISO_NULL_POINTER); + + ret = iso_memory_stream_new(buf, 3000, NULL); + CU_ASSERT_EQUAL(ret, ISO_NULL_POINTER); +} + +static +void test_mem_open() +{ + int ret; + IsoStream *stream; + unsigned char *buf; + + buf = malloc(3000); + ret = iso_memory_stream_new(buf, 3000, &stream); + CU_ASSERT_EQUAL(ret, 1); + + ret = iso_stream_open(stream); + CU_ASSERT_EQUAL(ret, 1); + + /* try to open an already opened stream */ + ret = iso_stream_open(stream); + CU_ASSERT_EQUAL(ret, ISO_FILE_ALREADY_OPENNED); + + ret = iso_stream_close(stream); + CU_ASSERT_EQUAL(ret, 1); + + ret = iso_stream_close(stream); + CU_ASSERT_EQUAL(ret, ISO_FILE_NOT_OPENNED); + + iso_stream_unref(stream); +} + +static +void test_mem_read() +{ + int ret; + IsoStream *stream; + unsigned char *buf; + unsigned char rbuf[3000]; + + buf = malloc(3000); + memset(buf, 2, 200); + memset(buf + 200, 3, 300); + memset(buf + 500, 5, 500); + memset(buf + 1000, 10, 1000); + memset(buf + 2000, 56, 48); + memset(buf + 2048, 137, 22); + memset(buf + 2070, 13, 130); + memset(buf + 2200, 88, 800); + + ret = iso_memory_stream_new(buf, 3000, &stream); + CU_ASSERT_EQUAL(ret, 1); + + /* test 1: read full buf */ + ret = iso_stream_open(stream); + CU_ASSERT_EQUAL(ret, 1); + + ret = iso_stream_read(stream, rbuf, 3000); + CU_ASSERT_EQUAL(ret, 3000); + CU_ASSERT_NSTRING_EQUAL(rbuf, buf, 3000); + + /* read again is EOF */ + ret = iso_stream_read(stream, rbuf, 20); + CU_ASSERT_EQUAL(ret, 0); + + ret = iso_stream_close(stream); + CU_ASSERT_EQUAL(ret, 1); + + /* test 2: read more than available bytes */ + ret = iso_stream_open(stream); + CU_ASSERT_EQUAL(ret, 1); + + ret = iso_stream_read(stream, rbuf, 3050); + CU_ASSERT_EQUAL(ret, 3000); + CU_ASSERT_NSTRING_EQUAL(rbuf, buf, 3000); + + /* read again is EOF */ + ret = iso_stream_read(stream, rbuf, 20); + CU_ASSERT_EQUAL(ret, 0); + + ret = iso_stream_close(stream); + CU_ASSERT_EQUAL(ret, 1); + + /* test 3: read in block size */ + ret = iso_stream_open(stream); + CU_ASSERT_EQUAL(ret, 1); + + ret = iso_stream_read(stream, rbuf, 2048); + CU_ASSERT_EQUAL(ret, 2048); + CU_ASSERT_NSTRING_EQUAL(rbuf, buf, 2048); + + ret = iso_stream_read(stream, rbuf, 2048); + CU_ASSERT_EQUAL(ret, 3000 - 2048); + CU_ASSERT_NSTRING_EQUAL(rbuf, buf + 2048, 3000 - 2048); + + ret = iso_stream_read(stream, rbuf, 20); + CU_ASSERT_EQUAL(ret, 0); + + ret = iso_stream_close(stream); + CU_ASSERT_EQUAL(ret, 1); + + iso_stream_unref(stream); +} + +static +void test_mem_size() +{ + int ret; + off_t size; + IsoStream *stream; + unsigned char *buf; + + buf = malloc(3000); + ret = iso_memory_stream_new(buf, 3000, &stream); + CU_ASSERT_EQUAL(ret, 1); + + size = iso_stream_get_size(stream); + CU_ASSERT_EQUAL(size, 3000); + + iso_stream_unref(stream); +} + +void add_stream_suite() +{ + CU_pSuite pSuite = CU_add_suite("IsoStreamSuite", NULL, NULL); + + CU_add_test(pSuite, "iso_memory_stream_new()", test_mem_new); + CU_add_test(pSuite, "MemoryStream->open()", test_mem_open); + CU_add_test(pSuite, "MemoryStream->read()", test_mem_read); + CU_add_test(pSuite, "MemoryStream->get_size()", test_mem_size); +} diff --git a/libisofs/branches/thomas/test/test_tree.c b/libisofs/branches/thomas/test/test_tree.c new file mode 100644 index 00000000..8379439c --- /dev/null +++ b/libisofs/branches/thomas/test/test_tree.c @@ -0,0 +1,566 @@ +/* + * Unit test for node.h + */ + +#include "libisofs.h" +#include "node.h" +#include "image.h" + +#include "test.h" +#include "mocked_fsrc.h" + +#include + +static +void test_iso_tree_add_new_dir() +{ + int result; + IsoDir *root; + IsoDir *node1, *node2, *node3, *node4; + IsoImage *image; + + result = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(result, 1); + root = iso_image_get_root(image); + CU_ASSERT_PTR_NOT_NULL(root); + + result = iso_tree_add_new_dir(root, "Dir1", &node1); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_EQUAL(root->nchildren, 1); + CU_ASSERT_PTR_EQUAL(root->children, node1); + CU_ASSERT_PTR_NULL(node1->node.next); + CU_ASSERT_PTR_EQUAL(node1->node.parent, root); + CU_ASSERT_EQUAL(node1->node.type, LIBISO_DIR); + CU_ASSERT_STRING_EQUAL(node1->node.name, "Dir1"); + + /* creation of a second dir, to be inserted before */ + result = iso_tree_add_new_dir(root, "A node to be added first", &node2); + CU_ASSERT_EQUAL(result, 2); + CU_ASSERT_EQUAL(root->nchildren, 2); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->node.next, node1); + CU_ASSERT_PTR_NULL(node1->node.next); + CU_ASSERT_PTR_EQUAL(node2->node.parent, root); + CU_ASSERT_EQUAL(node2->node.type, LIBISO_DIR); + CU_ASSERT_STRING_EQUAL(node2->node.name, "A node to be added first"); + + /* creation of a 3rd node, to be inserted last */ + result = iso_tree_add_new_dir(root, "This node will be inserted last", &node3); + CU_ASSERT_EQUAL(result, 3); + CU_ASSERT_EQUAL(root->nchildren, 3); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->node.next, node1); + CU_ASSERT_PTR_EQUAL(node1->node.next, node3); + CU_ASSERT_PTR_NULL(node3->node.next); + CU_ASSERT_PTR_EQUAL(node3->node.parent, root); + CU_ASSERT_EQUAL(node3->node.type, LIBISO_DIR); + CU_ASSERT_STRING_EQUAL(node3->node.name, "This node will be inserted last"); + + /* force some failures */ + result = iso_tree_add_new_dir(NULL, "dsadas", &node4); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + result = iso_tree_add_new_dir(root, NULL, &node4); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + + /* try to insert a new dir with same name */ + result = iso_tree_add_new_dir(root, "This node will be inserted last", &node4); + CU_ASSERT_EQUAL(result, ISO_NODE_NAME_NOT_UNIQUE); + CU_ASSERT_EQUAL(root->nchildren, 3); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->node.next, node1); + CU_ASSERT_PTR_EQUAL(node1->node.next, node3); + CU_ASSERT_PTR_NULL(node3->node.next); + CU_ASSERT_PTR_NULL(node4); + + /* but pointer to new dir can be null */ + result = iso_tree_add_new_dir(root, "Another node", NULL); + CU_ASSERT_EQUAL(result, 4); + CU_ASSERT_EQUAL(root->nchildren, 4); + CU_ASSERT_PTR_EQUAL(node2->node.next->next, node1); + CU_ASSERT_STRING_EQUAL(node2->node.next->name, "Another node"); + + iso_image_unref(image); +} + +static +void test_iso_tree_add_new_symlink() +{ + int result; + IsoDir *root; + IsoSymlink *node1, *node2, *node3, *node4; + IsoImage *image; + + result = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(result, 1); + root = iso_image_get_root(image); + CU_ASSERT_PTR_NOT_NULL(root); + + result = iso_tree_add_new_symlink(root, "Link1", "/path/to/dest", &node1); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_EQUAL(root->nchildren, 1); + CU_ASSERT_PTR_EQUAL(root->children, node1); + CU_ASSERT_PTR_NULL(node1->node.next); + CU_ASSERT_PTR_EQUAL(node1->node.parent, root); + CU_ASSERT_EQUAL(node1->node.type, LIBISO_SYMLINK); + CU_ASSERT_STRING_EQUAL(node1->node.name, "Link1"); + CU_ASSERT_STRING_EQUAL(node1->dest, "/path/to/dest"); + + /* creation of a second link, to be inserted before */ + result = iso_tree_add_new_symlink(root, "A node to be added first", "/home/me", &node2); + CU_ASSERT_EQUAL(result, 2); + CU_ASSERT_EQUAL(root->nchildren, 2); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->node.next, node1); + CU_ASSERT_PTR_NULL(node1->node.next); + CU_ASSERT_PTR_EQUAL(node2->node.parent, root); + CU_ASSERT_EQUAL(node2->node.type, LIBISO_SYMLINK); + CU_ASSERT_STRING_EQUAL(node2->node.name, "A node to be added first"); + CU_ASSERT_STRING_EQUAL(node2->dest, "/home/me"); + + /* creation of a 3rd node, to be inserted last */ + result = iso_tree_add_new_symlink(root, "This node will be inserted last", + "/path/to/dest", &node3); + CU_ASSERT_EQUAL(result, 3); + CU_ASSERT_EQUAL(root->nchildren, 3); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->node.next, node1); + CU_ASSERT_PTR_EQUAL(node1->node.next, node3); + CU_ASSERT_PTR_NULL(node3->node.next); + CU_ASSERT_PTR_EQUAL(node3->node.parent, root); + CU_ASSERT_EQUAL(node3->node.type, LIBISO_SYMLINK); + CU_ASSERT_STRING_EQUAL(node3->node.name, "This node will be inserted last"); + CU_ASSERT_STRING_EQUAL(node3->dest, "/path/to/dest"); + + /* force some failures */ + result = iso_tree_add_new_symlink(NULL, "dsadas", "/path/to/dest", &node4); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + result = iso_tree_add_new_symlink(root, NULL, "/path/to/dest", &node4); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + result = iso_tree_add_new_symlink(root, "dsadas", NULL, &node4); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + + /* try to insert a new link with same name */ + result = iso_tree_add_new_symlink(root, "This node will be inserted last", "/", &node4); + CU_ASSERT_EQUAL(result, ISO_NODE_NAME_NOT_UNIQUE); + CU_ASSERT_EQUAL(root->nchildren, 3); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->node.next, node1); + CU_ASSERT_PTR_EQUAL(node1->node.next, node3); + CU_ASSERT_PTR_NULL(node3->node.next); + CU_ASSERT_PTR_NULL(node4); + + /* but pointer to new link can be null */ + result = iso_tree_add_new_symlink(root, "Another node", ".", NULL); + CU_ASSERT_EQUAL(result, 4); + CU_ASSERT_EQUAL(root->nchildren, 4); + CU_ASSERT_PTR_EQUAL(node2->node.next->next, node1); + CU_ASSERT_EQUAL(node2->node.next->type, LIBISO_SYMLINK); + CU_ASSERT_STRING_EQUAL(((IsoSymlink*)(node2->node.next))->dest, "."); + CU_ASSERT_STRING_EQUAL(node2->node.next->name, "Another node"); + + iso_image_unref(image); +} + +static +void test_iso_tree_add_new_special() +{ + int result; + IsoDir *root; + IsoSpecial *node1, *node2, *node3, *node4; + IsoImage *image; + + result = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(result, 1); + root = iso_image_get_root(image); + CU_ASSERT_PTR_NOT_NULL(root); + + result = iso_tree_add_new_special(root, "Special1", S_IFSOCK | 0644, 0, &node1); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_EQUAL(root->nchildren, 1); + CU_ASSERT_PTR_EQUAL(root->children, node1); + CU_ASSERT_PTR_NULL(node1->node.next); + CU_ASSERT_PTR_EQUAL(node1->node.parent, root); + CU_ASSERT_EQUAL(node1->node.type, LIBISO_SPECIAL); + CU_ASSERT_STRING_EQUAL(node1->node.name, "Special1"); + CU_ASSERT_EQUAL(node1->dev, 0); + CU_ASSERT_EQUAL(node1->node.mode, S_IFSOCK | 0644); + + /* creation of a block dev, to be inserted before */ + result = iso_tree_add_new_special(root, "A node to be added first", S_IFBLK | 0640, 34, &node2); + CU_ASSERT_EQUAL(result, 2); + CU_ASSERT_EQUAL(root->nchildren, 2); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->node.next, node1); + CU_ASSERT_PTR_NULL(node1->node.next); + CU_ASSERT_PTR_EQUAL(node2->node.parent, root); + CU_ASSERT_EQUAL(node2->node.type, LIBISO_SPECIAL); + CU_ASSERT_STRING_EQUAL(node2->node.name, "A node to be added first"); + CU_ASSERT_EQUAL(node2->dev, 34); + CU_ASSERT_EQUAL(node2->node.mode, S_IFBLK | 0640); + + /* creation of a 3rd node, to be inserted last */ + result = iso_tree_add_new_special(root, "This node will be inserted last", + S_IFCHR | 0440, 345, &node3); + CU_ASSERT_EQUAL(result, 3); + CU_ASSERT_EQUAL(root->nchildren, 3); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->node.next, node1); + CU_ASSERT_PTR_EQUAL(node1->node.next, node3); + CU_ASSERT_PTR_NULL(node3->node.next); + CU_ASSERT_PTR_EQUAL(node3->node.parent, root); + CU_ASSERT_EQUAL(node3->node.type, LIBISO_SPECIAL); + CU_ASSERT_STRING_EQUAL(node3->node.name, "This node will be inserted last"); + CU_ASSERT_EQUAL(node3->dev, 345); + CU_ASSERT_EQUAL(node3->node.mode, S_IFCHR | 0440); + + /* force some failures */ + result = iso_tree_add_new_special(NULL, "dsadas", S_IFBLK | 0440, 345, &node4); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + result = iso_tree_add_new_special(root, NULL, S_IFBLK | 0440, 345, &node4); + CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); + result = iso_tree_add_new_special(root, "dsadas", S_IFDIR | 0666, 0, &node4); + CU_ASSERT_EQUAL(result, ISO_WRONG_ARG_VALUE); + result = iso_tree_add_new_special(root, "dsadas", S_IFREG | 0666, 0, &node4); + CU_ASSERT_EQUAL(result, ISO_WRONG_ARG_VALUE); + result = iso_tree_add_new_special(root, "dsadas", S_IFLNK | 0666, 0, &node4); + CU_ASSERT_EQUAL(result, ISO_WRONG_ARG_VALUE); + + /* try to insert a new special file with same name */ + result = iso_tree_add_new_special(root, "This node will be inserted last", S_IFIFO | 0666, 0, &node4); + CU_ASSERT_EQUAL(result, ISO_NODE_NAME_NOT_UNIQUE); + CU_ASSERT_EQUAL(root->nchildren, 3); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->node.next, node1); + CU_ASSERT_PTR_EQUAL(node1->node.next, node3); + CU_ASSERT_PTR_NULL(node3->node.next); + CU_ASSERT_PTR_NULL(node4); + + /* but pointer to new special can be null */ + result = iso_tree_add_new_special(root, "Another node", S_IFIFO | 0666, 0, NULL); + CU_ASSERT_EQUAL(result, 4); + CU_ASSERT_EQUAL(root->nchildren, 4); + CU_ASSERT_PTR_EQUAL(node2->node.next->next, node1); + CU_ASSERT_EQUAL(node2->node.next->type, LIBISO_SPECIAL); + CU_ASSERT_EQUAL(((IsoSpecial*)(node2->node.next))->dev, 0); + CU_ASSERT_EQUAL(node2->node.next->mode, S_IFIFO | 0666); + CU_ASSERT_STRING_EQUAL(node2->node.next->name, "Another node"); + + iso_image_unref(image); +} + +static +void test_iso_tree_add_node_dir() +{ + int result; + IsoDir *root; + IsoNode *node1, *node2, *node3, *node4; + IsoImage *image; + IsoFilesystem *fs; + struct stat info; + struct mock_file *mroot, *dir1, *dir2; + + result = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(result, 1); + root = iso_image_get_root(image); + CU_ASSERT_PTR_NOT_NULL(root); + + /* replace image filesystem with out mockep one */ + iso_filesystem_unref(image->fs); + result = test_mocked_filesystem_new(&fs); + CU_ASSERT_EQUAL(result, 1); + image->fs = fs; + mroot = test_mocked_fs_get_root(fs); + + /* add some files to the filesystem */ + info.st_mode = S_IFDIR | 0550; + info.st_uid = 20; + info.st_gid = 21; + info.st_atime = 234523; + info.st_ctime = 23432432; + info.st_mtime = 1111123; + result = test_mocked_fs_add_dir("dir", mroot, info, &dir1); + CU_ASSERT_EQUAL(result, 1); + + info.st_mode = S_IFDIR | 0555; + info.st_uid = 30; + info.st_gid = 31; + info.st_atime = 3234523; + info.st_ctime = 3234432; + info.st_mtime = 3111123; + result = test_mocked_fs_add_dir("a child node", dir1, info, &dir2); + CU_ASSERT_EQUAL(result, 1); + + info.st_mode = S_IFDIR | 0750; + info.st_uid = 40; + info.st_gid = 41; + info.st_atime = 4234523; + info.st_ctime = 4234432; + info.st_mtime = 4111123; + result = test_mocked_fs_add_dir("another one", dir1, info, &dir2); + CU_ASSERT_EQUAL(result, 1); + + info.st_mode = S_IFDIR | 0755; + info.st_uid = 50; + info.st_gid = 51; + info.st_atime = 5234523; + info.st_ctime = 5234432; + info.st_mtime = 5111123; + result = test_mocked_fs_add_dir("zzzz", mroot, info, &dir2); + CU_ASSERT_EQUAL(result, 1); + + /* and now insert those files to the image */ + result = iso_tree_add_node(image, root, "/dir", &node1); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_EQUAL(root->nchildren, 1); + CU_ASSERT_PTR_EQUAL(root->children, node1); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_EQUAL(node1->parent, root); + CU_ASSERT_EQUAL(node1->type, LIBISO_DIR); + CU_ASSERT_STRING_EQUAL(node1->name, "dir"); + CU_ASSERT_EQUAL(node1->mode, S_IFDIR | 0550); + CU_ASSERT_EQUAL(node1->uid, 20); + CU_ASSERT_EQUAL(node1->gid, 21); + CU_ASSERT_EQUAL(node1->atime, 234523); + CU_ASSERT_EQUAL(node1->ctime, 23432432); + CU_ASSERT_EQUAL(node1->mtime, 1111123); + CU_ASSERT_PTR_NULL(((IsoDir*)node1)->children); + CU_ASSERT_EQUAL(((IsoDir*)node1)->nchildren, 0); + + result = iso_tree_add_node(image, root, "/dir/a child node", &node2); + CU_ASSERT_EQUAL(result, 2); + CU_ASSERT_EQUAL(root->nchildren, 2); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->next, node1); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_EQUAL(node1->parent, root); + CU_ASSERT_PTR_EQUAL(node2->parent, root); + CU_ASSERT_EQUAL(node2->type, LIBISO_DIR); + CU_ASSERT_STRING_EQUAL(node2->name, "a child node"); + CU_ASSERT_EQUAL(node2->mode, S_IFDIR | 0555); + CU_ASSERT_EQUAL(node2->uid, 30); + CU_ASSERT_EQUAL(node2->gid, 31); + CU_ASSERT_EQUAL(node2->atime, 3234523); + CU_ASSERT_EQUAL(node2->ctime, 3234432); + CU_ASSERT_EQUAL(node2->mtime, 3111123); + CU_ASSERT_PTR_NULL(((IsoDir*)node2)->children); + CU_ASSERT_EQUAL(((IsoDir*)node2)->nchildren, 0); + + result = iso_tree_add_node(image, root, "/dir/another one", &node3); + CU_ASSERT_EQUAL(result, 3); + CU_ASSERT_EQUAL(root->nchildren, 3); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->next, node3); + CU_ASSERT_PTR_EQUAL(node3->next, node1); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_EQUAL(node1->parent, root); + CU_ASSERT_PTR_EQUAL(node2->parent, root); + CU_ASSERT_PTR_EQUAL(node3->parent, root); + CU_ASSERT_EQUAL(node3->type, LIBISO_DIR); + CU_ASSERT_STRING_EQUAL(node3->name, "another one"); + CU_ASSERT_EQUAL(node3->mode, S_IFDIR | 0750); + CU_ASSERT_EQUAL(node3->uid, 40); + CU_ASSERT_EQUAL(node3->gid, 41); + CU_ASSERT_EQUAL(node3->atime, 4234523); + CU_ASSERT_EQUAL(node3->ctime, 4234432); + CU_ASSERT_EQUAL(node3->mtime, 4111123); + CU_ASSERT_PTR_NULL(((IsoDir*)node3)->children); + CU_ASSERT_EQUAL(((IsoDir*)node3)->nchildren, 0); + + result = iso_tree_add_node(image, root, "/zzzz", &node4); + CU_ASSERT_EQUAL(result, 4); + CU_ASSERT_EQUAL(root->nchildren, 4); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->next, node3); + CU_ASSERT_PTR_EQUAL(node3->next, node1); + CU_ASSERT_PTR_EQUAL(node1->next, node4); + CU_ASSERT_PTR_NULL(node4->next); + CU_ASSERT_PTR_EQUAL(node1->parent, root); + CU_ASSERT_PTR_EQUAL(node2->parent, root); + CU_ASSERT_PTR_EQUAL(node3->parent, root); + CU_ASSERT_PTR_EQUAL(node4->parent, root); + CU_ASSERT_EQUAL(node4->type, LIBISO_DIR); + CU_ASSERT_STRING_EQUAL(node4->name, "zzzz"); + CU_ASSERT_EQUAL(node4->mode, S_IFDIR | 0755); + CU_ASSERT_EQUAL(node4->uid, 50); + CU_ASSERT_EQUAL(node4->gid, 51); + CU_ASSERT_EQUAL(node4->atime, 5234523); + CU_ASSERT_EQUAL(node4->ctime, 5234432); + CU_ASSERT_EQUAL(node4->mtime, 5111123); + CU_ASSERT_PTR_NULL(((IsoDir*)node4)->children); + CU_ASSERT_EQUAL(((IsoDir*)node4)->nchildren, 0); + + iso_image_unref(image); +} + +static +void test_iso_tree_add_node_link() +{ + int result; + IsoDir *root; + IsoNode *node1, *node2, *node3; + IsoImage *image; + IsoFilesystem *fs; + struct stat info; + struct mock_file *mroot, *link; + + result = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(result, 1); + root = iso_image_get_root(image); + CU_ASSERT_PTR_NOT_NULL(root); + + /* replace image filesystem with out mockep one */ + iso_filesystem_unref(image->fs); + result = test_mocked_filesystem_new(&fs); + CU_ASSERT_EQUAL(result, 1); + image->fs = fs; + mroot = test_mocked_fs_get_root(fs); + + /* add some files to the filesystem */ + info.st_mode = S_IFLNK | 0777; + info.st_uid = 12; + info.st_gid = 13; + info.st_atime = 123444; + info.st_ctime = 123555; + info.st_mtime = 123666; + result = test_mocked_fs_add_symlink("link1", mroot, info, "/home/me", &link); + CU_ASSERT_EQUAL(result, 1); + + info.st_mode = S_IFLNK | 0555; + info.st_uid = 22; + info.st_gid = 23; + info.st_atime = 223444; + info.st_ctime = 223555; + info.st_mtime = 223666; + result = test_mocked_fs_add_symlink("another link", mroot, info, "/", &link); + CU_ASSERT_EQUAL(result, 1); + + info.st_mode = S_IFLNK | 0750; + info.st_uid = 32; + info.st_gid = 33; + info.st_atime = 323444; + info.st_ctime = 323555; + info.st_mtime = 323666; + result = test_mocked_fs_add_symlink("this will be the last", mroot, info, "/etc", &link); + CU_ASSERT_EQUAL(result, 1); + + /* and now insert those files to the image */ + result = iso_tree_add_node(image, root, "/link1", &node1); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_EQUAL(root->nchildren, 1); + CU_ASSERT_PTR_EQUAL(root->children, node1); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_EQUAL(node1->parent, root); + CU_ASSERT_EQUAL(node1->type, LIBISO_SYMLINK); + CU_ASSERT_STRING_EQUAL(node1->name, "link1"); + CU_ASSERT_EQUAL(node1->mode, S_IFLNK | 0777); + CU_ASSERT_EQUAL(node1->uid, 12); + CU_ASSERT_EQUAL(node1->gid, 13); + CU_ASSERT_EQUAL(node1->atime, 123444); + CU_ASSERT_EQUAL(node1->ctime, 123555); + CU_ASSERT_EQUAL(node1->mtime, 123666); + CU_ASSERT_STRING_EQUAL(((IsoSymlink*)node1)->dest, "/home/me"); + + result = iso_tree_add_node(image, root, "/another link", &node2); + CU_ASSERT_EQUAL(result, 2); + CU_ASSERT_EQUAL(root->nchildren, 2); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->next, node1); + CU_ASSERT_PTR_NULL(node1->next); + CU_ASSERT_PTR_EQUAL(node1->parent, root); + CU_ASSERT_PTR_EQUAL(node2->parent, root); + CU_ASSERT_EQUAL(node2->type, LIBISO_SYMLINK); + CU_ASSERT_STRING_EQUAL(node2->name, "another link"); + CU_ASSERT_EQUAL(node2->mode, S_IFLNK | 0555); + CU_ASSERT_EQUAL(node2->uid, 22); + CU_ASSERT_EQUAL(node2->gid, 23); + CU_ASSERT_EQUAL(node2->atime, 223444); + CU_ASSERT_EQUAL(node2->ctime, 223555); + CU_ASSERT_EQUAL(node2->mtime, 223666); + CU_ASSERT_STRING_EQUAL(((IsoSymlink*)node2)->dest, "/"); + + result = iso_tree_add_node(image, root, "/this will be the last", &node3); + CU_ASSERT_EQUAL(result, 3); + CU_ASSERT_EQUAL(root->nchildren, 3); + CU_ASSERT_PTR_EQUAL(root->children, node2); + CU_ASSERT_PTR_EQUAL(node2->next, node1); + CU_ASSERT_PTR_EQUAL(node1->next, node3); + CU_ASSERT_PTR_NULL(node3->next); + CU_ASSERT_PTR_EQUAL(node1->parent, root); + CU_ASSERT_PTR_EQUAL(node2->parent, root); + CU_ASSERT_PTR_EQUAL(node3->parent, root); + CU_ASSERT_EQUAL(node3->type, LIBISO_SYMLINK); + CU_ASSERT_STRING_EQUAL(node3->name, "this will be the last"); + CU_ASSERT_EQUAL(node3->mode, S_IFLNK | 0750); + CU_ASSERT_EQUAL(node3->uid, 32); + CU_ASSERT_EQUAL(node3->gid, 33); + CU_ASSERT_EQUAL(node3->atime, 323444); + CU_ASSERT_EQUAL(node3->ctime, 323555); + CU_ASSERT_EQUAL(node3->mtime, 323666); + CU_ASSERT_STRING_EQUAL(((IsoSymlink*)node3)->dest, "/etc"); + + iso_image_unref(image); +} + +static +void test_iso_tree_path_to_node() +{ + int result; + IsoDir *root; + IsoDir *node1, *node2, *node11; + IsoNode *node; + IsoImage *image; + IsoFilesystem *fs; + + result = iso_image_new("volume_id", &image); + CU_ASSERT_EQUAL(result, 1); + root = iso_image_get_root(image); + CU_ASSERT_PTR_NOT_NULL(root); + + /* replace image filesystem with out mockep one */ + iso_filesystem_unref(image->fs); + result = test_mocked_filesystem_new(&fs); + CU_ASSERT_EQUAL(result, 1); + image->fs = fs; + + /* add some files */ + result = iso_tree_add_new_dir(root, "Dir1", &node1); + CU_ASSERT_EQUAL(result, 1); + result = iso_tree_add_new_dir(root, "Dir2", (IsoDir**)&node2); + CU_ASSERT_EQUAL(result, 2); + result = iso_tree_add_new_dir((IsoDir*)node1, "Dir11", (IsoDir**)&node11); + CU_ASSERT_EQUAL(result, 1); + + /* retrive some items */ + result = iso_tree_path_to_node(image, "/", &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, root); + result = iso_tree_path_to_node(image, "/Dir1", &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node1); + result = iso_tree_path_to_node(image, "/Dir2", &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node2); + result = iso_tree_path_to_node(image, "/Dir1/Dir11", &node); + CU_ASSERT_EQUAL(result, 1); + CU_ASSERT_PTR_EQUAL(node, node11); + + /* some failtures */ + result = iso_tree_path_to_node(image, "/Dir2/Dir11", &node); + CU_ASSERT_EQUAL(result, 0); + CU_ASSERT_PTR_NULL(node); + + iso_image_unref(image); +} + +void add_tree_suite() +{ + CU_pSuite pSuite = CU_add_suite("Iso Tree Suite", NULL, NULL); + + CU_add_test(pSuite, "iso_tree_add_new_dir()", test_iso_tree_add_new_dir); + CU_add_test(pSuite, "iso_tree_add_new_symlink()", test_iso_tree_add_new_symlink); + CU_add_test(pSuite, "iso_tree_add_new_special()", test_iso_tree_add_new_special); + CU_add_test(pSuite, "iso_tree_add_node() [1. dir]", test_iso_tree_add_node_dir); + CU_add_test(pSuite, "iso_tree_add_node() [2. symlink]", test_iso_tree_add_node_link); + CU_add_test(pSuite, "iso_tree_path_to_node()", test_iso_tree_path_to_node); + +} diff --git a/libisofs/branches/thomas/test/test_util.c b/libisofs/branches/thomas/test/test_util.c new file mode 100644 index 00000000..0852e926 --- /dev/null +++ b/libisofs/branches/thomas/test/test_util.c @@ -0,0 +1,1072 @@ +/* + * Unit test for util.h + * + * This test utiliy functions + */ +#include "test.h" +#include "util.h" + +#include +#include +#include + +static void test_int_pow() +{ + CU_ASSERT_EQUAL(int_pow(1, 2), 1); + CU_ASSERT_EQUAL(int_pow(2, 2), 4); + CU_ASSERT_EQUAL(int_pow(0, 2), 0); + CU_ASSERT_EQUAL(int_pow(-1, 2), 1); + CU_ASSERT_EQUAL(int_pow(-1, 3), -1); + CU_ASSERT_EQUAL(int_pow(3, 2), 9); + CU_ASSERT_EQUAL(int_pow(3, 10), 59049); +} + +static void test_strconv() +{ + int ret; + char *out; + + /* Prova de cadeia com codificação ISO-8859-15 */ + unsigned char in1[45] = + {0x50, 0x72, 0x6f, 0x76, 0x61, 0x20, 0x64, 0x65, 0x20, 0x63, 0x61, + 0x64, 0x65, 0x69, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x20, 0x63, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0xe7, 0xe3, 0x6f, 0x20, 0x49, + 0x53, 0x4f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, 0x31, 0x35, 0x0a, + 0x00}; /* encoded in ISO-8859-15 */ + unsigned char out1[47] = + {0x50, 0x72, 0x6f, 0x76, 0x61, 0x20, 0x64, 0x65, 0x20, 0x63, 0x61, + 0x64, 0x65, 0x69, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x20, 0x63, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0xc3, 0xa7, 0xc3, 0xa3, 0x6f, + 0x20, 0x49, 0x53, 0x4f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, 0x31, + 0x35, 0x0a, 0x00}; /* encoded in UTF-8 */ + unsigned char in2[45] = + {0x50, 0x72, 0x6f, 0x76, 0x61, 0x20, 0x64, 0x65, 0x20, 0x63, 0x61, + 0x64, 0x65, 0x69, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x20, 0x63, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0xe7, 0xe3, 0x6f, 0x20, 0x49, + 0x53, 0x4f, 0x2d, 0x38, 0x38, 0xff, 0xff, 0x2d, 0x31, 0x35, 0x0a, + 0x00}; /* incorrect encoding */ + + /* ISO-8859-15 to UTF-8 */ + ret = strconv((char*)in1, "ISO-8859-15", "UTF-8", &out); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_STRING_EQUAL(out, (char*)out1); + free(out); + + /* UTF-8 to ISO-8859-15 */ + ret = strconv((char*)out1, "UTF-8", "ISO-8859-15", &out); + CU_ASSERT_EQUAL(ret, 1); + CU_ASSERT_STRING_EQUAL(out, (char*)in1); + free(out); + + /* try with an incorrect input */ + ret = strconv((char*)in2, "UTF-8", "ISO-8859-15", &out); + CU_ASSERT_EQUAL(ret, ISO_CHARSET_CONV_ERROR); +} + +static void test_div_up() +{ + CU_ASSERT_EQUAL( DIV_UP(1, 2), 1 ); + CU_ASSERT_EQUAL( DIV_UP(2, 2), 1 ); + CU_ASSERT_EQUAL( DIV_UP(0, 2), 0 ); + CU_ASSERT_EQUAL( DIV_UP(-1, 2), 0 ); + CU_ASSERT_EQUAL( DIV_UP(3, 2), 2 ); +} + +static void test_round_up() +{ + CU_ASSERT_EQUAL( ROUND_UP(1, 2), 2 ); + CU_ASSERT_EQUAL( ROUND_UP(2, 2), 2 ); + CU_ASSERT_EQUAL( ROUND_UP(0, 2), 0 ); + CU_ASSERT_EQUAL( ROUND_UP(-1, 2), 0 ); + CU_ASSERT_EQUAL( ROUND_UP(3, 2), 4 ); + CU_ASSERT_EQUAL( ROUND_UP(15, 7), 21 ); + CU_ASSERT_EQUAL( ROUND_UP(13, 7), 14 ); + CU_ASSERT_EQUAL( ROUND_UP(14, 7), 14 ); +} + +static void test_iso_lsb_msb() +{ + uint8_t buf[4]; + uint32_t num; + + num = 0x01020304; + iso_lsb(buf, num, 4); + CU_ASSERT_EQUAL( buf[0], 0x04 ); + CU_ASSERT_EQUAL( buf[1], 0x03 ); + CU_ASSERT_EQUAL( buf[2], 0x02 ); + CU_ASSERT_EQUAL( buf[3], 0x01 ); + + iso_msb(buf, num, 4); + CU_ASSERT_EQUAL( buf[0], 0x01 ); + CU_ASSERT_EQUAL( buf[1], 0x02 ); + CU_ASSERT_EQUAL( buf[2], 0x03 ); + CU_ASSERT_EQUAL( buf[3], 0x04 ); + + iso_lsb(buf, num, 2); + CU_ASSERT_EQUAL( buf[0], 0x04 ); + CU_ASSERT_EQUAL( buf[1], 0x03 ); + + iso_msb(buf, num, 2); + CU_ASSERT_EQUAL( buf[0], 0x03 ); + CU_ASSERT_EQUAL( buf[1], 0x04 ); +} + +static void test_iso_read_lsb_msb() +{ + uint8_t buf[4]; + uint32_t num; + + buf[0] = 0x04; + buf[1] = 0x03; + buf[2] = 0x02; + buf[3] = 0x01; + + num = iso_read_lsb(buf, 4); + CU_ASSERT_EQUAL(num, 0x01020304); + + num = iso_read_msb(buf, 4); + CU_ASSERT_EQUAL(num, 0x04030201); + + num = iso_read_lsb(buf, 2); + CU_ASSERT_EQUAL(num, 0x0304); + + num = iso_read_msb(buf, 2); + CU_ASSERT_EQUAL(num, 0x0403); +} + +static void test_iso_bb() +{ + uint8_t buf[8]; + uint32_t num; + + num = 0x01020304; + iso_bb(buf, num, 4); + CU_ASSERT_EQUAL( buf[0], 0x04 ); + CU_ASSERT_EQUAL( buf[1], 0x03 ); + CU_ASSERT_EQUAL( buf[2], 0x02 ); + CU_ASSERT_EQUAL( buf[3], 0x01 ); + CU_ASSERT_EQUAL( buf[4], 0x01 ); + CU_ASSERT_EQUAL( buf[5], 0x02 ); + CU_ASSERT_EQUAL( buf[6], 0x03 ); + CU_ASSERT_EQUAL( buf[7], 0x04 ); + + iso_bb(buf, num, 2); + CU_ASSERT_EQUAL( buf[0], 0x04 ); + CU_ASSERT_EQUAL( buf[1], 0x03 ); + CU_ASSERT_EQUAL( buf[2], 0x03 ); + CU_ASSERT_EQUAL( buf[3], 0x04 ); +} + +static void test_iso_datetime_7() +{ + uint8_t buf[7]; + time_t t1, t2, tr; + char *tz; + struct tm tp; + + tz = getenv("TZ"); + + setenv("TZ", "", 1); + tzset(); + + strptime("01-03-1976 13:27:45", "%d-%m-%Y %T", &tp); + t1 = mktime(&tp); /* t1 in GMT */ + + strptime("01-07-2007 13:27:45", "%d-%m-%Y %T", &tp); + t2 = mktime(&tp); /* t1 in GMT (summer time) */ + + /* ----------------- European Timezones ----------------------*/ + setenv("TZ", "Europe/Madrid", 1); + tzset(); + + iso_datetime_7(buf, t1, 0); + CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 3); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 14); /* hour (GMT+1) */ + CU_ASSERT_EQUAL(buf[4], 27); /* minute */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], 4); /* GMT+1 hour for CET */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + + iso_datetime_7(buf, t2, 0); + CU_ASSERT_EQUAL(buf[0], 107); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 7); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 15); /* hour (GMT+2, summer time) */ + CU_ASSERT_EQUAL(buf[4], 27); /* minute */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], 8); /* GMT+2 hour for CEST */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Europe/London", 1); + tzset(); + + iso_datetime_7(buf, t1, 0); + CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 3); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 13); /* hour (GMT+0) */ + CU_ASSERT_EQUAL(buf[4], 27); /* minute */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], 0); /* GMT+0 */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + + iso_datetime_7(buf, t2, 0); + CU_ASSERT_EQUAL(buf[0], 107); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 7); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 14); /* hour (GMT+1, summer time) */ + CU_ASSERT_EQUAL(buf[4], 27); /* minute */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], 4); /* GMT+1 */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + /* ----------------- American Timezones ----------------------*/ + setenv("TZ", "America/New_York", 1); + tzset(); + + iso_datetime_7(buf, t1, 0); + CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 3); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 8); /* hour */ + CU_ASSERT_EQUAL(buf[4], 27); /* minute */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], -5*4); /* GMT-5 for EST */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + + /* ----------------- Asia Timezones ----------------------*/ + setenv("TZ", "Asia/Hong_Kong", 1); + tzset(); + + iso_datetime_7(buf, t1, 0); + CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 3); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 21); /* hour */ + CU_ASSERT_EQUAL(buf[4], 27); /* minute */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], 8*4); /* GMT+8 */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + + /* read from another timestamp */ + setenv("TZ", "Europe/Madrid", 1); + tzset(); + + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + + /* ----------------- Africa Timezones ----------------------*/ + + /* Africa country without Daylight saving time */ + setenv("TZ", "Africa/Luanda", 1); + tzset(); + + iso_datetime_7(buf, t1, 0); + CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 3); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 14); /* hour (GMT+1) */ + CU_ASSERT_EQUAL(buf[4], 27); /* minute */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], 4); /* GMT+1 hour */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + + iso_datetime_7(buf, t2, 0); + CU_ASSERT_EQUAL(buf[0], 107); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 7); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 14); /* hour (GMT+1, no summer time) */ + CU_ASSERT_EQUAL(buf[4], 27); /* minute */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], 4); /* GMT+1 hour */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + /* ----------------- Australia Timezones ----------------------*/ + + /* this is GMT+9:30 (note that in South summer is winter in North) */ + setenv("TZ", "Australia/Broken_Hill", 1); + tzset(); + + iso_datetime_7(buf, t1, 0); + CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 3); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 23); /* hour GMT+9+1 (summer time!!) */ + CU_ASSERT_EQUAL(buf[4], 57); /* minute + 30 */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], 42); /* GMT+9:30 hour + 1 (summer time) */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + + iso_datetime_7(buf, t2, 0); + CU_ASSERT_EQUAL(buf[0], 107); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 7); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 22); /* hour (GMT+9) */ + CU_ASSERT_EQUAL(buf[4], 57); /* minute +30 */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], 38); /* GMT+9:30 */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + /* ----------------- Pacific Timezones ----------------------*/ + + /* this is GMT+13, the max supported */ + setenv("TZ", "Pacific/Tongatapu", 1); + tzset(); + + iso_datetime_7(buf, t1, 0); + CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 3); /* month */ + CU_ASSERT_EQUAL(buf[2], 2); /* day */ + CU_ASSERT_EQUAL(buf[3], 2); /* hour (GMT+13) */ + CU_ASSERT_EQUAL(buf[4], 27); /* minute */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], 52); /* GMT+13 hour */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + + /* this is GMT-11, I can't found a -12 timezone */ + setenv("TZ", "Pacific/Pago_Pago", 1); + tzset(); + + iso_datetime_7(buf, t1, 0); + CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ + CU_ASSERT_EQUAL(buf[1], 3); /* month */ + CU_ASSERT_EQUAL(buf[2], 1); /* day */ + CU_ASSERT_EQUAL(buf[3], 2); /* hour (GMT-11) */ + CU_ASSERT_EQUAL(buf[4], 27); /* minute */ + CU_ASSERT_EQUAL(buf[5], 45); /* second */ + CU_ASSERT_EQUAL((int8_t)buf[6], -44); /* GMT-11 hour */ + + /* check that reading returns the same time */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + + /* --- and now test from several zones, just for write/read compatibilty */ + setenv("TZ", "Pacific/Kiritimati", 1); + tzset(); + iso_datetime_7(buf, t1, 1); /* this needs GMT */ + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "America/Argentina/La_Rioja", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "America/Argentina/La_Rioja", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "America/Caracas", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Asia/Bangkok", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Asia/Tehran", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Pacific/Pitcairn", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Antarctica/McMurdo", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "EET", 1); /* Eastern European Time */ + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Europe/Moscow", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Asia/Novosibirsk", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Asia/Vladivostok", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Asia/Anadyr", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Atlantic/Canary", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "Indian/Mauritius", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + setenv("TZ", "America/Los_Angeles", 1); + tzset(); + iso_datetime_7(buf, t1, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t1); + iso_datetime_7(buf, t2, 0); + tr = iso_datetime_read_7(buf); + CU_ASSERT_EQUAL(tr, t2); + + if (tz) + setenv("TZ", tz, 1); + else + unsetenv("TZ"); + tzset(); +} + +static void test_iso_1_dirid() +{ + char *dir; + dir = iso_1_dirid("dir1"); + CU_ASSERT_STRING_EQUAL(dir, "DIR1"); + free(dir); + dir = iso_1_dirid("dIR1"); + CU_ASSERT_STRING_EQUAL(dir, "DIR1"); + free(dir); + dir = iso_1_dirid("DIR1"); + CU_ASSERT_STRING_EQUAL(dir, "DIR1"); + free(dir); + dir = iso_1_dirid("dirwithbigname"); + CU_ASSERT_STRING_EQUAL(dir, "DIRWITHB"); + free(dir); + dir = iso_1_dirid("dirwith8"); + CU_ASSERT_STRING_EQUAL(dir, "DIRWITH8"); + free(dir); + dir = iso_1_dirid("dir.1"); + CU_ASSERT_STRING_EQUAL(dir, "DIR_1"); + free(dir); + dir = iso_1_dirid("4f<0KmM::xcvf"); + CU_ASSERT_STRING_EQUAL(dir, "4F_0KMM_"); + free(dir); +} + +static void test_iso_2_dirid() +{ + char *dir; + dir = iso_2_dirid("dir1"); + CU_ASSERT_STRING_EQUAL(dir, "DIR1"); + free(dir); + dir = iso_2_dirid("dIR1"); + CU_ASSERT_STRING_EQUAL(dir, "DIR1"); + free(dir); + dir = iso_2_dirid("DIR1"); + CU_ASSERT_STRING_EQUAL(dir, "DIR1"); + free(dir); + dir = iso_2_dirid("dirwithbigname"); + CU_ASSERT_STRING_EQUAL(dir, "DIRWITHBIGNAME"); + free(dir); + dir = iso_2_dirid("dirwith8"); + CU_ASSERT_STRING_EQUAL(dir, "DIRWITH8"); + free(dir); + dir = iso_2_dirid("dir.1"); + CU_ASSERT_STRING_EQUAL(dir, "DIR_1"); + free(dir); + dir = iso_2_dirid("4f<0KmM::xcvf"); + CU_ASSERT_STRING_EQUAL(dir, "4F_0KMM__XCVF"); + free(dir); + dir = iso_2_dirid("directory with 31 characters ok"); + CU_ASSERT_STRING_EQUAL(dir, "DIRECTORY_WITH_31_CHARACTERS_OK"); + free(dir); + dir = iso_2_dirid("directory with more than 31 characters"); + CU_ASSERT_STRING_EQUAL(dir, "DIRECTORY_WITH_MORE_THAN_31_CHA"); + free(dir); +} + +static void test_iso_1_fileid() +{ + char *file; + file = iso_1_fileid("file1"); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + file = iso_1_fileid("fILe1"); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + file = iso_1_fileid("FILE1"); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + file = iso_1_fileid(".EXT"); + CU_ASSERT_STRING_EQUAL(file, ".EXT"); + free(file); + file = iso_1_fileid("file.ext"); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_1_fileid("fiLE.ext"); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_1_fileid("file.EXt"); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_1_fileid("FILE.EXT"); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_1_fileid("bigfilename"); + CU_ASSERT_STRING_EQUAL(file, "BIGFILEN."); + free(file); + file = iso_1_fileid("bigfilename.ext"); + CU_ASSERT_STRING_EQUAL(file, "BIGFILEN.EXT"); + free(file); + file = iso_1_fileid("bigfilename.e"); + CU_ASSERT_STRING_EQUAL(file, "BIGFILEN.E"); + free(file); + file = iso_1_fileid("file.bigext"); + CU_ASSERT_STRING_EQUAL(file, "FILE.BIG"); + free(file); + file = iso_1_fileid(".bigext"); + CU_ASSERT_STRING_EQUAL(file, ".BIG"); + free(file); + file = iso_1_fileid("bigfilename.bigext"); + CU_ASSERT_STRING_EQUAL(file, "BIGFILEN.BIG"); + free(file); + file = iso_1_fileid("file<:a.ext"); + CU_ASSERT_STRING_EQUAL(file, "FILE__A.EXT"); + free(file); + file = iso_1_fileid("file.<:a"); + CU_ASSERT_STRING_EQUAL(file, "FILE.__A"); + free(file); + file = iso_1_fileid("file<:a.--a"); + CU_ASSERT_STRING_EQUAL(file, "FILE__A.__A"); + free(file); + file = iso_1_fileid("file.ex1.ex2"); + CU_ASSERT_STRING_EQUAL(file, "FILE_EX1.EX2"); + free(file); + file = iso_1_fileid("file.ex1.ex2.ex3"); + CU_ASSERT_STRING_EQUAL(file, "FILE_EX1.EX3"); + free(file); + file = iso_1_fileid("fil.ex1.ex2.ex3"); + CU_ASSERT_STRING_EQUAL(file, "FIL_EX1_.EX3"); + free(file); +} + +static void test_iso_2_fileid() +{ + char *file; + file = iso_2_fileid("file1"); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + file = iso_2_fileid("fILe1"); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + file = iso_2_fileid("FILE1"); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + file = iso_2_fileid(".EXT"); + CU_ASSERT_STRING_EQUAL(file, ".EXT"); + free(file); + file = iso_2_fileid("file.ext"); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_2_fileid("fiLE.ext"); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_2_fileid("file.EXt"); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_2_fileid("FILE.EXT"); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_2_fileid("bigfilename"); + CU_ASSERT_STRING_EQUAL(file, "BIGFILENAME."); + free(file); + file = iso_2_fileid("bigfilename.ext"); + CU_ASSERT_STRING_EQUAL(file, "BIGFILENAME.EXT"); + free(file); + file = iso_2_fileid("bigfilename.e"); + CU_ASSERT_STRING_EQUAL(file, "BIGFILENAME.E"); + free(file); + file = iso_2_fileid("31 characters filename.extensio"); + CU_ASSERT_STRING_EQUAL(file, "31_CHARACTERS_FILENAME.EXTENSIO"); + free(file); + file = iso_2_fileid("32 characters filename.extension"); + CU_ASSERT_STRING_EQUAL(file, "32_CHARACTERS_FILENAME.EXTENSIO"); + free(file); + file = iso_2_fileid("more than 30 characters filename.extension"); + CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FIL.EXT"); + free(file); + file = iso_2_fileid("file.bigext"); + CU_ASSERT_STRING_EQUAL(file, "FILE.BIGEXT"); + free(file); + file = iso_2_fileid(".bigext"); + CU_ASSERT_STRING_EQUAL(file, ".BIGEXT"); + free(file); + file = iso_2_fileid("bigfilename.bigext"); + CU_ASSERT_STRING_EQUAL(file, "BIGFILENAME.BIGEXT"); + free(file); + file = iso_2_fileid("file<:a.ext"); + CU_ASSERT_STRING_EQUAL(file, "FILE__A.EXT"); + free(file); + file = iso_2_fileid("file.<:a"); + CU_ASSERT_STRING_EQUAL(file, "FILE.__A"); + free(file); + file = iso_2_fileid("file<:a.--a"); + CU_ASSERT_STRING_EQUAL(file, "FILE__A.__A"); + free(file); + file = iso_2_fileid("file.ex1.ex2"); + CU_ASSERT_STRING_EQUAL(file, "FILE_EX1.EX2"); + free(file); + file = iso_2_fileid("file.ex1.ex2.ex3"); + CU_ASSERT_STRING_EQUAL(file, "FILE_EX1_EX2.EX3"); + free(file); + file = iso_2_fileid("fil.ex1.ex2.ex3"); + CU_ASSERT_STRING_EQUAL(file, "FIL_EX1_EX2.EX3"); + free(file); + file = iso_2_fileid(".file.bigext"); + CU_ASSERT_STRING_EQUAL(file, "_FILE.BIGEXT"); + free(file); +} + +static void test_iso_r_dirid() +{ + char *dir; + + dir = iso_r_dirid("dir1", 31, 0); + CU_ASSERT_STRING_EQUAL(dir, "DIR1"); + free(dir); + + dir = iso_r_dirid("dIR1", 31, 0); + CU_ASSERT_STRING_EQUAL(dir, "DIR1"); + free(dir); + + /* allow lowercase */ + dir = iso_r_dirid("dIR1", 31, 1); + CU_ASSERT_STRING_EQUAL(dir, "dIR1"); + free(dir); + dir = iso_r_dirid("dIR1", 31, 2); + CU_ASSERT_STRING_EQUAL(dir, "dIR1"); + free(dir); + + dir = iso_r_dirid("DIR1", 31, 0); + CU_ASSERT_STRING_EQUAL(dir, "DIR1"); + free(dir); + dir = iso_r_dirid("dirwithbigname", 31, 0); + CU_ASSERT_STRING_EQUAL(dir, "DIRWITHBIGNAME"); + free(dir); + dir = iso_r_dirid("dirwith8", 31, 0); + CU_ASSERT_STRING_EQUAL(dir, "DIRWITH8"); + free(dir); + + /* dot is not allowed */ + dir = iso_r_dirid("dir.1", 31, 0); + CU_ASSERT_STRING_EQUAL(dir, "DIR_1"); + free(dir); + dir = iso_r_dirid("dir.1", 31, 1); + CU_ASSERT_STRING_EQUAL(dir, "dir_1"); + free(dir); + dir = iso_r_dirid("dir.1", 31, 2); + CU_ASSERT_STRING_EQUAL(dir, "dir.1"); + free(dir); + + dir = iso_r_dirid("4f<0KmM::xcvf", 31, 0); + CU_ASSERT_STRING_EQUAL(dir, "4F_0KMM__XCVF"); + free(dir); + dir = iso_r_dirid("4f<0KmM::xcvf", 31, 1); + CU_ASSERT_STRING_EQUAL(dir, "4f_0KmM__xcvf"); + free(dir); + dir = iso_r_dirid("4f<0KmM::xcvf", 31, 2); + CU_ASSERT_STRING_EQUAL(dir, "4f<0KmM::xcvf"); + free(dir); + + dir = iso_r_dirid("directory with 31 characters ok", 31, 0); + CU_ASSERT_STRING_EQUAL(dir, "DIRECTORY_WITH_31_CHARACTERS_OK"); + free(dir); + dir = iso_r_dirid("directory with more than 31 characters", 31, 0); + CU_ASSERT_STRING_EQUAL(dir, "DIRECTORY_WITH_MORE_THAN_31_CHA"); + free(dir); + dir = iso_r_dirid("directory with more than 31 characters", 35, 0); + CU_ASSERT_STRING_EQUAL(dir, "DIRECTORY_WITH_MORE_THAN_31_CHARACT"); + free(dir); +} + +static void test_iso_r_fileid() +{ + char *file; + + /* force dot */ + file = iso_r_fileid("file1", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + + /* and not */ + file = iso_r_fileid("file1", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE1"); + free(file); + + /* allow lowercase */ + file = iso_r_fileid("file1", 30, 1, 0); + CU_ASSERT_STRING_EQUAL(file, "file1"); + free(file); + file = iso_r_fileid("file1", 30, 2, 0); + CU_ASSERT_STRING_EQUAL(file, "file1"); + free(file); + + /* force d-char and dot */ + file = iso_r_fileid("fILe1", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + /* force d-char but not dot */ + file = iso_r_fileid("fILe1", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE1"); + free(file); + /* allow lower case but force dot */ + file = iso_r_fileid("fILe1", 30, 1, 1); + CU_ASSERT_STRING_EQUAL(file, "fILe1."); + free(file); + + file = iso_r_fileid("FILE1", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + file = iso_r_fileid(".EXT", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, ".EXT"); + free(file); + file = iso_r_fileid(".EXT", 30, 1, 0); + CU_ASSERT_STRING_EQUAL(file, ".EXT"); + free(file); + + file = iso_r_fileid("file.ext", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + + /* not force dot is the same in this case */ + file = iso_r_fileid("fiLE.ext", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_r_fileid("fiLE.ext", 30, 2, 0); + CU_ASSERT_STRING_EQUAL(file, "fiLE.ext"); + free(file); + + file = iso_r_fileid("file.EXt", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_r_fileid("FILE.EXT", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + + file = iso_r_fileid("31 characters filename.extensio", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "31_CHARACTERS_FILENAME.EXTENSIO"); + free(file); + file = iso_r_fileid("32 characters filename.extension", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "32_CHARACTERS_FILENAME.EXTENSIO"); + free(file); + + /* allow lowercase */ + file = iso_r_fileid("31 characters filename.extensio", 30, 1, 1); + CU_ASSERT_STRING_EQUAL(file, "31_characters_filename.extensio"); + free(file); + + /* and all characters */ + file = iso_r_fileid("31 characters filename.extensio", 30, 2, 1); + CU_ASSERT_STRING_EQUAL(file, "31 characters filename.extensio"); + free(file); + + file = iso_r_fileid("more than 30 characters filename.extension", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FIL.EXT"); + free(file); + + /* incrementing the size... */ + file = iso_r_fileid("more than 30 characters filename.extension", 35, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FILENAME.EXT"); + free(file); + + file = iso_r_fileid("more than 30 characters filename.extension", 36, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FILENAME.EXTE"); + free(file); + + file = iso_r_fileid("file.bigext", 30, 1, 0); + CU_ASSERT_STRING_EQUAL(file, "file.bigext"); + free(file); + + file = iso_r_fileid(".bigext", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, ".BIGEXT"); + free(file); + + /* "strange" characters */ + file = iso_r_fileid("file<:a.ext", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE__A.EXT"); + free(file); + file = iso_r_fileid("file<:a.ext", 30, 1, 0); + CU_ASSERT_STRING_EQUAL(file, "file__a.ext"); + free(file); + file = iso_r_fileid("file<:a.ext", 30, 2, 0); + CU_ASSERT_STRING_EQUAL(file, "file<:a.ext"); + free(file); + + /* multiple dots */ + file = iso_r_fileid("fi.le.a.ext", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FI_LE_A.EXT"); + free(file); + file = iso_r_fileid("fi.le.a.ext", 30, 1, 0); + CU_ASSERT_STRING_EQUAL(file, "fi_le_a.ext"); + free(file); + file = iso_r_fileid("fi.le.a.ext", 30, 2, 0); + CU_ASSERT_STRING_EQUAL(file, "fi.le.a.ext"); + free(file); + + file = iso_r_fileid("file.<:a", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE.__A"); + free(file); + file = iso_r_fileid("file<:a.--a", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE__A.__A"); + free(file); + + file = iso_r_fileid(".file.bigext", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "_FILE.BIGEXT"); + free(file); + + file = iso_r_fileid(".file.bigext", 30, 2, 0); + CU_ASSERT_STRING_EQUAL(file, ".file.bigext"); + free(file); +} + +static void test_iso_rbtree_insert() +{ + int res; + IsoRBTree *tree; + char *str1, *str2, *str3, *str4, *str5; + void *str; + + res = iso_rbtree_new((compare_function_t)strcmp, &tree); + CU_ASSERT_EQUAL(res, 1); + + /* ok, insert one str */ + str1 = "first str"; + res = iso_rbtree_insert(tree, str1, &str); + CU_ASSERT_EQUAL(res, 1); + CU_ASSERT_PTR_EQUAL(str, str1); + + str2 = "second str"; + res = iso_rbtree_insert(tree, str2, &str); + CU_ASSERT_EQUAL(res, 1); + CU_ASSERT_PTR_EQUAL(str, str2); + + /* an already inserted string */ + str3 = "second str"; + res = iso_rbtree_insert(tree, str3, &str); + CU_ASSERT_EQUAL(res, 0); + CU_ASSERT_PTR_EQUAL(str, str2); + + /* an already inserted string */ + str3 = "first str"; + res = iso_rbtree_insert(tree, str3, &str); + CU_ASSERT_EQUAL(res, 0); + CU_ASSERT_PTR_EQUAL(str, str1); + + str4 = "a string to be inserted first"; + res = iso_rbtree_insert(tree, str4, &str); + CU_ASSERT_EQUAL(res, 1); + CU_ASSERT_PTR_EQUAL(str, str4); + + str5 = "this to be inserted last"; + res = iso_rbtree_insert(tree, str5, &str); + CU_ASSERT_EQUAL(res, 1); + CU_ASSERT_PTR_EQUAL(str, str5); + + /* + * TODO write a really good test to check all possible estrange + * behaviors of a red-black tree + */ + + iso_rbtree_destroy(tree, NULL); +} + +void test_iso_htable_put_get() +{ + int res; + IsoHTable *table; + char *str1, *str2, *str3, *str4, *str5; + void *str; + + res = iso_htable_create(4, iso_str_hash, (compare_function_t)strcmp, &table); + CU_ASSERT_EQUAL(res, 1); + + /* try to get str from empty table */ + res = iso_htable_get(table, "first str", &str); + CU_ASSERT_EQUAL(res, 0); + + /* ok, insert one str */ + str1 = "first str"; + res = iso_htable_put(table, str1, str1); + CU_ASSERT_EQUAL(res, 1); + + /* and now get str from table */ + res = iso_htable_get(table, "first str", &str); + CU_ASSERT_EQUAL(res, 1); + CU_ASSERT_PTR_EQUAL(str, str1); + res = iso_htable_get(table, "second str", &str); + CU_ASSERT_EQUAL(res, 0); + + str2 = "second str"; + res = iso_htable_put(table, str2, str2); + CU_ASSERT_EQUAL(res, 1); + + str = NULL; + res = iso_htable_get(table, "first str", &str); + CU_ASSERT_EQUAL(res, 1); + CU_ASSERT_PTR_EQUAL(str, str1); + res = iso_htable_get(table, "second str", &str); + CU_ASSERT_EQUAL(res, 1); + CU_ASSERT_PTR_EQUAL(str, str2); + + /* insert again, with same key but other data */ + res = iso_htable_put(table, str2, str1); + CU_ASSERT_EQUAL(res, 0); + + res = iso_htable_get(table, "second str", &str); + CU_ASSERT_EQUAL(res, 1); + CU_ASSERT_PTR_EQUAL(str, str2); + + str3 = "third str"; + res = iso_htable_put(table, str3, str3); + CU_ASSERT_EQUAL(res, 1); + + str4 = "four str"; + res = iso_htable_put(table, str4, str4); + CU_ASSERT_EQUAL(res, 1); + + str5 = "fifth str"; + res = iso_htable_put(table, str5, str5); + CU_ASSERT_EQUAL(res, 1); + + /* some searches */ + res = iso_htable_get(table, "sixth str", &str); + CU_ASSERT_EQUAL(res, 0); + + res = iso_htable_get(table, "fifth str", &str); + CU_ASSERT_EQUAL(res, 1); + CU_ASSERT_PTR_EQUAL(str, str5); + + iso_htable_destroy(table, NULL); +} + +void add_util_suite() +{ + CU_pSuite pSuite = CU_add_suite("UtilSuite", NULL, NULL); + + CU_add_test(pSuite, "strconv()", test_strconv); + CU_add_test(pSuite, "int_pow()", test_int_pow); + CU_add_test(pSuite, "DIV_UP()", test_div_up); + CU_add_test(pSuite, "ROUND_UP()", test_round_up); + CU_add_test(pSuite, "iso_bb()", test_iso_bb); + CU_add_test(pSuite, "iso_lsb/msb()", test_iso_lsb_msb); + CU_add_test(pSuite, "iso_read_lsb/msb()", test_iso_read_lsb_msb); + CU_add_test(pSuite, "iso_datetime_7()", test_iso_datetime_7); + CU_add_test(pSuite, "iso_1_dirid()", test_iso_1_dirid); + CU_add_test(pSuite, "iso_2_dirid()", test_iso_2_dirid); + CU_add_test(pSuite, "iso_1_fileid()", test_iso_1_fileid); + CU_add_test(pSuite, "iso_2_fileid()", test_iso_2_fileid); + CU_add_test(pSuite, "iso_r_dirid()", test_iso_r_dirid); + CU_add_test(pSuite, "iso_r_fileid()", test_iso_r_fileid); + CU_add_test(pSuite, "iso_rbtree_insert()", test_iso_rbtree_insert); + CU_add_test(pSuite, "iso_htable_put/get()", test_iso_htable_put_get); +} diff --git a/libisofs/branches/thomas/version.h.in b/libisofs/branches/thomas/version.h.in new file mode 100644 index 00000000..f903ff0a --- /dev/null +++ b/libisofs/branches/thomas/version.h.in @@ -0,0 +1,3 @@ +#define LIBISOFS_MAJOR_VERSION @LIBISOFS_MAJOR_VERSION@ +#define LIBISOFS_MINOR_VERSION @LIBISOFS_MINOR_VERSION@ +#define LIBISOFS_MICRO_VERSION @LIBISOFS_MICRO_VERSION@