From 165f73dd30025f57899627121e4bc23a4c89623c Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Tue, 31 Jul 2007 07:32:26 +0000 Subject: [PATCH] Improved docs, and created 0.3.8 branch --- libisofs/branches/ZeroTwoEight/AUTHORS | 4 + libisofs/branches/ZeroTwoEight/CONTRIBUTORS | 4 + libisofs/branches/ZeroTwoEight/COPYING | 280 ++++++ libisofs/branches/ZeroTwoEight/COPYRIGHT | 19 + libisofs/branches/ZeroTwoEight/ChangeLog | 1 + libisofs/branches/ZeroTwoEight/INSTALL | 234 +++++ libisofs/branches/ZeroTwoEight/Makefile.am | 134 +++ libisofs/branches/ZeroTwoEight/NEWS | 1 + libisofs/branches/ZeroTwoEight/README | 231 +++++ libisofs/branches/ZeroTwoEight/TODO | 29 + libisofs/branches/ZeroTwoEight/acinclude.m4 | 22 + .../ZeroTwoEight/bindings/python/README | 17 + .../bindings/python/isofs/__init__.py | 2 + .../bindings/python/isofs/core.py | 185 ++++ .../bindings/python/isofs/defines.py | 9 + .../bindings/python/isofs/isofs.py | 58 ++ libisofs/branches/ZeroTwoEight/bootstrap | 10 + libisofs/branches/ZeroTwoEight/configure.ac | 117 +++ libisofs/branches/ZeroTwoEight/doc/Makefile | 4 + libisofs/branches/ZeroTwoEight/doc/comments | 123 +++ .../ZeroTwoEight/doc/comments_test_ts | 121 +++ .../branches/ZeroTwoEight/doc/doxygen.conf.in | 186 ++++ .../branches/ZeroTwoEight/libisofs-1.pc.in | 11 + .../branches/ZeroTwoEight/libisofs/Makefile | 4 + .../branches/ZeroTwoEight/libisofs/ecma119.c | 840 +++++++++++++++++ .../branches/ZeroTwoEight/libisofs/ecma119.h | 306 +++++++ .../ZeroTwoEight/libisofs/ecma119_tree.c | 623 +++++++++++++ .../ZeroTwoEight/libisofs/ecma119_tree.h | 98 ++ .../branches/ZeroTwoEight/libisofs/eltorito.c | 454 ++++++++++ .../branches/ZeroTwoEight/libisofs/eltorito.h | 60 ++ .../branches/ZeroTwoEight/libisofs/exclude.c | 38 + .../branches/ZeroTwoEight/libisofs/exclude.h | 37 + .../branches/ZeroTwoEight/libisofs/file.c | 188 ++++ .../branches/ZeroTwoEight/libisofs/file.h | 69 ++ .../branches/ZeroTwoEight/libisofs/hash.c | 158 ++++ .../branches/ZeroTwoEight/libisofs/hash.h | 46 + .../branches/ZeroTwoEight/libisofs/joliet.c | 464 ++++++++++ .../branches/ZeroTwoEight/libisofs/joliet.h | 89 ++ .../branches/ZeroTwoEight/libisofs/libisofs.h | 544 +++++++++++ .../ZeroTwoEight/libisofs/rockridge.c | 313 +++++++ .../ZeroTwoEight/libisofs/rockridge.h | 112 +++ .../branches/ZeroTwoEight/libisofs/susp.c | 256 ++++++ .../branches/ZeroTwoEight/libisofs/susp.h | 76 ++ .../branches/ZeroTwoEight/libisofs/tree.c | 401 +++++++++ .../branches/ZeroTwoEight/libisofs/tree.h | 177 ++++ .../branches/ZeroTwoEight/libisofs/util.c | 842 ++++++++++++++++++ .../branches/ZeroTwoEight/libisofs/util.h | 136 +++ .../branches/ZeroTwoEight/libisofs/volume.c | 258 ++++++ .../branches/ZeroTwoEight/libisofs/volume.h | 53 ++ libisofs/branches/ZeroTwoEight/test.sh | 33 + libisofs/branches/ZeroTwoEight/test/Makefile | 4 + libisofs/branches/ZeroTwoEight/test/iso.c | 141 +++ libisofs/branches/ZeroTwoEight/test/iso.py | 297 ++++++ libisofs/branches/ZeroTwoEight/test/test.c | 28 + libisofs/branches/ZeroTwoEight/test/test.h | 24 + .../ZeroTwoEight/test/test_ecma119_tree.c | 34 + .../branches/ZeroTwoEight/test/test_exclude.c | 33 + .../ZeroTwoEight/test/test_file_hashtable.c | 248 ++++++ .../branches/ZeroTwoEight/test/test_tree.c | 395 ++++++++ .../branches/ZeroTwoEight/test/test_util.c | 265 ++++++ .../branches/ZeroTwoEight/test/test_volume.c | 238 +++++ libisofs/branches/ZeroTwoEight/test/tree.py | 77 ++ libisofs/branches/ZeroTwoEight/version.h.in | 3 + libisofs/trunk/libisofs/ecma119_tree.c | 82 ++ libisofs/trunk/libisofs/rockridge.c | 24 + libisofs/trunk/libisofs/rockridge.h | 96 +- libisofs/trunk/libisofs/susp.c | 24 - libisofs/trunk/libisofs/susp.h | 24 +- 68 files changed, 10480 insertions(+), 34 deletions(-) create mode 100644 libisofs/branches/ZeroTwoEight/AUTHORS create mode 100644 libisofs/branches/ZeroTwoEight/CONTRIBUTORS create mode 100644 libisofs/branches/ZeroTwoEight/COPYING create mode 100644 libisofs/branches/ZeroTwoEight/COPYRIGHT create mode 100644 libisofs/branches/ZeroTwoEight/ChangeLog create mode 100644 libisofs/branches/ZeroTwoEight/INSTALL create mode 100644 libisofs/branches/ZeroTwoEight/Makefile.am create mode 100644 libisofs/branches/ZeroTwoEight/NEWS create mode 100644 libisofs/branches/ZeroTwoEight/README create mode 100644 libisofs/branches/ZeroTwoEight/TODO create mode 100644 libisofs/branches/ZeroTwoEight/acinclude.m4 create mode 100644 libisofs/branches/ZeroTwoEight/bindings/python/README create mode 100644 libisofs/branches/ZeroTwoEight/bindings/python/isofs/__init__.py create mode 100644 libisofs/branches/ZeroTwoEight/bindings/python/isofs/core.py create mode 100644 libisofs/branches/ZeroTwoEight/bindings/python/isofs/defines.py create mode 100644 libisofs/branches/ZeroTwoEight/bindings/python/isofs/isofs.py create mode 100755 libisofs/branches/ZeroTwoEight/bootstrap create mode 100644 libisofs/branches/ZeroTwoEight/configure.ac create mode 100644 libisofs/branches/ZeroTwoEight/doc/Makefile create mode 100644 libisofs/branches/ZeroTwoEight/doc/comments create mode 100644 libisofs/branches/ZeroTwoEight/doc/comments_test_ts create mode 100644 libisofs/branches/ZeroTwoEight/doc/doxygen.conf.in create mode 100644 libisofs/branches/ZeroTwoEight/libisofs-1.pc.in create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/Makefile create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/ecma119.c create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/ecma119.h create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/ecma119_tree.c create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/ecma119_tree.h create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/eltorito.c create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/eltorito.h create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/exclude.c create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/exclude.h create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/file.c create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/file.h create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/hash.c create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/hash.h create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/joliet.c create mode 100644 libisofs/branches/ZeroTwoEight/libisofs/joliet.h create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/libisofs.h create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/rockridge.c create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/rockridge.h create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/susp.c create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/susp.h create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/tree.c create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/tree.h create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/util.c create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/util.h create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/volume.c create mode 100755 libisofs/branches/ZeroTwoEight/libisofs/volume.h create mode 100644 libisofs/branches/ZeroTwoEight/test.sh create mode 100644 libisofs/branches/ZeroTwoEight/test/Makefile create mode 100644 libisofs/branches/ZeroTwoEight/test/iso.c create mode 100644 libisofs/branches/ZeroTwoEight/test/iso.py create mode 100644 libisofs/branches/ZeroTwoEight/test/test.c create mode 100644 libisofs/branches/ZeroTwoEight/test/test.h create mode 100644 libisofs/branches/ZeroTwoEight/test/test_ecma119_tree.c create mode 100644 libisofs/branches/ZeroTwoEight/test/test_exclude.c create mode 100644 libisofs/branches/ZeroTwoEight/test/test_file_hashtable.c create mode 100644 libisofs/branches/ZeroTwoEight/test/test_tree.c create mode 100644 libisofs/branches/ZeroTwoEight/test/test_util.c create mode 100644 libisofs/branches/ZeroTwoEight/test/test_volume.c create mode 100644 libisofs/branches/ZeroTwoEight/test/tree.py create mode 100644 libisofs/branches/ZeroTwoEight/version.h.in diff --git a/libisofs/branches/ZeroTwoEight/AUTHORS b/libisofs/branches/ZeroTwoEight/AUTHORS new file mode 100644 index 00000000..0f8d838f --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/AUTHORS @@ -0,0 +1,4 @@ +Developers: + +Mario Danic + diff --git a/libisofs/branches/ZeroTwoEight/CONTRIBUTORS b/libisofs/branches/ZeroTwoEight/CONTRIBUTORS new file mode 100644 index 00000000..79706621 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/CONTRIBUTORS @@ -0,0 +1,4 @@ +Joe Neeman +Philippe Rouquier +Suriyan Laohaprapanon +Vreixo Formoso Lopes diff --git a/libisofs/branches/ZeroTwoEight/COPYING b/libisofs/branches/ZeroTwoEight/COPYING new file mode 100644 index 00000000..5a965fbc --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/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/ZeroTwoEight/COPYRIGHT b/libisofs/branches/ZeroTwoEight/COPYRIGHT new file mode 100644 index 00000000..a6b57b3c --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/COPYRIGHT @@ -0,0 +1,19 @@ +Derek Foreman and Ben Jansens +Copyright (C) 2002-2006 Derek Foreman and Ben Jansens +Mario Danic , Thomas Schmitt +Copyright (C) 2006 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/ZeroTwoEight/ChangeLog b/libisofs/branches/ZeroTwoEight/ChangeLog new file mode 100644 index 00000000..7001d0f2 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/ChangeLog @@ -0,0 +1 @@ +nothing here now diff --git a/libisofs/branches/ZeroTwoEight/INSTALL b/libisofs/branches/ZeroTwoEight/INSTALL new file mode 100644 index 00000000..5458714e --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/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/ZeroTwoEight/Makefile.am b/libisofs/branches/ZeroTwoEight/Makefile.am new file mode 100644 index 00000000..0fdc2cbf --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/Makefile.am @@ -0,0 +1,134 @@ +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/tree.h \ + libisofs/tree.c \ + libisofs/volume.h \ + libisofs/volume.c \ + libisofs/util.h \ + libisofs/util.c \ + libisofs/ecma119.c \ + libisofs/ecma119.h \ + libisofs/ecma119_tree.c \ + libisofs/ecma119_tree.h \ + libisofs/susp.h \ + libisofs/susp.c \ + libisofs/rockridge.h \ + libisofs/rockridge.c \ + libisofs/joliet.c \ + libisofs/joliet.h \ + libisofs/exclude.c \ + libisofs/exclude.h \ + libisofs/hash.h \ + libisofs/hash.c \ + libisofs/file.h \ + libisofs/file.c \ + libisofs/eltorito.h \ + libisofs/eltorito.c + +libinclude_HEADERS = \ + libisofs/libisofs.h + +## ========================================================================= ## + +## Build test applications +noinst_PROGRAMS = \ + test/iso + +test_iso_CPPFLAGS = -Ilibisofs +test_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +test_iso_SOURCES = test/iso.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_exclude.c \ + test/test_tree.c \ + test/test_ecma119_tree.c \ + test/test_file_hashtable.c \ + test/test_util.c \ + test/test_volume.c \ + test/test.c + +## ========================================================================= ## + +## Build documentation (You need Doxygen for this to work) +webhost = http://libburn-api.pykix.org +webpath = / +docdir = $(DESTDIR)$(prefix)/share/doc/$(PACKAGE)-$(VERSION) + +doc: doc/html + +doc/html: doc/doxygen.conf + if [ -f ./doc/doc.lock ]; then \ + $(RM) -r doc/html; \ + doxygen doc/doxygen.conf; \ + fi + +doc-upload: doc/html + scp -r $ and Thomas Schmitt +Copyright (C) 2006 Mario Danic, Thomas Schmitt + +Still containing parts of +Libburn. By Derek Foreman and + Ben Jansens +Copyright (C) 2002-2006 Derek Foreman and Ben Jansens +These parts are to be replaced by own code of above libburnia.pykix.org +copyright holders and then libburnia.pykix.org is to be their sole copyright. +This is done to achieve the right to issue the clarification and the +commitment as written at the end of this text. +The rights and merits of the Libburn-copyright holders Derek Foreman and +Ben Jansens will be duely respected. + +This libburnia.pykix.org toplevel README (C) 2006 Thomas Schmitt +------------------------------------------------------------------------------ + + Build and Installation + +Our build system is based on autotools. For preparing the build of a SVN +snapshot you will need autotools of at least version 1.7. +Check out from SVN by + svn co http://libburnia-svn.pykix.org/libburn/trunk libburn_pykix +go into directory libburn_pykix and apply autotools by + ./bootstrap + +Alternatively you may unpack a release tarball for which you do not need +autotools installed. + +To build a libburnia.pykix.org subproject it should be sufficient to go +into its toplevel directory (here: "libburn_pykix") and execute + ./configure + make + +To make the libraries accessible for running resp. developing applications + make install + + +The other half of the project, libisofs, is hosted in the libburnia SVN, too: + svn co http://libburnia-svn.pykix.org/libisofs/trunk libisofs_pykix +See README file there. + + +------------------------------------------------------------------------------ + + + Overview of libburnia.pykix.org + +libburnia.pykix.org is an open-source software project for reading, mastering +and writing optical discs. For now this means only CD-R and CD-RW. + +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 do have a workable code base for burning data CDs, though. The burn API is +quite comprehensively documented and can be used to build a presentable +application. +We do 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. + +- libisofs is the library to pack up hard disk files and directories into a + ISO 9660 disk image. This may then be brought to CD 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. + 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 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 Juli 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 . + + +------------------------------------------------------------------------------ + + 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 + +------------------------------------------------------------------------------ +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/ZeroTwoEight/TODO b/libisofs/branches/ZeroTwoEight/TODO new file mode 100644 index 00000000..6b002991 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/TODO @@ -0,0 +1,29 @@ +FEATURES +======== + + El-Torito + Support for multiple images + HFS/HFS+ + CD reading + Multisession + UDF + ISO relaxed contraints + ISO 9660:1998 + + Support for special files (only dirs, reg. files and symlinks are supported). + +TESTS +===== + + For all + +IMPLEMENTATION +============== + + a way to return NULL sources meaning a failure!! + Error message queue + Public API for all things already implemented + Better charset support + default input charset to locale one, no always UTF-8 + + \ No newline at end of file diff --git a/libisofs/branches/ZeroTwoEight/acinclude.m4 b/libisofs/branches/ZeroTwoEight/acinclude.m4 new file mode 100644 index 00000000..861847bb --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/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/ZeroTwoEight/bindings/python/README b/libisofs/branches/ZeroTwoEight/bindings/python/README new file mode 100644 index 00000000..9a79b017 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/bindings/python/README @@ -0,0 +1,17 @@ +libisofs python bindings +======================== + +The Python bindings are layered on two levels. The low-level 1:1 mapping of +the C API is done through ctypes. The higher level bindings build a +pythonic API on that mapping. + +The low-level API is in isofs.core. That file is autogenerated using +pyglet's ctypes code generator, which can be found at: + +http://pyglet.googlecode.com/svn/trunk/tools/wraptypes/ + +Note that this is not a permanent solution. Right now the generated code +is committed to the repository, to facilitate development. In the longer +term, the wraptypes tool should be added to the libburnia repository and +the core binding generated by the build system. + diff --git a/libisofs/branches/ZeroTwoEight/bindings/python/isofs/__init__.py b/libisofs/branches/ZeroTwoEight/bindings/python/isofs/__init__.py new file mode 100644 index 00000000..6e296da7 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/bindings/python/isofs/__init__.py @@ -0,0 +1,2 @@ +from defines import * +from isofs import IsoFS diff --git a/libisofs/branches/ZeroTwoEight/bindings/python/isofs/core.py b/libisofs/branches/ZeroTwoEight/bindings/python/isofs/core.py new file mode 100644 index 00000000..b2d5c614 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/bindings/python/isofs/core.py @@ -0,0 +1,185 @@ +'''Wrapper for isofs + +Generated with: +../wraptypes/wrap.py -ocore.py -lisofs ../../../libisofs/libisofs.h + +Do not modify this file. +''' + +__docformat__ = 'restructuredtext' +__version__ = '$Id: wrap.py 738 2007-03-12 04:53:42Z Alex.Holkner $' + +import ctypes +from ctypes import * +from ctypes.util import find_library as _find_library + +#_libpath = _find_library('isofs') +#if not _libpath: +# raise ImportError('Could not locate isofs library') + +# If you are a developer, comment the above three lines and uncomment +# the following line, to hardcode the library path. +_libpath = 'libisofs.so' + +_lib = cdll.LoadLibrary(_libpath) + +_int_types = (c_int16, c_int32) +if hasattr(ctypes, 'c_int64'): + # Some builds of ctypes apparently do not have c_int64 + # defined; it's a pretty good bet that these builds do not + # have 64-bit pointers. + _int_types += (ctypes.c_int64,) +for t in _int_types: + if sizeof(t) == sizeof(c_size_t): + c_ptrdiff_t = t + +class c_void(Structure): + # c_void_p is a buggy return type, converting to int, so + # POINTER(None) == c_void_p is actually written as + # POINTER(c_void), so it can be treated as a real pointer. + _fields_ = [('dummy', c_int)] + + + +class struct_iso_volume(Structure): + __slots__ = [ + ] +struct_iso_volume._fields_ = [ + ('_opaque_struct', c_int) +] + +class struct_iso_tree_node(Structure): + __slots__ = [ + ] +struct_iso_tree_node._fields_ = [ + ('_opaque_struct', c_int) +] + +class struct_iso_volset(Structure): + __slots__ = [ + ] +struct_iso_volset._fields_ = [ + ('_opaque_struct', c_int) +] + +class struct_burn_source(Structure): + __slots__ = [ + ] +struct_burn_source._fields_ = [ + ('_opaque_struct', c_int) +] + +# ../../../libisofs/libisofs.h:45 +iso_volume_new = _lib.iso_volume_new +iso_volume_new.restype = POINTER(struct_iso_volume) +iso_volume_new.argtypes = [c_char_p, c_char_p, c_char_p] + +# ../../../libisofs/libisofs.h:49 +iso_volume_new_with_root = _lib.iso_volume_new_with_root +iso_volume_new_with_root.restype = POINTER(struct_iso_volume) +iso_volume_new_with_root.argtypes = [c_char_p, c_char_p, c_char_p, POINTER(struct_iso_tree_node)] + +# ../../../libisofs/libisofs.h:57 +iso_volume_free = _lib.iso_volume_free +iso_volume_free.restype = None +iso_volume_free.argtypes = [POINTER(struct_iso_volume)] + +# ../../../libisofs/libisofs.h:62 +iso_volset_free = _lib.iso_volset_free +iso_volset_free.restype = None +iso_volset_free.argtypes = [POINTER(struct_iso_volset)] + +# ../../../libisofs/libisofs.h:67 +iso_volume_get_root = _lib.iso_volume_get_root +iso_volume_get_root.restype = POINTER(struct_iso_tree_node) +iso_volume_get_root.argtypes = [POINTER(struct_iso_volume)] + +# ../../../libisofs/libisofs.h:72 +iso_volume_set_volume_id = _lib.iso_volume_set_volume_id +iso_volume_set_volume_id.restype = None +iso_volume_set_volume_id.argtypes = [POINTER(struct_iso_volume), c_char_p] + +# ../../../libisofs/libisofs.h:78 +iso_volume_set_publisher_id = _lib.iso_volume_set_publisher_id +iso_volume_set_publisher_id.restype = None +iso_volume_set_publisher_id.argtypes = [POINTER(struct_iso_volume), c_char_p] + +# ../../../libisofs/libisofs.h:84 +iso_volume_set_data_preparer_id = _lib.iso_volume_set_data_preparer_id +iso_volume_set_data_preparer_id.restype = None +iso_volume_set_data_preparer_id.argtypes = [POINTER(struct_iso_volume), c_char_p] + +# ../../../libisofs/libisofs.h:96 +iso_tree_volume_path_to_node = _lib.iso_tree_volume_path_to_node +iso_tree_volume_path_to_node.restype = POINTER(struct_iso_tree_node) +iso_tree_volume_path_to_node.argtypes = [POINTER(struct_iso_volume), c_char_p] + +# ../../../libisofs/libisofs.h:107 +iso_tree_volume_add_path = _lib.iso_tree_volume_add_path +iso_tree_volume_add_path.restype = POINTER(struct_iso_tree_node) +iso_tree_volume_add_path.argtypes = [POINTER(struct_iso_volume), c_char_p, c_char_p] + +# ../../../libisofs/libisofs.h:119 +iso_tree_volume_add_new_dir = _lib.iso_tree_volume_add_new_dir +iso_tree_volume_add_new_dir.restype = POINTER(struct_iso_tree_node) +iso_tree_volume_add_new_dir.argtypes = [POINTER(struct_iso_volume), c_char_p] + +# ../../../libisofs/libisofs.h:128 +iso_volset_new = _lib.iso_volset_new +iso_volset_new.restype = POINTER(struct_iso_volset) +iso_volset_new.argtypes = [POINTER(struct_iso_volume), c_char_p] + +# ../../../libisofs/libisofs.h:141 +iso_tree_add_node = _lib.iso_tree_add_node +iso_tree_add_node.restype = POINTER(struct_iso_tree_node) +iso_tree_add_node.argtypes = [POINTER(struct_iso_tree_node), c_char_p] + +# ../../../libisofs/libisofs.h:157 +iso_tree_radd_dir = _lib.iso_tree_radd_dir +iso_tree_radd_dir.restype = POINTER(struct_iso_tree_node) +iso_tree_radd_dir.argtypes = [POINTER(struct_iso_tree_node), c_char_p] + +# ../../../libisofs/libisofs.h:166 +iso_exclude_add_path = _lib.iso_exclude_add_path +iso_exclude_add_path.restype = None +iso_exclude_add_path.argtypes = [c_char_p] + +# ../../../libisofs/libisofs.h:173 +iso_exclude_remove_path = _lib.iso_exclude_remove_path +iso_exclude_remove_path.restype = None +iso_exclude_remove_path.argtypes = [c_char_p] + +# ../../../libisofs/libisofs.h:178 +iso_exclude_empty = _lib.iso_exclude_empty +iso_exclude_empty.restype = None +iso_exclude_empty.argtypes = [] + +# ../../../libisofs/libisofs.h:191 +iso_tree_add_new_dir = _lib.iso_tree_add_new_dir +iso_tree_add_new_dir.restype = POINTER(struct_iso_tree_node) +iso_tree_add_new_dir.argtypes = [POINTER(struct_iso_tree_node), c_char_p] + +# ../../../libisofs/libisofs.h:197 +iso_tree_node_set_name = _lib.iso_tree_node_set_name +iso_tree_node_set_name.restype = None +iso_tree_node_set_name.argtypes = [POINTER(struct_iso_tree_node), c_char_p] + +# ../../../libisofs/libisofs.h:204 +iso_tree_print = _lib.iso_tree_print +iso_tree_print.restype = None +iso_tree_print.argtypes = [POINTER(struct_iso_tree_node), c_int] + +# ../../../libisofs/libisofs.h:220 +iso_source_new_ecma119 = _lib.iso_source_new_ecma119 +iso_source_new_ecma119.restype = POINTER(struct_burn_source) +iso_source_new_ecma119.argtypes = [POINTER(struct_iso_volset), c_int, c_int, c_int] + + +__all__ = ['iso_volume_new', 'iso_volume_new_with_root', 'iso_volume_free', +'iso_volset_free', 'iso_volume_get_root', 'iso_volume_set_volume_id', +'iso_volume_set_publisher_id', 'iso_volume_set_data_preparer_id', +'iso_tree_volume_path_to_node', 'iso_tree_volume_add_path', +'iso_tree_volume_add_new_dir', 'iso_volset_new', 'iso_tree_add_node', +'iso_tree_radd_dir', 'iso_exclude_add_path', 'iso_exclude_remove_path', +'iso_exclude_empty', 'iso_tree_add_new_dir', 'iso_tree_node_set_name', +'iso_tree_print', 'iso_source_new_ecma119'] diff --git a/libisofs/branches/ZeroTwoEight/bindings/python/isofs/defines.py b/libisofs/branches/ZeroTwoEight/bindings/python/isofs/defines.py new file mode 100644 index 00000000..ee1e3766 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/bindings/python/isofs/defines.py @@ -0,0 +1,9 @@ +# The automatic code generator does not wrap enums into python. As +# isofs has only two enum values publicly defined, they are hardcoded +# here. + +# from enum ecma119_extension_flag +ECMA119_ROCKRIDGE = 1 +ECMA119_JOLIET = 2 + +__all__ = ['ECMA119_ROCKRIDGE', 'ECMA119_JOLIET'] diff --git a/libisofs/branches/ZeroTwoEight/bindings/python/isofs/isofs.py b/libisofs/branches/ZeroTwoEight/bindings/python/isofs/isofs.py new file mode 100644 index 00000000..e836ef13 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/bindings/python/isofs/isofs.py @@ -0,0 +1,58 @@ +# High level interface to the isofs library. + +import core +import defines +import os.path + +def _wrap_volume_property(var_name, core_setter): + def get(self): + return getattr(self, var_name) + def set(self, value): + setattr(self, var_name, value) + core_setter(self._volume, value) + return property(get, set) + +class IsoFS(object): + def __init__(self, volume_id='', publisher_id='', + data_preparer_id=''): + self._volume_id = volume_id + self._publisher_id = publisher_id + self._data_preparer_id = data_preparer_id + + self._volume = core.iso_volume_new( + volume_id, publisher_id, data_preparer_id) + self._volset = core.iso_volset_new(self._volume, volume_id) + + def __del__(self): + core.iso_volume_free(self._volume) + core.iso_volset_free(self._volset) + + volume_id = _wrap_volume_property( + '_volume_id', core.iso_volume_set_volume_id) + publisher_id = _wrap_volume_property( + '_publisher_id', core.iso_volume_set_publisher_id) + data_preparer_id = _wrap_volume_property( + '_data_preparer_id', core.iso_volume_set_data_preparer_id) + + def add(self, disc_path, path, exclude=None): #, make_parents=False): + disc_path = os.path.normpath(disc_path) + path = os.path.abspath(os.path.normpath(path)) + exclude = exclude or [] + + # Does the disc parent exist? + disc_parent, _ = os.path.split(disc_path) + if not core.iso_tree_volume_path_to_node(self._volume, disc_parent): + print "No such parent" + return # TODO: Raise exception and/or create all missing + # parents. + + # Flush all ignores that may have stayed over. + core.iso_exclude_empty() + for exclude_path in exclude: + core.iso_exclude_add_path(exclude_path) + + core.iso_tree_volume_add_path(self._volume, disc_path, path) + + def display(self): + root = core.iso_volume_get_root(self._volume) + core.iso_tree_print(root, 0) diff --git a/libisofs/branches/ZeroTwoEight/bootstrap b/libisofs/branches/ZeroTwoEight/bootstrap new file mode 100755 index 00000000..86709bfc --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/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/ZeroTwoEight/configure.ac b/libisofs/branches/ZeroTwoEight/configure.ac new file mode 100644 index 00000000..96b506d4 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/configure.ac @@ -0,0 +1,117 @@ +AC_INIT([libisofs], [0.2.4], [http://libburnia.pykix.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 Making releases: +dnl BURN_MICRO_VERSION += 1; +dnl BURN_INTERFACE_AGE += 1; +dnl BURN_BINARY_AGE += 1; +dnl if any functions have been added, set BURN_INTERFACE_AGE to 0. +dnl if backwards compatibility has been broken, +dnl set BURN_BINARY_AGE and BURN_INTERFACE_AGE to 0. +dnl +dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match +dnl +BURN_MAJOR_VERSION=0 +BURN_MINOR_VERSION=2 +BURN_MICRO_VERSION=4 +BURN_INTERFACE_AGE=0 +BURN_BINARY_AGE=0 +BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION + +AC_SUBST(BURN_MAJOR_VERSION) +AC_SUBST(BURN_MINOR_VERSION) +AC_SUBST(BURN_MICRO_VERSION) +AC_SUBST(BURN_INTERFACE_AGE) +AC_SUBST(BURN_BINARY_AGE) +AC_SUBST(BURN_VERSION) + +dnl Libtool versioning +LT_RELEASE=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION +LT_CURRENT=`expr $BURN_MICRO_VERSION - $BURN_INTERFACE_AGE` +LT_REVISION=$BURN_INTERFACE_AGE +LT_AGE=`expr $BURN_BINARY_AGE - $BURN_INTERFACE_AGE` +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_ERROR([Libisofs requires largefile support.]) +fi + +AC_PROG_LIBTOOL +AC_SUBST(LIBTOOL_DEPS) +LIBTOOL="$LIBTOOL --silent" + +AC_PROG_INSTALL + +AC_CHECK_HEADERS() + +AC_CHECK_MEMBER([struct tm.tm_gmtoff], + [AC_DEFINE(HAVE_TM_GMTOFF, 1, + [Define this if tm structure includes a tm_gmtoff entry.])], + , + [#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 + +AC_CONFIG_FILES([ + Makefile + doc/doxygen.conf + version.h + libisofs-1.pc + ]) +AC_OUTPUT diff --git a/libisofs/branches/ZeroTwoEight/doc/Makefile b/libisofs/branches/ZeroTwoEight/doc/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/doc/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libisofs/branches/ZeroTwoEight/doc/comments b/libisofs/branches/ZeroTwoEight/doc/comments new file mode 100644 index 00000000..a35270b7 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/doc/comments @@ -0,0 +1,123 @@ +/** + @author Mario Danic, Thomas Schmitt + + @mainpage Libburn Documentation Index + + @section intro Introduction + +Libburn is an open-source library for reading, mastering and writing +optical discs. For now this means only CD-R and CD-RW. + +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 do have a workable code base for burning data CDs, though. The burn API is +quite comprehensively documented and can be used to build a presentable +application. +We do have a functional binary which emulates parts of cdrecord in order to +prove that usability, and in order to allow you to explore libburn's scope +by help of existing cdrecord frontends. + +@subsection components 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. + +- libisofs is the library to pack up hard disk files and directories into a + ISO 9660 disk image. This may then be brought to CD 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. + 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 for more. + +- "test" is a collection of application gestures and examples given by the + authors of the library features. The main API example of libburn + is named test/libburner.c . + Explore these examples if you look for inspiration. + +We plan to be a responsive upstream. Bear with us. + + + @section using Using the libraries + +Our build system is based on autotools. +User experience tells us that you will need at least autotools version 1.7. + +To build libburn and its subprojects it should be sufficient to go into +its toplevel directory and execute + +- ./bootstrap (needed if you downloaded from SVN) + +- ./configure + +- make + +To make the libraries accessible for running resp. developing applications + +- make install + +Both libraries are written in C language and get built by autotools. +Thus we expect them to be useable by a wide range of Linux-implemented +languages and development tools. + + +@section libburner Libburner + +libburner is a minimal demo application for the library libburn +(see: libburn/libburn.h) as provided on http://libburn.pykix.org . +It can list the available devices, can blank a CD-RW and +can burn to CD-R or CD-RW. + +It's main purpose, nevertheless, is to show you how to use libburn and also +to serve the libburn team as reference application. libburner does indeed +define the standard way how above three gestures can be implemented and +stay upward compatible for a good while. + + @subsection libburner-help Libburner --help +
+Usage: test/libburner
+       [--drive 
||"-"] + [--blank_fast|--blank_full] [--audio] + [--try_to_simulate] [--stdin_size ] + [|"-"] +Examples +A bus scan (needs rw-permissions to see a drive): + test/libburner --drive - +Burn a file to drive chosen by number: + test/libburner --drive 0 my_image_file +Burn a file to drive chosen by persistent address: + test/libburner --drive /dev/hdc my_image_file +Blank a used CD-RW (is combinable with burning in one run): + test/libburner --drive /dev/hdc --blank_fast +Burn two audio tracks + lame --decode -t /path/to/track1.mp3 track1.cd + test/dewav /path/to/track2.wav -o track2.cd + test/libburner --drive /dev/hdc --audio track1.cd track2.cd +Burn a compressed afio archive on-the-fly, pad up to 700 MB: + ( cd my_directory ; find . -print | afio -oZ - ) | \ + test/libburner --drive /dev/hdc --stdin_size 734003200 - +To be read from *not mounted* CD via: afio -tvZ /dev/hdc +Program tar would need a clean EOF which our padded CD cannot deliver. +
+ + @subsection libburner-source Sourceode of libburner + +Click on blue names of functions, structures, variables, etc in oder to +get to the according specs of libburn API or libburner sourcecode. + +*/ diff --git a/libisofs/branches/ZeroTwoEight/doc/comments_test_ts b/libisofs/branches/ZeroTwoEight/doc/comments_test_ts new file mode 100644 index 00000000..a4e23c55 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/doc/comments_test_ts @@ -0,0 +1,121 @@ +/** + @author Mario Danic, Thomas Schmitt + + @mainpage Libburn Documentation Index + + @section intro Introduction + +Libburn is an open-source library for reading, mastering and writing +optical discs. For now this means only CD-R and CD-RW. + +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 and we will have a hard time to widen +this for now, because of our history. The project could need advise from or +membership of skilled kernel people and people who know how to talk CD/DVD +drives into doing things. + +We do have a workable code base for burning data CDs, though. The burn API is +quite comprehensively documented and can be used to build a presentable +application. +We do have a functional binary which emulates parts of cdrecord in order to +prove that usability, and in order to allow you to explore libburn's scope +by help of existing cdrecord frontends. + +@subsection components 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. + +- libisofs is the library to pack up hard disk files and directories into a + ISO 9660 disk image. This may then be brought to CD 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. + 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 for more. + +- "test" is a collection of application gestures and examples given by the + authors of the library features. The main API example of libburn + is named test/libburner.c . + Explore these examples if you look for inspiration. + +We plan to be a responsive upstream. Bear with us. + + + @section using Using the libraries + +Our build system is based on autotools. +User experience tells us that you will need at least autotools version 1.7. + +To build libburn and its subprojects it should be sufficient to go into +its toplevel directory and execute + +- ./bootstrap (needed if you downloaded from SVN) + +- ./configure + +- make + +To make the libraries accessible for running resp. developing applications + +- make install + +Both libraries are written in C language and get built by autotools. +Thus we expect them to be useable by a wide range of Linux-implemented +languages and development tools. + + +@section libburner Libburner + +libburner is a minimal demo application for the library libburn +(see: libburn/libburn.h) as provided on http://libburn.pykix.org . +It can list the available devices, can blank a CD-RW and +can burn to CD-R or CD-RW. + +It's main purpose, nevertheless, is to show you how to use libburn and also +to serve the libburn team as reference application. libburner does indeed +define the standard way how above three gestures can be implemented and +stay upward compatible for a good while. + + @subsection libburner-help Libburner --help +
+Usage: test/libburner
+       [--drive 
||"-"] + [--verbose ] [--blank_fast|--blank_full] + [--burn_for_real|--try_to_simulate] [--stdin_size ] + [|"-"] +Examples +A bus scan (needs rw-permissions to see a drive): + test/libburner --drive - +Burn a file to drive chosen by number: + test/libburner --drive 0 --burn_for_real my_image_file +Burn a file to drive chosen by persistent address: + test/libburner --drive /dev/hdc --burn_for_real my_image_file +Blank a used CD-RW (is combinable with burning in one run): + test/libburner --drive 0 --blank_fast +Burn a compressed afio archive on-the-fly, pad up to 700 MB: + ( cd my_directory ; find . -print | afio -oZ - ) | \ + test/libburner --drive /dev/hdc --burn_for_real --stdin_size 734003200 - +To be read from *not mounted* CD via: + afio -tvZ /dev/hdc +Program tar would need a clean EOF which our padded CD cannot deliver. +
+ + @subsection libburner-source Sourceode of libburner + +Click on blue names of functions, structures, variables, etc in oder to +get to the according specs of libburn API or libburner sourcecode. + +@include libburner.c +*/ diff --git a/libisofs/branches/ZeroTwoEight/doc/doxygen.conf.in b/libisofs/branches/ZeroTwoEight/doc/doxygen.conf.in new file mode 100644 index 00000000..10ae7525 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/doc/doxygen.conf.in @@ -0,0 +1,186 @@ +# Doxyfile 1.2.18 + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = @PACKAGE_NAME@ +PROJECT_NUMBER = @PACKAGE_VERSION@ +OUTPUT_DIRECTORY = +OUTPUT_LANGUAGE = English +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = @top_srcdir@ +INTERNAL_DOCS = NO +STRIP_CODE_COMMENTS = NO +CASE_SENSE_NAMES = NO +SHORT_NAMES = NO +HIDE_SCOPE_NAMES = NO +VERBATIM_HEADERS = YES +SHOW_INCLUDE_FILES = YES +JAVADOC_AUTOBRIEF = YES +MULTILINE_CPP_IS_BRIEF = YES +DETAILS_AT_TOP = YES +INHERIT_DOCS = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 4 +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ALIASES = +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +SHOW_USED_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = libisofs doc +FILE_PATTERNS = libisofs.h comments +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = test +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = OB OTK _ +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = doc/html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 200 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = letter +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_SCHEMA = +XML_DTD = +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = DOXYGEN +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +GRAPHICAL_HIERARCHY = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/libisofs/branches/ZeroTwoEight/libisofs-1.pc.in b/libisofs/branches/ZeroTwoEight/libisofs-1.pc.in new file mode 100644 index 00000000..4cf18c17 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/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/ZeroTwoEight/libisofs/Makefile b/libisofs/branches/ZeroTwoEight/libisofs/Makefile new file mode 100755 index 00000000..062350dd --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libisofs/branches/ZeroTwoEight/libisofs/ecma119.c b/libisofs/branches/ZeroTwoEight/libisofs/ecma119.c new file mode 100755 index 00000000..7747b2c6 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/ecma119.c @@ -0,0 +1,840 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include +#include +#include +#include +#include +#include + +#include "ecma119.h" +#include "ecma119_tree.h" +#include "susp.h" +#include "rockridge.h" +#include "joliet.h" +#include "volume.h" +#include "tree.h" +#include "util.h" +#include "file.h" +#include "libisofs.h" +#include "libburn/libburn.h" +#include "eltorito.h" + +/* burn-source compatible stuff */ +static int +bs_read(struct burn_source *bs, unsigned char *buf, int size); +static off_t +bs_get_size(struct burn_source *bs); +static void +bs_free_data(struct burn_source *bs); + +typedef void (*write_fn)(struct ecma119_write_target*, uint8_t*); + +/* return true if the given state is only required for Joliet volumes */ +static int +is_joliet_state(enum ecma119_write_state); + +static void +next_state(struct ecma119_write_target *t); + +/* write t->state_data to the buf, one block at a time */ +static void +write_data_chunk(struct ecma119_write_target *t, uint8_t *buf); + +/* writing functions. All these functions assume the buf is large enough */ +static void +write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf); +static void +write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf); +static void +write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf); +static void +write_l_path_table(struct ecma119_write_target *t, uint8_t *buf); +static void +write_m_path_table(struct ecma119_write_target *t, uint8_t *buf); +static void +write_one_dir_record(struct ecma119_write_target *t, + struct ecma119_tree_node *dir, + int file_id, + uint8_t *buf); +static void +write_one_dir(struct ecma119_write_target *t, + struct ecma119_tree_node *dir, + uint8_t *buf); +static void +write_dirs(struct ecma119_write_target *t, uint8_t *buf); + +/* wrapper functions for writing */ +static void wr_system_area(struct ecma119_write_target*, uint8_t*); +static void wr_pri_vol_desc(struct ecma119_write_target*, uint8_t*); +static void wr_vol_desc_term(struct ecma119_write_target*, uint8_t*); +static void wr_l_path_table(struct ecma119_write_target*, uint8_t*); +static void wr_m_path_table(struct ecma119_write_target*, uint8_t*); +static void wr_dir_records(struct ecma119_write_target*, uint8_t*); +static void wr_files(struct ecma119_write_target*, uint8_t*); + +static const write_fn writers[] = +{ + NULL, + wr_system_area, + wr_pri_vol_desc, + el_torito_wr_boot_vol_desc, + joliet_wr_sup_vol_desc, + wr_vol_desc_term, + wr_l_path_table, + wr_m_path_table, + joliet_wr_l_path_table, + joliet_wr_m_path_table, + wr_dir_records, + joliet_wr_dir_records, + el_torito_wr_catalog, + wr_files +}; + +/* When a writer is created, we + * 1) create an ecma119 tree + * 2) add SUSP fields (if necessary) + * 3) calculate the size and position of all nodes in the tree + * 4) finalize SUSP fields (if necessary) + */ + +static void +add_susp_fields_rec(struct ecma119_write_target *t, + struct ecma119_tree_node *node) +{ + size_t i; + + rrip_add_PX(t, node); + rrip_add_NM(t, node); + rrip_add_TF(t, node); + + switch (node->type) { + case ECMA119_FILE: + break; + case ECMA119_SYMLINK: + rrip_add_SL(t, node); + break; + case ECMA119_DIR: + if (node->info.dir.real_parent != node->parent) { + rrip_add_RE(t, node); + rrip_add_PL(t, node); + } + for (i = 0; i < node->info.dir.nchildren; i++) { + add_susp_fields_rec(t, node->info.dir.children[i]); + } + break; + case ECMA119_PLACEHOLDER: + rrip_add_CL(t, node); + break; + default: + // FIXME support for device blocks by uncommenting this + //if (node->iso_self->attrib.st_rdev) + // rrip_add_PN(t, node); + break; + } + susp_add_CE(t, node); +} + +static void +add_susp_fields(struct ecma119_write_target *t) +{ + susp_add_SP(t, t->root); + rrip_add_ER(t, t->root); + add_susp_fields_rec(t, t->root); +} + +/** + * Fill out the dir.len and dir.CE_len fields for each + * ecma119_tree_node that is a directory. Also calculate the total number of + * directories and the number of files for which we need to write out data. + * (dirlist_len and filelist_len) + */ +static void +calc_dir_size(struct ecma119_write_target *t, + struct ecma119_tree_node *dir) +{ + size_t i; + size_t newlen; + + assert(dir->type == ECMA119_DIR); + + t->dirlist_len++; + dir->info.dir.len = 34 + dir->info.dir.self_susp.non_CE_len + + 34 + dir->info.dir.parent_susp.non_CE_len; + dir->info.dir.CE_len = dir->info.dir.self_susp.CE_len + + dir->info.dir.parent_susp.CE_len; + for (i = 0; i < dir->info.dir.nchildren; ++i) { + struct ecma119_tree_node *ch = dir->info.dir.children[i]; + + newlen = dir->info.dir.len + ch->dirent_len + ch->susp.non_CE_len; + if ((newlen % 2048) < (dir->info.dir.len % 2048)) { + dir->info.dir.len = newlen + (2048 - (dir->info.dir.len % 2048)); + } else { + dir->info.dir.len += ch->dirent_len + ch->susp.non_CE_len; + } + dir->info.dir.CE_len += ch->susp.CE_len; + } + t->total_dir_size += round_up(dir->info.dir.len + dir->info.dir.CE_len, + t->block_size); + + for (i = 0; i < dir->info.dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->info.dir.children[i]; + //struct iso_tree_node *iso = ch->iso_self; + if (ch->type == ECMA119_DIR) { + calc_dir_size(t, ch); + } +// else if (iso && iso->attrib.st_size +// && iso->loc.type == LIBISO_FILESYS +// && iso->loc.path) { +// t->filelist_len++; +// } + } +} + +/** + * Fill out the block field in each ecma119_tree_node that is a directory and + * fill out t->dirlist. + */ +static void +calc_dir_pos(struct ecma119_write_target *t, + struct ecma119_tree_node *dir) +{ + size_t i; + + assert(dir->type == ECMA119_DIR); + + dir->info.dir.block = t->curblock; + t->curblock += div_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size); + t->dirlist[t->curfile++] = dir; + for (i = 0; i < dir->info.dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->info.dir.children[i]; + if (ch->type == ECMA119_DIR) + calc_dir_pos(t, ch); + } +} + +static int +cmp_file(const void *f1, const void *f2) +{ + struct iso_file *f = *((struct iso_file**)f1); + struct iso_file *g = *((struct iso_file**)f2); + /* higher weighted first */ + return g->sort_weight - f->sort_weight; +} + +/** + * Fill out the block field for each file and fill out t->filelist. + */ +static void +calc_file_pos(struct ecma119_write_target *t, + struct ecma119_tree_node *dir) +{ + size_t i; + + assert(dir->type == ECMA119_DIR); + + t->filelist = calloc(1, sizeof(struct iso_file *) * t->file_table->count); + + for (i = 0; i < FILE_HASH_NODES; ++i) { + + struct iso_file_hash_node *node; + + node = t->file_table->table[i]; + if (!node) + continue; + + do { + struct iso_file *file = node->file; + if (file->size) + t->filelist[t->curfile++] = file; + node = node->next; + } while (node); + } + + t->filelist_len = t->curfile; + + /* sort */ + if ( t->sort_files ) + qsort(t->filelist, t->filelist_len, sizeof(void*), cmp_file); + + /* fill block value */ + for ( i = 0; i < t->filelist_len; ++i) { + struct iso_file *file = t->filelist[i]; + file->block = t->curblock; + t->curblock += div_up(file->size, t->block_size); + } + + /* reset curfile when we're finished */ + t->curfile = 0; +} + + +/** + * Create a new ecma119_write_target from the given volume number of the + * given volume set. + * + * \pre \p volnum is less than \p volset-\>volset_size. + * \post For each node in the tree, writer_data has been allocated. + * \post The directory heirarchy has been reorganised to be ecma119-compatible. + */ +static struct ecma119_write_target* +ecma119_target_new(struct iso_volset *volset, + const struct ecma119_source_opts *opts) +{ + struct ecma119_write_target *t = + calloc(1, sizeof(struct ecma119_write_target)); + size_t i, j, cur; + struct iso_tree_node *iso_root = + (struct iso_tree_node*) volset->volume[opts->volnum]->root; + + t->cache_inodes = opts->no_cache_inodes ? 0 : 1; + t->replace_mode = opts->default_mode ? 0 : 1; + if ( opts->replace_dir_mode ) + t->replace_mode |= 0x02; + if ( opts->replace_file_mode ) + t->replace_mode |= 0x04; + if ( opts->replace_gid ) + t->replace_mode |= 0x08; + if ( opts->replace_uid ) + t->replace_mode |= 0x10; + t->dir_mode = opts->dir_mode; + t->file_mode = opts->file_mode; + t->gid = opts->gid; + t->uid = opts->uid; + + //TODO get defailt values for current locale, no UTF-8 + t->input_charset = opts->input_charset ? opts->input_charset : "UTF-8"; + t->ouput_charset = opts->ouput_charset ? opts->ouput_charset : "UTF-8"; + t->sort_files = opts->sort_files; + + t->file_table = iso_file_table_new(t->cache_inodes); + volset->refcount++; + t->iso_level = opts->level; + t->block_size = 2048; + t->relaxed_constraints = opts->relaxed_constraints; + + t->rockridge = (opts->flags & ECMA119_ROCKRIDGE) ? 1 : 0; + t->joliet = (opts->flags & ECMA119_JOLIET) ? 1 : 0; + + t->catalog = volset->volume[opts->volnum]->bootcat; + t->eltorito = t->catalog ? 1 : 0; + + t->root = ecma119_tree_create(t, iso_root); + if (t->joliet) + t->joliet_root = joliet_tree_create(t, iso_root); + t->volset = volset; + t->volnum = opts->volnum; + t->now = time(NULL); + + if (t->rockridge) + add_susp_fields(t); + + calc_dir_size(t, t->root); + if (t->joliet) { + joliet_calc_dir_size(t, t->joliet_root); + t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet); + t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet); + } + + t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len); + t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len); + + /* fill out the pathlist */ + t->pathlist[0] = t->root; + t->path_table_size = 10; /* root directory record */ + cur = 1; + for (i = 0; i < t->dirlist_len; i++) { + struct ecma119_tree_node *dir = t->pathlist[i]; + for (j = 0; j < dir->info.dir.nchildren; j++) { + struct ecma119_tree_node *ch = dir->info.dir.children[j]; + if (ch->type == ECMA119_DIR) { + size_t len = 8 + strlen(ch->iso_name); + t->pathlist[cur++] = ch; + t->path_table_size += len + len % 2; + } + } + } + + t->curblock = 16 /* system area */ + + 1 /* volume desc */ + + 1; /* volume desc terminator */ + + if (t->eltorito) + t->curblock += 1; /* boot record volume descriptor */ + if (t->joliet) /* supplementary vol desc */ + t->curblock += div_up (2048, t->block_size); + + t->l_path_table_pos = t->curblock; + t->curblock += div_up(t->path_table_size, t->block_size); + t->m_path_table_pos = t->curblock; + t->curblock += div_up(t->path_table_size, t->block_size); + if (t->joliet) { + joliet_prepare_path_tables(t); + t->l_path_table_pos_joliet = t->curblock; + t->curblock += div_up(t->path_table_size_joliet, t->block_size); + t->m_path_table_pos_joliet = t->curblock; + t->curblock += div_up(t->path_table_size_joliet, t->block_size); + } + + calc_dir_pos(t, t->root); + + /* reset curfile when we're finished */ + t->curfile = 0; + if (t->joliet) { + + joliet_calc_dir_pos(t, t->joliet_root); + + /* reset curfile when we're finished */ + t->curfile = 0; + } + + /* el-torito? */ + if (t->eltorito) { + + /* add catalog block */ + t->catalog->file->block = t->curblock; + t->curblock += div_up(2048, t->block_size); + el_torito_get_image_files(t); + } + + calc_file_pos(t, t->root); + + if (t->eltorito) + el_torito_patch_image_files(t); + + if (t->rockridge) { + susp_finalize(t, t->root); + rrip_finalize(t, t->root); + } + + t->total_size = t->curblock * t->block_size; + t->vol_space_size = t->curblock; + + /* prepare for writing */ + t->curblock = 0; + t->state = ECMA119_WRITE_SYSTEM_AREA; + + return t; +} + +static int +is_joliet_state(enum ecma119_write_state state) +{ + return state == ECMA119_WRITE_SUP_VOL_DESC_JOLIET + || state == ECMA119_WRITE_L_PATH_TABLE_JOLIET + || state == ECMA119_WRITE_M_PATH_TABLE_JOLIET + || state == ECMA119_WRITE_DIR_RECORDS_JOLIET; +} + +static int +is_eltorito_state(enum ecma119_write_state state) +{ + return state == ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC + || state == ECMA119_WRITE_ELTORITO_CATALOG; +} + +static void +next_state(struct ecma119_write_target *t) +{ + t->state++; + while ( (!t->joliet && is_joliet_state(t->state)) + ||(!t->eltorito && is_eltorito_state(t->state)) ) + t->state++; + + printf ("now in state %d, curblock=%d\n", (int)t->state, (int)t->curblock); +} + +static void +wr_system_area(struct ecma119_write_target *t, uint8_t *buf) +{ + memset(buf, 0, t->block_size); + if (t->curblock == 15) { + next_state(t); + } +} +static void +wr_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf) +{ + ecma119_start_chunking(t, write_pri_vol_desc, 2048, buf); +} + +static void +wr_vol_desc_term(struct ecma119_write_target *t, uint8_t *buf) +{ + ecma119_start_chunking(t, write_vol_desc_terminator, 2048, buf); +} + +static void +wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf) +{ + ecma119_start_chunking(t, write_l_path_table, t->path_table_size, buf); +} + +static void +wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf) +{ + ecma119_start_chunking(t, write_m_path_table, t->path_table_size, buf); +} + +static void +wr_dir_records(struct ecma119_write_target *t, uint8_t *buf) +{ + ecma119_start_chunking(t, write_dirs, t->total_dir_size, buf); +} + +static void +wr_files(struct ecma119_write_target *t, uint8_t *buf) +{ + struct state_files *f_st = &t->state_files; + size_t nread; + struct iso_file *f = t->filelist[f_st->file]; + const char *path = f->path; + + + if (!f_st->fd) { + printf("Writing file %s\n", path); + f_st->data_len = f->size; + f_st->fd = fopen(path, "r"); + if (!f_st->fd) + err(1, "couldn't open %s for reading", path); + assert(t->curblock == f->block); + } + + nread = fread(buf, 1, t->block_size, f_st->fd); + f_st->pos += t->block_size; + if (nread < 0) + warn("problem reading from %s", path); + else if (nread != t->block_size && f_st->pos < f_st->data_len) + warnx("incomplete read from %s", path); + if (f_st->pos >= f_st->data_len) { + fclose(f_st->fd); + f_st->fd = 0; + f_st->pos = 0; + f_st->file++; + if (f_st->file >= t->filelist_len) + next_state(t); + } +} + +static void +write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf) +{ + struct ecma119_pri_vol_desc *vol = (struct ecma119_pri_vol_desc*)buf; + struct iso_volume *volume = t->volset->volume[t->volnum]; + char *vol_id = str2d_char(volume->volume_id, t->input_charset); + char *pub_id = str2a_char(volume->publisher_id, t->input_charset); + char *data_id = str2a_char(volume->data_preparer_id, t->input_charset); + char *volset_id = str2d_char(t->volset->volset_id, t->input_charset); + + char *system_id = str2a_char(volume->system_id, t->input_charset); + char *application_id = str2a_char(volume->application_id, t->input_charset); + char *copyright_file_id = str2d_char(volume->copyright_file_id, t->input_charset); + char *abstract_file_id = str2d_char(volume->abstract_file_id, t->input_charset); + char *biblio_file_id = str2d_char(volume->biblio_file_id, t->input_charset); + + vol->vol_desc_type[0] = 1; + memcpy(vol->std_identifier, "CD001", 5); + vol->vol_desc_version[0] = 1; + if (system_id) + strncpy((char*)vol->system_id, system_id, 32); + else + /* put linux by default? */ + memcpy(vol->system_id, "LINUX", 5); + if (vol_id) + strncpy((char*)vol->volume_id, vol_id, 32); + iso_bb(vol->vol_space_size, t->vol_space_size, 4); + iso_bb(vol->vol_set_size, t->volset->volset_size, 2); + iso_bb(vol->vol_seq_number, t->volnum + 1, 2); + iso_bb(vol->block_size, t->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, 3, vol->root_dir_record); + + /* mmm, why not check for null? */ + strncpy((char*)vol->vol_set_id, volset_id, 128); + strncpy((char*)vol->publisher_id, pub_id, 128); + strncpy((char*)vol->data_prep_id, data_id, 128); + + if (application_id) + strncpy((char*)vol->application_id, application_id, 128); + if (copyright_file_id) + strncpy((char*)vol->copyright_file_id, copyright_file_id, 37); + if (abstract_file_id) + strncpy((char*)vol->abstract_file_id, abstract_file_id, 37); + if (biblio_file_id) + strncpy((char*)vol->bibliographic_file_id, biblio_file_id, 37); + + iso_datetime_17(vol->vol_creation_time, t->now); + iso_datetime_17(vol->vol_modification_time, t->now); + iso_datetime_17(vol->vol_effective_time, t->now); + 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); +} + +static void +write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf) +{ + struct ecma119_vol_desc_terminator *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; +} + +static void +write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf) +{ + void (*write_int)(uint8_t*, uint32_t, int) = l_type ? iso_lsb + : iso_msb; + size_t i; + struct ecma119_path_table_record *rec; + struct ecma119_tree_node *dir; + int parent = 0; + + for (i = 0; i < t->dirlist_len; i++) { + dir = t->pathlist[i]; + assert(dir->type == ECMA119_DIR); + while ((i) && t->pathlist[parent] != dir->parent) + parent++; + assert(parent < i || i == 0); + + 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]); + buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2); + } +} + +static void +write_l_path_table(struct ecma119_write_target *t, uint8_t *buf) +{ + write_path_table(t, 1, buf); +} + +static void +write_m_path_table(struct ecma119_write_target *t, uint8_t *buf) +{ + write_path_table(t, 0, buf); +} + +/* if file_id is >= 0, we use it instead of the filename. As a magic number, + * file_id == 3 means that we are writing the root directory record (in order + * to distinguish it from the "." entry in the root directory) */ +static void +write_one_dir_record(struct ecma119_write_target *t, + struct ecma119_tree_node *node, + int file_id, + uint8_t *buf) +{ + uint32_t len; + uint32_t block; + uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len; + uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->iso_name); + uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id); + uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->iso_name; + struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; + + if (node->type == ECMA119_DIR) { + len = node->info.dir.len; + block = node->info.dir.block; + } else if (node->type == ECMA119_FILE) { + len = node->info.file->size; + 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; + } + + /* we don't write out susp fields for the root node */ + if (t->rockridge) { + if (file_id == 0) { + assert(node->type == ECMA119_DIR); + susp_write(t, &node->info.dir.self_susp, &buf[len_dr]); + len_dr += node->info.dir.self_susp.non_CE_len; + } else if (file_id == 1) { + assert(node->type == ECMA119_DIR); + susp_write(t, &node->info.dir.parent_susp, &buf[len_dr]); + len_dr += node->info.dir.parent_susp.non_CE_len; + } else if (file_id < 0) { + susp_write(t, &node->susp, &buf[len_dr]); + len_dr += node->susp.non_CE_len; + } + } + 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); + rec->flags[0] = (node->type == ECMA119_DIR) ? 2 : 0; + iso_bb(rec->vol_seq_number, t->volnum + 1, 2); + rec->len_fi[0] = len_fi; + memcpy(rec->file_id, name, len_fi); +} + +static void +write_one_dir(struct ecma119_write_target *t, + struct ecma119_tree_node *dir, + uint8_t *buf) +{ + size_t i; + int j; + size_t len; + uint8_t *orig_buf = buf; + uint8_t *prior_buf = buf; + + assert(dir->type == ECMA119_DIR); + /* write the "." and ".." entries first */ + write_one_dir_record(t, dir, 0, buf); + buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; + + write_one_dir_record(t, dir, 1, buf); + buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; + + for (i = 0; i < dir->info.dir.nchildren; i++) { + write_one_dir_record(t, dir->info.dir.children[i], -1, buf); + len = ((struct ecma119_dir_record*) buf)->len_dr[0]; + if ((buf + len - prior_buf) >= 2048) { + for (j = len - 1; j >= 0; j--) { + prior_buf[2048 + j] = buf[j]; + buf[j] = 0; + } + prior_buf += 2048; + buf = prior_buf + len; + } + else { + buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; + } + } + + /* write the susp continuation areas */ + if (t->rockridge) { + susp_write_CE(t, &dir->info.dir.self_susp, buf); + buf += dir->info.dir.self_susp.CE_len; + susp_write_CE(t, &dir->info.dir.parent_susp, buf); + buf += dir->info.dir.parent_susp.CE_len; + for (i = 0; i < dir->info.dir.nchildren; i++) { + susp_write_CE(t, &dir->info.dir.children[i]->susp, buf); + buf += dir->info.dir.children[i]->susp.CE_len; + } + } + assert (buf - orig_buf == dir->info.dir.len + dir->info.dir.CE_len); +} + +static void +write_dirs(struct ecma119_write_target *t, uint8_t *buf) +{ + size_t i; + struct ecma119_tree_node *dir; + for (i = 0; i < t->dirlist_len; i++) { + dir = t->dirlist[i]; + write_one_dir(t, dir, buf); + buf += round_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size); + } +} + +void +ecma119_start_chunking(struct ecma119_write_target *t, + write_fn writer, + off_t data_size, + uint8_t *buf) +{ + if (data_size != t->state_data_size) { + data_size = round_up(data_size, t->block_size); + t->state_data = realloc(t->state_data, data_size); + t->state_data_size = data_size; + } + memset(t->state_data, 0, t->state_data_size); + t->state_data_off = 0; + t->state_data_valid = 1; + writer(t, t->state_data); + write_data_chunk(t, buf); +} + +static void +write_data_chunk(struct ecma119_write_target *t, uint8_t *buf) +{ + memcpy(buf, t->state_data + t->state_data_off, t->block_size); + t->state_data_off += t->block_size; + if (t->state_data_off >= t->state_data_size) { + assert (t->state_data_off <= t->state_data_size); + t->state_data_valid = 0; + next_state(t); + } +} + +static int +bs_read(struct burn_source *bs, unsigned char *buf, int size) +{ + struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; + if (size != t->block_size) { + warnx("you must read data in block-sized chunks (%d bytes)", + (int)t->block_size); + return 0; + } else if (t->curblock >= t->vol_space_size) { + return 0; + } + if (t->state_data_valid) + write_data_chunk(t, buf); + else + writers[t->state](t, buf); + t->curblock++; + return size; +} + +static off_t +bs_get_size(struct burn_source *bs) +{ + struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; + return t->total_size; +} + +static void +bs_free_data(struct burn_source *bs) +{ + struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; + ecma119_tree_free(t->root); + iso_file_table_clear(t->file_table); + + free(t->dirlist); + free(t->pathlist); + free(t->dirlist_joliet); + free(t->pathlist_joliet); + free(t->filelist); + free(t->state_data); + if (t->joliet) + joliet_tree_free(t->joliet_root); + if (t->state_files.fd) + fclose(t->state_files.fd); +} + +struct burn_source *iso_source_new_ecma119(struct iso_volset *volset, + struct ecma119_source_opts *opts) +{ + struct burn_source *ret = calloc(1, sizeof(struct burn_source)); + ret->refcount = 1; + ret->read = bs_read; + ret->get_size = bs_get_size; + ret->free_data = bs_free_data; + ret->data = ecma119_target_new(volset, opts); + return ret; +} diff --git a/libisofs/branches/ZeroTwoEight/libisofs/ecma119.h b/libisofs/branches/ZeroTwoEight/libisofs/ecma119.h new file mode 100755 index 00000000..0600d048 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/ecma119.h @@ -0,0 +1,306 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file ecma119.h + * + * Structures and definitions used for writing an emca119 (ISO9660) compatible + * volume. + */ + +#ifndef LIBISO_ECMA119_H +#define LIBISO_ECMA119_H + +#include +#include +#include /* for FILE */ +#include +#include "susp.h" + +struct ecma119_tree_node; +struct joliet_tree_node; + +/** + * The possible states that the ecma119 writer can be in. + */ +enum ecma119_write_state +{ + ECMA119_WRITE_BEFORE, + + ECMA119_WRITE_SYSTEM_AREA, + ECMA119_WRITE_PRI_VOL_DESC, + ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC, + ECMA119_WRITE_SUP_VOL_DESC_JOLIET, + ECMA119_WRITE_VOL_DESC_TERMINATOR, + ECMA119_WRITE_L_PATH_TABLE, + ECMA119_WRITE_M_PATH_TABLE, + ECMA119_WRITE_L_PATH_TABLE_JOLIET, + ECMA119_WRITE_M_PATH_TABLE_JOLIET, + ECMA119_WRITE_DIR_RECORDS, + ECMA119_WRITE_DIR_RECORDS_JOLIET, + ECMA119_WRITE_ELTORITO_CATALOG, + ECMA119_WRITE_FILES, + + ECMA119_WRITE_DONE +}; + +/** + * Data describing the state of the ecma119 writer. Everything here should be + * considered private! + */ +struct ecma119_write_target +{ + struct ecma119_tree_node *root; + struct joliet_tree_node *joliet_root; + struct iso_volset *volset; + int volnum; + + time_t now; /**< Time at which writing began. */ + off_t total_size; /**< Total size of the output. This only + * includes the current volume. */ + uint32_t vol_space_size; + + unsigned int rockridge:1; + unsigned int joliet:1; + unsigned int iso_level:2; + unsigned int eltorito:1; + + int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */ + + struct el_torito_boot_catalog *catalog; + + int replace_mode; /**< Replace ownership and modes of files + * + * 0. filesystem values + * 1. useful values + * bits 1-4 bitmask: + * 2 - replace dir + * 3 - replace file + * 4 - replace gid + * 5 - replace uid + */ + mode_t dir_mode; + mode_t file_mode; + gid_t gid; + uid_t uid; + + char *input_charset; + char *ouput_charset; + + int cache_inodes; + + int sort_files; /**< if sort files or not. Sorting is based of + * the weight of each file */ + + /** + * 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; + + int curblock; + uint16_t block_size; + uint32_t path_table_size; + uint32_t path_table_size_joliet; + uint32_t l_path_table_pos; + uint32_t m_path_table_pos; + uint32_t l_path_table_pos_joliet; + uint32_t m_path_table_pos_joliet; + uint32_t total_dir_size; + uint32_t total_dir_size_joliet; + + struct ecma119_tree_node **dirlist; + /**< A pre-order list of directories + * (this is the order in which we write + * out directory records). + */ + struct ecma119_tree_node **pathlist; + /**< A breadth-first list of + * directories. This is used for + * writing out the path tables. + */ + size_t dirlist_len; /**< The length of the previous 2 lists. + */ + + + struct iso_file_table *file_table; + /**< + * A hash table with info about all files + */ + + struct iso_file **filelist; /**< A pre-order list of files.*/ + size_t filelist_len; /* Length of the previous list. */ + + int curfile; /**< Used as a helper field for writing + out filelist and dirlist */ + + /* Joliet versions of the above lists. Since Joliet doesn't require + * directory relocation, the order of these lists might be different + * from the lists above (but they will be the same length). + */ + struct joliet_tree_node **dirlist_joliet; + struct joliet_tree_node **pathlist_joliet; + + size_t dirlist_len_joliet; + + enum ecma119_write_state state; /* The current state of the writer. */ + + /* Most writers work by + * 1) making sure state_data is big enough for their data + * 2) writing _all_ their data into state_data + * 3) relying on write_data_chunk to write the data block + * by block. + */ + uint8_t *state_data; + off_t state_data_size; + off_t state_data_off; + int state_data_valid; + + /* for writing out files */ + struct state_files { + off_t pos; /* The number of bytes we have written + * so far in the current file. + */ + off_t data_len;/* The number of bytes in the currently + * open file. + */ + FILE *fd; /* The currently open file. */ + int file; /* The index in filelist that we are + * currently writing (or about to write). */ + } state_files; +}; + +#define BP(a,b) [(b) - (a) + 1] + +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); +}; + +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); +}; + +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); +}; + +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); +}; + +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) */ +}; + +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) */ +}; + +/** + * A utility function for writers that want to write their data all at once + * rather than block-by-block. This creates a buffer of size \p size, passes + * it to the given writer, then hands out block-sized chunks. + */ +void +ecma119_start_chunking(struct ecma119_write_target *t, + void (*)(struct ecma119_write_target*, uint8_t*), + off_t size, + uint8_t *buf); + +#endif /* LIBISO_ECMA119_H */ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/ecma119_tree.c b/libisofs/branches/ZeroTwoEight/libisofs/ecma119_tree.c new file mode 100644 index 00000000..d31ce6a9 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/ecma119_tree.c @@ -0,0 +1,623 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include +#include +#include +#include + +#include "ecma119.h" +#include "ecma119_tree.h" +#include "tree.h" +#include "util.h" +#include "eltorito.h" + +static size_t calc_dirent_len(struct ecma119_tree_node *n) +{ + int ret = n->iso_name ? strlen(n->iso_name) + 33 : 34; + if (ret % 2) ret++; + return ret; +} + +/** + * Replace the file permissions and user/group id of an ECMA-119 node. + * This is used when a replace mode is selected, i.e., when we want to + * create a disc where the mode of each file or directory will be + * different than the mode in the original source. + */ +static void +replace_node_mode(struct ecma119_write_target *t, struct stat *st) +{ + if ( S_ISDIR(st->st_mode) ) { + if ( t->replace_mode & 0x02 ) { + /* replace dir mode with specific */ + st->st_mode &= S_IFMT; + st->st_mode |= t->dir_mode; + } else if (t->replace_mode & 0x01) { + /* replace dir mode with default */ + /* read perm */ + mode_t new_mode = (st->st_mode & S_IFMT) | 0444; + /* search bit if any */ + if ( st->st_mode & 0111) + new_mode |= 0111; + st->st_mode = new_mode; + } + } else { + if ( t->replace_mode & 0x04 ) { + /* replace file mode with specific */ + st->st_mode &= S_IFMT; + st->st_mode |= t->file_mode; + } else if (t->replace_mode & 0x01) { + /* replace file mode with default */ + /* read perm */ + mode_t new_mode = (st->st_mode & S_IFMT) | 0444; + /* execute bit if any */ + if ( st->st_mode & 0111) + new_mode |= 0111; + st->st_mode = new_mode; + } + + } + if ( t->replace_mode & 0x08 ) { + /* replace gid mode with specific */ + st->st_gid = t->gid; + } else if (t->replace_mode & 0x01) { + st->st_gid = 0; + } + if ( t->replace_mode & 0x10 ) { + /* replace gid mode with specific */ + st->st_uid = t->uid; + } else if (t->replace_mode & 0x01) { + st->st_uid = 0; + } +} + +/** + * Creates a new ECMA-119 node from the given iso tree node, and initializes + * the fields that are common to all kind of nodes (dir, reg file, symlink...). + * + * @param t + * The options for the ECMA-119 tree that is being created + * @param parent + * The parent of the node, or NULL if it's the root. + * @param iso + * The node from which this function creates a ECMA-119 node + * @return + * The created node. + */ +static struct ecma119_tree_node* +create_ecma119_node(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node *iso) +{ + struct ecma119_tree_node *ret; + char *(*iso_name)(const char *, const char *) = ISO_ISDIR(iso) ? + ((t->iso_level == 1) ? iso_1_dirid : iso_2_dirid) + : ((t->iso_level == 1) ? iso_1_fileid : iso_2_fileid); + char *(*iso_r_name)(const char *, const char *, int) = + ISO_ISDIR(iso) ? iso_r_dirid : iso_r_fileid; + + assert(t && (!parent || parent->type == ECMA119_DIR) && iso ); + + ret = calloc(1, sizeof(struct ecma119_tree_node)); + + /* + * If selected one ISO relaxed constraints other than NO_DIR_REALOCATION, + * we use the function that computes the relaxed name, otherwise normal + * function for specified level is used. + */ + ret->iso_name = iso->name ? + ( t->relaxed_constraints & ~ECMA119_NO_DIR_REALOCATION ? + iso_r_name(iso->name, t->input_charset, t->relaxed_constraints) : + iso_name(iso->name, t->input_charset) + ) : NULL; + ret->dirent_len = calc_dirent_len(ret); + + /* iso node keeps the same file attribs as the original file. */ + ret->attrib = iso->attrib; + + /* + * When using RR extension and replace mode, we will replace the + * permissions and uid/gid of each file with those previously selected + * by the user. + */ + if ( t->rockridge && t->replace_mode ) + replace_node_mode(t, &ret->attrib); + + if (!iso->name) + ret->full_name = NULL; + else if ( strcmp(t->input_charset,t->ouput_charset) ) + /* convert the file name charset */ + ret->full_name = convert_str(iso->name, t->input_charset, + t->ouput_charset); + else + ret->full_name = strdup(iso->name); + ret->target = t; + ret->parent = parent; + return ret; +} + +/** + * Create a new ECMA-119 node representing a directory from a iso directory + * node. + */ +static struct ecma119_tree_node* +create_dir(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node_dir *iso) +{ + struct ecma119_tree_node *ret; + + assert(t && (!parent || parent->type == ECMA119_DIR) + && iso && S_ISDIR(iso->node.attrib.st_mode)); + + ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso); + ret->type = ECMA119_DIR; + ret->info.dir.real_parent = parent; + ret->info.dir.depth = parent ? parent->info.dir.depth + 1 : 1; + ret->info.dir.nchildren = 0; + ret->info.dir.children = calloc(1, sizeof(void*) * iso->nchildren); + return ret; +} + +/** + * Create a new ECMA-119 node representing a regular file from a iso file + * node. + */ +static struct ecma119_tree_node* +create_file(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node_file *iso) +{ + struct ecma119_tree_node *ret; + struct iso_file *file; + + assert(t && iso && parent && parent->type == ECMA119_DIR); + + ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso); + ret->type = ECMA119_FILE; + + /* get iso_file struct */ + file = iso_file_table_lookup(t->file_table, iso); + if ( file == NULL ) { + /* + * If the file is not already added to the disc, we add it now + * to the file table, and get a new inode number for it. + */ + file = iso_file_new(iso); + iso_file_table_add_file(t->file_table, file); + file->ino = ++t->ino; + } else { + /* increment number of hard-links */ + file->nlink++; + } + + ret->attrib.st_nlink = file->nlink; + ret->attrib.st_ino = file->ino; + ret->info.file = file; + return ret; +} + +/** + * Create a new ECMA-119 node representing a placeholder for a relocated + * dir. + * + * See IEEE P1282, section 4.1.5 for details + */ +static struct ecma119_tree_node* +create_placeholder(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct ecma119_tree_node *real) +{ + struct ecma119_tree_node *ret; + + assert(t && real && real->type == ECMA119_DIR + && parent && parent->type == ECMA119_DIR); + + ret = calloc(1, sizeof(struct ecma119_tree_node)); + ret->iso_name = real->iso_name; /* TODO strdup? */ + /* FIXME + * if we strdup above, if name changes in mangle_all, + * this probably keeps as original. + * if not, both change, but we need to update dirent_len. + * I think that attributes of a placeholder must be taken from + * real_me, not keept here. + * FIXME + * Another question is that real is a dir, while placeholder is + * a file, and ISO name restricctions are different, what to do? + */ + ret->dirent_len = real->dirent_len; + ret->attrib = real->attrib; + ret->full_name = strdup(real->full_name); + ret->target = t; + ret->parent = parent; + ret->type = ECMA119_PLACEHOLDER; + ret->info.real_me = real; + ret->attrib.st_nlink = 1; + ret->attrib.st_ino = ++t->ino; + return ret; +} + +/** + * Create a new ECMA-119 node representing a symbolic link from a iso symlink + * node. + */ +static struct ecma119_tree_node* +create_symlink(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node_symlink *iso) +{ + struct ecma119_tree_node *ret; + + assert(t && iso && parent && parent->type == ECMA119_DIR); + + ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso); + ret->iso_name = iso->node.name ? ((t->iso_level == 1) ? + iso_1_fileid(iso->node.name, t->input_charset) + : iso_2_fileid(iso->node.name, t->input_charset)) + : NULL; + ret->type = ECMA119_SYMLINK; + ret->info.dest = iso->dest; /* TODO strdup? */ + ret->attrib.st_nlink = 1; + ret->attrib.st_ino = ++t->ino; + return ret; +} + +/** + * Create a new ECMA-119 node representing a boot catalog. This is like a + * regular file, but its contents are taken from a El-Torito boot catalog, + * and not from a file in the local filesystem. + * + * See "El Torito" Bootable CD-ROM Format Specification Version 1.0 for + * more details. + */ +static struct ecma119_tree_node* +create_boot_catalog(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node_boot_catalog *iso) +{ + struct ecma119_tree_node *ret; + struct iso_file *file; + + assert(t && iso && parent && parent->type == ECMA119_DIR); + + /* + * This will simply create a ECMA119 file, with the only difference + * that the iso_file is not taken from table, but from boot catalog + */ + + ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso); + ret->type = ECMA119_FILE; + + file = iso->catalog->file; + file->ino = ++t->ino; + + ret->attrib.st_nlink = file->nlink; + ret->attrib.st_ino = file->ino; + ret->info.file = file; + return ret; +} + +/** + * Create a new ECMA-119 node that corresponds to the given iso tree node. + * If that node is a dir, this function recurses over all their children, + * thus creating a ECMA-119 tree whose root is the given iso dir. + */ +static struct ecma119_tree_node* +create_tree(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node *iso) +{ + struct ecma119_tree_node *ret = NULL; + + assert(t && iso); + + if ( iso->hide_flags & LIBISO_HIDE_ON_RR ) + return NULL; + + switch ( iso->type ) { + case LIBISO_NODE_FILE: + ret = create_file(t, parent, (struct iso_tree_node_file*)iso); + break; + case LIBISO_NODE_SYMLINK: + if ( !t->rockridge ) + printf("Can't add symlinks to a non ISO tree. Skipping %s \n", + iso->name); + else + ret = create_symlink(t, parent, (struct iso_tree_node_symlink*)iso); + break; + case LIBISO_NODE_DIR: + { + size_t i; + struct iso_tree_node_dir *dir = (struct iso_tree_node_dir*)iso; + ret = create_dir(t, parent, dir); + for (i = 0; i < dir->nchildren; i++) { + struct ecma119_tree_node *child; + child = create_tree(t, ret, dir->children[i]); + if (child) + ret->info.dir.children[ret->info.dir.nchildren++] = child; + } + } + break; + case LIBISO_NODE_BOOTCATALOG: + ret = create_boot_catalog(t, parent, + (struct iso_tree_node_boot_catalog*)iso); + break; + default: + /* should never happen */ + assert( 0 ); + break; + } + return ret; +} + +void +ecma119_tree_free(struct ecma119_tree_node *root) +{ + size_t i; + + if (root->type == ECMA119_DIR) { + for (i=0; i < root->info.dir.nchildren; i++) { + ecma119_tree_free(root->info.dir.children[i]); + } + free(root->info.dir.children); + } + free(root->iso_name); + free(root->full_name); + free(root); +} + +static size_t +max_child_name_len(struct ecma119_tree_node *root) +{ + size_t ret = 0, i; + + assert(root->type == ECMA119_DIR); + for (i=0; i < root->info.dir.nchildren; i++) { + size_t len = strlen(root->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 void +reparent(struct ecma119_tree_node *child, + struct ecma119_tree_node *parent) +{ + int found = 0; + size_t i; + struct ecma119_tree_node *placeholder; + + assert(child && parent && parent->type == ECMA119_DIR && child->parent); + + /* 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) { + placeholder = create_placeholder(child->target, + child->parent, child); + child->parent->info.dir.children[i] = placeholder; + found = 1; + break; + } + } + assert(found); + + /* 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; +} + +/** + * 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 (see ECMA-119, + * 6.8.2.1). + */ +static void +reorder_tree(struct ecma119_write_target *t, + struct ecma119_tree_node *root, + struct ecma119_tree_node *cur) +{ + size_t max_path; + + assert(root && cur && cur->type == ECMA119_DIR); + + cur->info.dir.depth = cur->parent ? cur->parent->info.dir.depth + 1 : 1; + cur->info.dir.path_len = cur->parent ? cur->parent->info.dir.path_len + + strlen(cur->iso_name) : 0; + max_path = cur->info.dir.path_len + cur->info.dir.depth + + max_child_name_len(cur); + + if (cur->info.dir.depth > 8 || max_path > 255) { + + if (t->rockridge) { + reparent(cur, root); + /* we are appended to the root's children now, so there is no + * need to recurse (the root will hit us again) */ + } else { + /* we need to delete cur */ + size_t i,j; + struct ecma119_tree_node *parent = cur->parent; + + printf("Can't dirs deeper than 8 without RR. Skipping %s\n", + cur->full_name); + for (i=0; i < parent->info.dir.nchildren; ++i) { + if (parent->info.dir.children[i] == cur) { + break; + } + } + assert ( i < parent->info.dir.nchildren); + for ( j = i; j < parent->info.dir.nchildren - 1; ++j) + parent->info.dir.children[j] = parent->info.dir.children[j+1]; + parent->info.dir.nchildren--; + ecma119_tree_free(cur); + } + } else { + size_t i; + + for (i=0; i < cur->info.dir.nchildren; i++) { + if (cur->info.dir.children[i]->type == ECMA119_DIR) + reorder_tree(t, root, cur->info.dir.children[i]); + } + } +} + +/** + * Compare the iso name of two ECMA-119 nodes + */ +static int +cmp_node(const void *f1, const void *f2) +{ + struct ecma119_tree_node *f = *((struct ecma119_tree_node**)f1); + struct ecma119_tree_node *g = *((struct ecma119_tree_node**)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(struct ecma119_tree_node *root) +{ + size_t i; + + assert(root && root->type == ECMA119_DIR); + + qsort(root->info.dir.children, root->info.dir.nchildren, + sizeof(void*), cmp_node); + 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]); + } +} + +/** + * Change num_change characters of the given filename in order to ensure the + * name is unique. If the name is short enough (depending on the ISO level), + * we can append the characters instead of changing them. + * + * \p seq_num is the index of this file in the sequence of identical filenames. + * + * For example, seq_num=3, num_change=2, name="HELLOTHERE.TXT" changes name to + * "HELLOTHE03.TXT" + */ +static void +mangle_name(char **name, int num_change, int level, int seq_num) +{ + char *dot = strrchr(*name, '.'); + char *semi = strrchr(*name, ';'); + size_t len = strlen(*name); + char base[len+1], ext[len+1]; + char fmt[12]; + size_t baselen, extlen; + + if (num_change >= len) { + return; + } + strncpy(base, *name, len+1); + if (dot) { + base[dot - *name] = '\0'; + strncpy(ext, dot+1, len+1); + if (semi) { + ext[semi - dot - 1] = '\0'; + } + } else { + base[len-2] = '\0'; + ext[0] = '\0'; + } + baselen = strlen(base); + extlen = strlen(ext); + if (level == 1 && baselen + num_change > 8) { + base[8 - num_change] = '\0'; + } else if (level != 1 && baselen + extlen + num_change > 30) { + base[30 - extlen - num_change] = '\0'; + } + + sprintf(fmt, "%%s%%0%1dd.%%s;1", num_change); + *name = realloc(*name, baselen + extlen + num_change + 4); + sprintf(*name, fmt, base, seq_num, ext); +} + +/** + * Ensures that the ISO name of each children of the given dir is unique, + * changing some of them if needed. + */ +static void +mangle_all(struct ecma119_tree_node *dir) +{ + size_t i, j, k; + struct ecma119_dir_info d = dir->info.dir; + size_t n_change; + int changed; + + assert(dir->type == ECMA119_DIR); + do { + changed = 0; + for (i=0; i < d.nchildren; i++) { + /* find the number of consecutive equal names */ + j = 1; + while ( i+j < d.nchildren && + !strcmp(d.children[i]->iso_name, + d.children[i+j]->iso_name) ) + j++; + if (j == 1) continue; + + /* mangle the names */ + changed = 1; + n_change = j / 10 + 1; + for (k=0; k < j; k++) { + mangle_name(&(d.children[i+k]->iso_name), + n_change, + dir->target->iso_level, + k); + d.children[i+k]->dirent_len = + calc_dirent_len(d.children[i+k]); + } + + /* skip ahead by the number of mangled names */ + i += j - 1; + } + } while (changed); + + for (i=0; i < d.nchildren; i++) { + if (d.children[i]->type == ECMA119_DIR) + mangle_all(d.children[i]); + } +} + +struct ecma119_tree_node* +ecma119_tree_create(struct ecma119_write_target *t, + struct iso_tree_node *iso_root) +{ + t->root = create_tree(t, NULL, iso_root); + if ( !(t->relaxed_constraints & ECMA119_NO_DIR_REALOCATION) ) + reorder_tree(t, t->root, t->root); + sort_tree(t->root); + mangle_all(t->root); + return t->root; +} + +void +ecma119_tree_print(struct ecma119_tree_node *root, int spaces) +{ + size_t i; + char sp[spaces+1]; + + memset(sp, ' ', spaces); + sp[spaces] = '\0'; + + printf("%s%s\n", sp, root->iso_name); + if (root->type == ECMA119_DIR) + for (i=0; i < root->info.dir.nchildren; i++) + ecma119_tree_print(root->info.dir.children[i], spaces+2); +} diff --git a/libisofs/branches/ZeroTwoEight/libisofs/ecma119_tree.h b/libisofs/branches/ZeroTwoEight/libisofs/ecma119_tree.h new file mode 100644 index 00000000..e215acd9 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/ecma119_tree.h @@ -0,0 +1,98 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file ecma119_tree.h + * + * Declarations for creating, modifying and printing filesystem trees that + * are compatible with ecma119. + */ + +#ifndef LIBISO_ECMA119_TREE_H +#define LIBISO_ECMA119_TREE_H + +#include + +#include "file.h" + +struct ecma119_write_target; +struct iso_tree_node; + +enum ecma119_node_type { + ECMA119_FILE, + ECMA119_SYMLINK, + ECMA119_DIR, + ECMA119_PLACEHOLDER /**< placeholder for a relocated dir. */ +}; + +struct ecma119_dir_info { + struct susp_info self_susp; /**< susp entries for "." */ + struct susp_info parent_susp; /**< susp entries for ".." */ + + size_t len; /**< sum of the lengths of children's + * Directory Records (including SU) */ + size_t CE_len; /**< sum of the lengths of children's + * SUSP CE areas */ + size_t block; + + int depth; + size_t path_len; /**< The length of a path up to, and + * including, this directory. This + * cannot exceed 255. */ + size_t nchildren; + struct ecma119_tree_node **children; + + struct ecma119_tree_node *real_parent; + /**< The parent before relocation */ +}; + +/** + * A node for a tree containing all the information necessary for writing + * an ISO9660 volume. + */ +struct ecma119_tree_node +{ + char *iso_name; /**< in ASCII, conforming to the + * current ISO level. */ + char *full_name; /**< full name, in current locale (TODO put this in UTF-8?) */ + size_t dirent_len; /**< Length of the directory record, + * not including SU. */ + + struct ecma119_tree_node *parent; + struct ecma119_write_target *target; + + struct stat attrib; + + struct susp_info susp; + + enum ecma119_node_type type; /**< file, symlink, directory or placeholder */ + union { + struct iso_file *file; + char *dest; + struct ecma119_dir_info dir; + struct ecma119_tree_node *real_me; /**< this field points to + * the relocated directory. + */ + } info; +}; + +/** + * Create a new ecma119_tree that corresponds to the tree represented by + * \p iso_root. + */ +struct ecma119_tree_node* +ecma119_tree_create(struct ecma119_write_target *target, + struct iso_tree_node *iso_root); + +/** + * Free an ecma119 tree. + */ +void +ecma119_tree_free(struct ecma119_tree_node *root); + +/** + * Print an ecma119 tree. + */ +void +ecma119_tree_print(struct ecma119_tree_node *root, int spaces); + +#endif /* LIBISO_ECMA119_TREE_H */ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/eltorito.c b/libisofs/branches/ZeroTwoEight/libisofs/eltorito.c new file mode 100644 index 00000000..f066db23 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/eltorito.c @@ -0,0 +1,454 @@ +#include "libisofs.h" + +#include "eltorito.h" +#include "volume.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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); +}; + +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); +}; + +struct el_torito_section_header_entry { + 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); +}; + +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); +}; + +/** + * 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 */ +}; + +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]; +}; + +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; +}; + +static struct el_torito_boot_image * +create_image(struct iso_tree_node *image, + enum eltorito_boot_media_type type) +{ + 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; + + switch (type) { + case ELTORITO_FLOPPY_EMUL: + switch (image->attrib.st_size) { + 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: + fprintf(stderr, "Invalid image size %d Kb. Must be one of 1.2, 1.44" + "or 2.88 Mb", (int) image->attrib.st_size / 1024); + libisofs_errno = ELTORITO_WRONG_IMAGE_SIZE; + return NULL; + 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; + int fd; + struct hard_disc_mbr mbr; + int used_partition; + + /* read the MBR on disc and get the type of the partition */ + fd = open(((struct iso_tree_node_file*)image)->path, O_RDONLY); + if ( fd == -1 ) { + fprintf(stderr, "Can't open image file\n"); + return NULL; + } + if ( read(fd, &mbr, sizeof(mbr)) ) { + fprintf(stderr, "Can't read MBR from image file\n"); + close(fd); + return NULL; + } + close(fd); + + /* check valid MBR signature */ + if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) { + fprintf(stderr, "Invalid MBR. Wrong signature.\n"); + return NULL; + } + + /* 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) { + fprintf(stderr, "Invalid MBR. At least 2 paritions: %d and " + "%d, are being used\n", used_partition, i); + return NULL; + } 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)); + boot->bootable = 1; + boot->image = (struct iso_tree_node_file *) image; + boot->type = boot_media_type; + boot->load_size = load_sectors; + boot->partition_type = partition_type; + return boot; +} + +static struct iso_tree_node_boot_catalog* +create_boot_catalog_node(struct iso_tree_node_dir *parent, + const char *name) +{ + struct iso_tree_node_boot_catalog *boot; + + assert( parent && name ); + + boot = calloc(1, sizeof(struct iso_tree_node_boot_catalog)); + boot->node.attrib.st_mode = S_IFREG | 0444; + boot->node.attrib.st_atime = + boot->node.attrib.st_mtime = + boot->node.attrib.st_ctime = time(NULL); + boot->node.type = LIBISO_NODE_BOOTCATALOG; + boot->node.name = strdup(name); + iso_tree_add_child(parent, (struct iso_tree_node*) boot); + return boot; +} + +struct el_torito_boot_image * +iso_volume_create_boot_catalog(struct iso_volume *volume, + struct iso_tree_node *image, + enum eltorito_boot_media_type type, + struct iso_tree_node_dir *dir, + char *name) +{ + struct el_torito_boot_image *boot_image; + struct iso_tree_node_boot_catalog *boot_node; + struct el_torito_boot_catalog *catalog; + + assert( volume && !volume->bootcat); + assert( image && ISO_ISREG(image) && dir && name); + + boot_image = create_image(image, type); + if ( !boot_image ) + return NULL; + + /* creates the catalog with the given default image */ + catalog = malloc(sizeof(struct el_torito_boot_catalog)); + catalog->nentries = 1; + catalog->entries = malloc(sizeof(struct el_torito_boot_image *)); + catalog->entries[0] = boot_image; + catalog->file = malloc(sizeof(struct iso_file)); + catalog->file->size = 2048; + + /* add catalog file */ + boot_node = create_boot_catalog_node(dir, name); + boot_node->catalog = catalog; + + volume->bootcat = catalog; + + return boot_image; +} + +void +el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment) +{ + if (bootimg->type != ELTORITO_NO_EMUL) + return; + bootimg->load_seg = segment; +} + +void +el_torito_set_load_size(struct el_torito_boot_image *bootimg, int sectors) +{ + if (bootimg->type != ELTORITO_NO_EMUL) + return; + bootimg->load_size = sectors; +} + +void +el_torito_set_no_bootable(struct el_torito_boot_image *bootimg) +{ + bootimg->bootable = 0; +} + +void +el_torito_set_write_boot_info(struct el_torito_boot_image *bootimg) +{ + bootimg->patch_isolinux = 1; +} + +void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat) +{ + size_t i; + + assert(cat); + + for(i = 0; i < cat->nentries; ++i) { + free(cat->entries[i]); + } + free(cat->entries); + free(cat->file); + free(cat); +} + +void el_torito_get_image_files(struct ecma119_write_target *t) +{ + size_t i; + struct el_torito_boot_catalog *cat = t->catalog; + assert(cat); + + for(i = 0; i < cat->nentries; ++i) { + + struct iso_tree_node_file *image = cat->entries[i]->image; + struct iso_file *file = iso_file_table_lookup(t->file_table, image); + if ( file == NULL ) { + file = iso_file_new(image); + iso_file_table_add_file(t->file_table, file); + } + cat->entries[i]->file = file; + } +} + +/** + * Write the Boot Record Volume Descriptor + */ +static void +write_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf) +{ + struct el_torito_boot_catalog *cat = t->catalog; + struct ecma119_boot_rec_vol_desc *vol = + (struct ecma119_boot_rec_vol_desc*)buf; + + assert(cat); + + 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, cat->file->block, 4); +} + +static void +write_validation_entry(struct ecma119_write_target *t, 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 -= buf[i]; + checksum -= (buf[i] << 8); + } + iso_lsb(ve->checksum, checksum, 2); +} + +static void +patch_boot_file(struct el_torito_boot_image *img) +{ + struct boot_info_table info; + int fd; + uint32_t checksum; + ssize_t len; + uint8_t buf[4]; + + memset(&info, 0, sizeof(info)); + + /* open image */ + fd = open(img->image->path, O_RDWR); + if ( fd == -1 ) { + //TODO what do do? exit or just continue? + fprintf(stderr, "Can't patch boot image %s\n", img->image->path); + close(fd); + return; + } + + /* compute checksum, as the the sum of all 32 bit words in boot image + * from offset 64 */ + checksum = 0; + lseek(fd, (off_t) 64, SEEK_SET); + + //TODO this can (must) be optimizied by reading to a longer buffer + while ( (len = read(fd, buf, 4) ) == 4 ) { + checksum += iso_read_lsb(buf, 4); + } + if ( len != 0 ) { + /* error reading file, or file length not multiple of 4 */ + //TODO what do do? exit or just continue? + fprintf(stderr, "Can't patch boot image %s\n", img->image->path); + close(fd); + return; + } + + /* fill boot info table */ + iso_lsb(info.bi_pvd, 16, 4); //FIXME this should be changed when we implement ms + iso_lsb(info.bi_file, img->file->block, 4); + iso_lsb(info.bi_length, img->image->node.attrib.st_size, 4); + iso_lsb(info.bi_csum, checksum, 4); + + /* patch file */ + lseek(fd, (off_t) 8, SEEK_SET); + write(fd, &info, sizeof(info)); + + close(fd); +} + +void +el_torito_patch_image_files(struct ecma119_write_target *t) +{ + size_t i; + struct el_torito_boot_catalog *cat = t->catalog; + assert(cat); + + for (i = 0; i < cat->nentries; ++i) { + struct el_torito_boot_image *img = cat->entries[i]; + if ( img->patch_isolinux ) + patch_boot_file(img); + } +} + +/** + * Write one section entry. + * Currently this is used for both default and other entries since we + * put selection criteria no 0 (no sel. criteria) + */ +static void +write_section_entry(uint8_t *buf, struct el_torito_boot_image *img) +{ + struct el_torito_section_entry *se = + (struct el_torito_section_entry*)buf; + + 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] = 0; //TODO need to get the partition type + iso_lsb(se->sec_count, img->load_size, 2); + iso_lsb(se->block, img->file->block, 4); +} + +/** + * Write El-Torito Boot Catalog + */ +static void +write_catalog(struct ecma119_write_target *t, uint8_t *buf) +{ + struct el_torito_boot_catalog *cat = t->catalog; + assert(cat); + assert(cat->nentries >= 1 && cat->nentries < 63); + + write_validation_entry(t, buf); + + /* write default entry */ + write_section_entry(buf + 32, cat->entries[0]); + + //TODO write all images +} + +void +el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf) +{ + assert(t->catalog); + ecma119_start_chunking(t, + write_boot_vol_desc, + 2048, + buf); +} + +void +el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf) +{ + assert(t->catalog); + ecma119_start_chunking(t, + write_catalog, + 2048, + buf); +} diff --git a/libisofs/branches/ZeroTwoEight/libisofs/eltorito.h b/libisofs/branches/ZeroTwoEight/libisofs/eltorito.h new file mode 100644 index 00000000..2e27fac8 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/eltorito.h @@ -0,0 +1,60 @@ +#ifndef ELTORITO_H_ +#define ELTORITO_H_ + +#include "tree.h" +#include "file.h" +#include "ecma119.h" + +/** + * Location of the boot catalog + */ +struct iso_tree_node_boot_catalog +{ + struct iso_tree_node node; + struct el_torito_boot_catalog *catalog; +}; + +struct el_torito_boot_catalog { + int nentries; + struct el_torito_boot_image **entries; + struct iso_file *file; /**< The catalog file */ +}; + +struct el_torito_boot_image { + unsigned char bootable; /**< If the entry is bootable. */ + unsigned char patch_isolinux; /**< 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 sector to load. */ + struct iso_tree_node_file *image; + struct iso_file *file; +}; + +/*struct el_torito_boot_entry * +el_torito_add_boot_entry(struct el_torito_boot_catalog *cat, + struct iso_tree_node_file *image); +*/ + +void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat); + +/** + * For each boot image file, this gets the related iso_file object. + * In most cases, the file is already in the hash table. However, if the + * boot record is hidden in both ISO/RR and joliet trees, this ensures + * that boot images will be written to image. + */ +void el_torito_get_image_files(struct ecma119_write_target *t); + +/** + * Patch image files if selected. This is needed for isolinux boot images + */ +void el_torito_patch_image_files(struct ecma119_write_target *t); + +void +el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf); + +void +el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf); + +#endif /*ELTORITO_H_*/ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/exclude.c b/libisofs/branches/ZeroTwoEight/libisofs/exclude.c new file mode 100644 index 00000000..f4e245a3 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/exclude.c @@ -0,0 +1,38 @@ +#include "exclude.h" + +void +iso_exclude_add_path(struct iso_hash_table *table, const char *path) +{ + if (!path) + return; + + table->num += iso_hash_insert(table->table, path); +} + +/*void +iso_exclude_remove_path(struct iso_hash_table *table, const char *path) +{ + if (!table->num || !path) + return; + + table->num -= iso_hash_remove(table->table, path); +}*/ + +void +iso_exclude_empty(struct iso_hash_table *table) +{ + if (!table->num) + return; + + iso_hash_empty(table->table); + table->num=0; +} + +int +iso_exclude_lookup(struct iso_hash_table *table, const char *path) +{ + if (!table->num || !path) + return 0; + + return iso_hash_lookup(table->table, path); +} diff --git a/libisofs/branches/ZeroTwoEight/libisofs/exclude.h b/libisofs/branches/ZeroTwoEight/libisofs/exclude.h new file mode 100644 index 00000000..30a9a011 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/exclude.h @@ -0,0 +1,37 @@ +#ifndef ISO_EXCLUDE_H +#define ISO_EXCLUDE_H + +#include "hash.h" + +struct iso_hash_table { + struct iso_hash_node *table[HASH_NODES]; + int num; +}; + +/** + * Add a path to ignore when adding a directory recursively. + * + * \param path The path, on the local filesystem, of the file. + */ +int iso_exclude_lookup(struct iso_hash_table *table, const char *path); + +/** + * Add the path of a file or directory to ignore when adding a directory recursively. + * + * \param path The path, on the local filesystem, of the file. + */ +void iso_exclude_add_path(struct iso_hash_table *table, const char *path); + +/** + * Remove a path that was set to be ignored when adding a directory recusively. + * + * \param path The path, on the local filesystem, of the file. + */ +//void iso_exclude_remove_path(struct iso_hash_table *table, const char *path); + +/** + * Remove all paths that were set to be ignored when adding a directory recusively. + */ +void iso_exclude_empty(struct iso_hash_table *table); + +#endif /* ISO_EXCLUDE */ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/file.c b/libisofs/branches/ZeroTwoEight/libisofs/file.c new file mode 100644 index 00000000..3bbe1349 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/file.c @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include + +#include "file.h" +#include "tree.h" + +//TODO: refactor both hash and this hash table into a single one!! + +struct iso_file * +iso_file_new(struct iso_tree_node_file *f) +{ + struct iso_file *file = calloc(1, sizeof(struct iso_file)); + file->path = f->path; /*TODO strdup? it needs to be free on clear then */ + file->size = f->node.attrib.st_size; + file->nlink = 1; + file->real_dev = f->node.attrib.st_dev; + file->real_ino = f->node.attrib.st_ino; + file->sort_weight = f->sort_weight; + return file; +} + +static unsigned int +iso_file_table_hash(const char *path) +{ + unsigned int hash_num=0; + const char *c; + + c=path; + while(*c) + hash_num = (hash_num << 15) + (hash_num << 3) + (hash_num >> 3) + *c++; + + return hash_num % FILE_HASH_NODES; +} + +static inline unsigned int +iso_file_table_hash_inode(dev_t dev, ino_t ino) +{ + return (dev ^ ino) % FILE_HASH_NODES; +} + +struct iso_file_table* +iso_file_table_new(int cache_inodes) +{ + struct iso_file_table *table = calloc(1, sizeof(struct iso_file_table)); + table->cache_inodes = cache_inodes; + return table; +} + +static struct iso_file_hash_node * +iso_file_table_node_new(struct iso_file *file) +{ + struct iso_file_hash_node *node; + node = calloc(1, sizeof(struct iso_file_hash_node) ); + node->file = file; + return node; +} + +static void +iso_file_table_node_free(struct iso_file_hash_node *node) +{ + free(node->file); + free(node); +} + +void +iso_file_table_clear(struct iso_file_table *ft) +{ + int i; + + for (i=0; i < FILE_HASH_NODES; i++) { + struct iso_file_hash_node *node; + + node=ft->table[i]; + if (!node) + continue; + + ft->table[i] = NULL; + + do { + struct iso_file_hash_node *next; + + next = node->next; + iso_file_table_node_free(node); + node = next; + } while (node); + } + ft->count = 0; +} + +/** + * return 0 if equal, != 0 if not + */ +static int +iso_table_compare_files(struct iso_file_table *ft, + struct iso_file *f1, struct iso_file *f2) +{ + if (ft->cache_inodes) { + return (f1->real_dev != f2->real_dev) || (f1->real_ino != f2->real_ino); + } else { + return strcmp(f1->path, f2->path); + } +} + +int +iso_file_table_add_file(struct iso_file_table *ft, struct iso_file *f) +{ + struct iso_file_hash_node *node; + unsigned int hash_num; + + /* find the hash number */ + if (ft->cache_inodes) + hash_num = iso_file_table_hash_inode(f->real_dev, f->real_ino); + else + hash_num = iso_file_table_hash(f->path); + + /* insert it */ + node = ft->table[hash_num]; + + /* unfortunately, we can't safely consider that a file + * won't be twice in the hash table so make sure it + * doesn't already exists */ + if (!node) { + ft->table[hash_num]=iso_file_table_node_new(f); + ft->count++; + return 1; + } + + /* if it's already in, we don't do anything */ + if (!iso_table_compare_files(ft, f, node->file)) + return 0; + + while (node->next) { + node = node->next; + + /* if it's already in, we don't do anything */ + if (!iso_table_compare_files(ft, f, node->file)) + return 0; + } + + node->next = iso_file_table_node_new(f); + ft->count++; + return 1; +} + +struct iso_file * +iso_file_table_lookup(struct iso_file_table *ft, struct iso_tree_node_file *f) +{ + struct iso_file_hash_node *node; + unsigned int hash_num; + int equal; + + /* find the hash number */ + if ( ft->cache_inodes ) + hash_num = iso_file_table_hash_inode(f->node.attrib.st_dev, + f->node.attrib.st_ino); + else + hash_num = iso_file_table_hash(f->path); + + node = ft->table[hash_num]; + + if (!node) + return NULL; + + equal = ft->cache_inodes ? + ((f->node.attrib.st_dev == node->file->real_dev) + && (f->node.attrib.st_ino == node->file->real_ino)) + : !strcmp(f->path, node->file->path); + if (equal) + return node->file; + + while (node->next) { + node = node->next; + + equal = ft->cache_inodes ? + ((f->node.attrib.st_dev == node->file->real_dev) + && (f->node.attrib.st_ino == node->file->real_ino)) + : !strcmp(f->path, node->file->path); + if (equal) + return node->file; + } + + return NULL; +} + diff --git a/libisofs/branches/ZeroTwoEight/libisofs/file.h b/libisofs/branches/ZeroTwoEight/libisofs/file.h new file mode 100644 index 00000000..aec8815a --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/file.h @@ -0,0 +1,69 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file file.h + * + * Declare the structs to keep track of the files to be written into image. + * These files are stored in a hash table. Two modes of operation are supported: + * when cache inodes is enabled, the files are indexed into the table by the + * device and inode id in the local filesystem. This way, two different files + * with same inode and device id are treated as if they were a single file. + * This is usually the correct behavior, as a different file with same inode + * and device used to be a hard link. + * When cache inode is disabled, indexing is done by path on local filesystem. + */ + +#ifndef FILE_H_ +#define FILE_H_ + +#define FILE_HASH_NODES 2048 + +struct iso_file { + char *path; /**< Path of the file on local filesystem */ + off_t size; /**< size of this file */ + ino_t ino; /**< This will be the inode number on CD of the file (RR) */ + nlink_t nlink; /**< Number of hard links of the file on CD (RR) */ + size_t block; /**< Block where this file is to be written on image */ + dev_t real_dev; + ino_t real_ino; /**< for lookup by inode caching */ + int sort_weight; +}; + +struct iso_file_hash_node { + struct iso_file_hash_node *next; + struct iso_file *file; +}; + +struct iso_file_table { + struct iso_file_hash_node *table[FILE_HASH_NODES]; + size_t count; + int cache_inodes; /**< 1 to index by inode number */ +}; + +struct iso_tree_node_file; + +/** + * Create a struct that represents the specified iso_tree_node_file, + * suitable to be stored into the table, + */ +struct iso_file *iso_file_new(struct iso_tree_node_file*); + +struct iso_file_table *iso_file_table_new(int cache_inodes); + +/** + * Clear a hash table. All iso_file structs stored will also be freed, + * but not the path of each iso_file + */ +void iso_file_table_clear(struct iso_file_table *ft); + +/** + * Add a new file to the table. + * \return 1 if the file is added, 0 if the file already exist on table + */ +int iso_file_table_add_file(struct iso_file_table *ft, struct iso_file *f); + +struct iso_file *iso_file_table_lookup(struct iso_file_table *ft, + struct iso_tree_node_file *f); + +#endif /*FILE_H_*/ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/hash.c b/libisofs/branches/ZeroTwoEight/libisofs/hash.c new file mode 100644 index 00000000..1976efa9 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/hash.c @@ -0,0 +1,158 @@ +#include +#include + +#include "hash.h" + +static unsigned int +iso_hash_path(const char *path) +{ + unsigned int hash_num=0; + const char *c; + + c=path; + while(*c) + hash_num = (hash_num << 15) + (hash_num << 3) + (hash_num >> 3) + *c++; + + return hash_num % HASH_NODES; +} + +int +iso_hash_lookup(struct iso_hash_node **table, const char *path) +{ + struct iso_hash_node *node; + unsigned int hash_num; + + hash_num = iso_hash_path(path); + + node=table[hash_num]; + + if (!node) + return 0; + + if (!strcmp(path, node->path)) + return 1; + + while (node->next) { + node=node->next; + + if (!strcmp(path, node->path)) + return 1; + } + + return 0; + } + +static struct iso_hash_node* +iso_hash_node_new (const char *path) +{ + struct iso_hash_node *node; + + /*create an element to be inserted in the hash table */ + node=malloc(sizeof(struct iso_hash_node)); + node->path=strdup(path); + node->next=NULL; + + return node; +} + +int +iso_hash_insert(struct iso_hash_node **table, const char *path) +{ + struct iso_hash_node *node; + unsigned int hash_num; + + /* find the hash number */ + hash_num = iso_hash_path(path); + + /* insert it */ + node = table[hash_num]; + + /* unfortunately, we can't safely consider that a path + * won't be twice in the hash table so make sure it + * doesn't already exists */ + if (!node) { + table[hash_num]=iso_hash_node_new(path); + return 1; + } + + /* if it's already in, we don't do anything */ + if (!strcmp(path, node->path)) + return 0; + + while (node->next) { + node = node->next; + + /* if it's already in, we don't do anything */ + if (!strcmp (path, node->path)) + return 0; + } + + node->next = iso_hash_node_new(path); + return 1; +} + +static void +iso_hash_node_free(struct iso_hash_node *node) +{ + free(node->path); + free(node); +} + +int +iso_hash_remove(struct iso_hash_node **table, const char *path) +{ + unsigned int hash_num; + struct iso_hash_node *node; + + hash_num = iso_hash_path(path); + + node=table[hash_num]; + if (!node) + return 0; + + if (!strcmp(path, node->path)) { + table[hash_num]=node->next; + iso_hash_node_free(node); + return 1; + } + + while (node->next) { + struct iso_hash_node *prev; + + prev = node; + node = node->next; + + if (!strcmp (path, node->path)) { + prev->next=node->next; + iso_hash_node_free(node); + return 1; + } + } + + return 0; +} + +void +iso_hash_empty(struct iso_hash_node **table) +{ + int i; + + for (i=0; i < HASH_NODES; i++) { + struct iso_hash_node *node; + + node=table[i]; + if (!node) + continue; + + table[i]=NULL; + + do { + struct iso_hash_node *next; + + next=node->next; + iso_hash_node_free(node); + node=next; + } while (node); + } +} + diff --git a/libisofs/branches/ZeroTwoEight/libisofs/hash.h b/libisofs/branches/ZeroTwoEight/libisofs/hash.h new file mode 100644 index 00000000..2d691c78 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/hash.h @@ -0,0 +1,46 @@ +#ifndef ISO_HASH_H +#define ISO_HASH_H + +struct iso_hash_node { + struct iso_hash_node *next; + char *path; +}; + +#define HASH_NODES 128 + +/** + * Searches in the hash table if the path exists. + * + * \param table The hash table. + * \param path The path of the file to look for. + * + * \return 1 if the path exists in the hash table, 0 otherwise. + */ +int iso_hash_lookup(struct iso_hash_node **table, const char *path); + +/** + * Insert a new path in the hash table. + * + * \param table The hash table. + * \param path The path of a file to add to the hash table. + * + * \return 1 if the file wasn't already in the hash table, 0 otherwise. + */ +int iso_hash_insert(struct iso_hash_node **table, const char *path); + +/** + * Remove a path from the hash table. + * + * \param table The hash table. + * \param path The path of a file to remove from the hash table. + * + * \return 1 if the file was found and removed, 0 otherwise. + */ +int iso_hash_remove(struct iso_hash_node **table, const char *path); + +/** + * Empty the hash table. + */ +void iso_hash_empty(struct iso_hash_node **table); + +#endif /* ISO_HASH_H */ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/joliet.c b/libisofs/branches/ZeroTwoEight/libisofs/joliet.c new file mode 100644 index 00000000..0266f8d2 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/joliet.c @@ -0,0 +1,464 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "joliet.h" +#include "ecma119.h" +#include "ecma119_tree.h" +#include "tree.h" +#include "util.h" +#include "volume.h" +#include "eltorito.h" + +#include +#include + +static struct joliet_tree_node* +create_node(struct ecma119_write_target *t, + struct joliet_tree_node *parent, + struct iso_tree_node *iso) +{ + struct joliet_tree_node *ret = + calloc(1, sizeof(struct joliet_tree_node)); + + ret->name = iso_j_id(iso->name, t->input_charset); + ret->dirent_len = 34 + (ret->name ? ucslen(ret->name) * 2 : 0); + ret->parent = parent; + ret->target = t; + + if ( ISO_ISDIR(iso) ) { + struct iso_tree_node_dir *dir = (struct iso_tree_node_dir *) iso; + ret->info.dir.children = calloc(sizeof(void*), dir->nchildren); + ret->type = JOLIET_DIR; + } else if (ISO_ISREG(iso)) { + /* it's a file */ + struct iso_tree_node_file *iso_f = (struct iso_tree_node_file *) iso; + struct iso_file *file; + file = iso_file_table_lookup(t->file_table, iso_f); + if ( file == NULL ) { + file = iso_file_new(iso_f); + iso_file_table_add_file(t->file_table, file); + } + ret->info.file = file; + ret->type = JOLIET_FILE; + } else { + /* it's boot catalog info */ + struct iso_tree_node_boot_catalog *iso_b = + (struct iso_tree_node_boot_catalog *) iso; + struct iso_file *file; + file = iso_b->catalog->file; + ret->info.file = file; + ret->type = JOLIET_FILE; + } + + return ret; +} + +static struct joliet_tree_node* +create_tree(struct ecma119_write_target *t, + struct joliet_tree_node *parent, + struct iso_tree_node *iso) +{ + struct joliet_tree_node *root; + assert(t && iso); + + if ( iso->hide_flags & LIBISO_HIDE_ON_JOLIET ) + return NULL; + + switch (iso->type) { + case LIBISO_NODE_FILE: + case LIBISO_NODE_BOOTCATALOG: + root = create_node(t, parent, iso); + break; + case LIBISO_NODE_DIR: + { + size_t i; + struct joliet_tree_node *node; + struct iso_tree_node_dir *dir; + root = create_node(t, parent, iso); + dir = (struct iso_tree_node_dir*)iso; + for (i = 0; i < dir->nchildren; ++i) { + node = create_tree(t, root, dir->children[i]); + if ( node != NULL ) { + root->info.dir.children[root->info.dir.nchildren++] = node; + } + } + } + break; + default: + //TODO replace this printf + printf("Can't add this kind of node to a Joliet tree"); + return NULL; + break; + } + return root; +} + +static int +cmp_node(const void *f1, const void *f2) +{ + struct joliet_tree_node *f = *((struct joliet_tree_node**)f1); + struct joliet_tree_node *g = *((struct joliet_tree_node**)f2); + return ucscmp(f->name, g->name); +} + +static void +sort_tree(struct joliet_tree_node *root) +{ + size_t i; + + assert(root && (root->type == JOLIET_DIR)); + + qsort(root->info.dir.children, root->info.dir.nchildren, + sizeof(void*), cmp_node); + for (i = 0; i < root->info.dir.nchildren; i++) { + struct joliet_tree_node *child = root->info.dir.children[i]; + if ( child->type == JOLIET_DIR ) + sort_tree(child); + } +} + +void +joliet_prepare_path_tables(struct ecma119_write_target *t) +{ + size_t cur, i, j; + + t->pathlist_joliet[0] = t->joliet_root; + t->path_table_size_joliet = 10; /* root directory record */ + cur = 1; + + for (i = 0; i < t->dirlist_len_joliet; i++) { + struct joliet_tree_node *dir = t->pathlist_joliet[i]; + for (j = 0; j < dir->info.dir.nchildren; j++) { + struct joliet_tree_node *ch = dir->info.dir.children[j]; + if (ch->type == JOLIET_DIR) { + size_t len = 8 + ucslen(ch->name)*2; + t->pathlist_joliet[cur++] = ch; + t->path_table_size_joliet += len; + } + } + } +} + +/** + * Calculate the size of each directory. + */ +void +joliet_calc_dir_size(struct ecma119_write_target *t, + struct joliet_tree_node *root) +{ + size_t i; + size_t newlen; + struct joliet_tree_node *ch; + + assert(root && (root->type == JOLIET_DIR) ); + + t->dirlist_len_joliet++; + root->info.dir.len = 68; /* for "." and ".." entries */ + for (i = 0; i < root->info.dir.nchildren; ++i) { + ch = root->info.dir.children[i]; + newlen = root->info.dir.len + ch->dirent_len; + if ((newlen % 2048) < (root->info.dir.len % 2048)) { + root->info.dir.len = newlen + (2048 - (root->info.dir.len % 2048)); + } else { + root->info.dir.len += ch->dirent_len; + } + if (ch->type == JOLIET_DIR) + joliet_calc_dir_size(t, ch); + } + t->total_dir_size_joliet += round_up (root->info.dir.len, t->block_size); +} + +/** + * Calculate the position of each directory. Also fill out t->dirlist_joliet. + */ +void +joliet_calc_dir_pos(struct ecma119_write_target *t, + struct joliet_tree_node *root) +{ + size_t i; + struct joliet_tree_node *ch; + + assert(root && (root->type == JOLIET_DIR)); + + root->info.dir.block = t->curblock; + t->curblock += div_up(root->info.dir.len, t->block_size); + + t->dirlist_joliet[t->curfile++] = root; + for (i = 0; i < root->info.dir.nchildren; i++) { + ch = root->info.dir.children[i]; + if (ch->type == JOLIET_DIR) + joliet_calc_dir_pos(t, ch); + } +} + +struct joliet_tree_node* +joliet_tree_create(struct ecma119_write_target *t, + struct iso_tree_node *iso_root) +{ + struct joliet_tree_node *root = create_tree(t, NULL, iso_root); + + sort_tree(root); + return root; +} + +void +joliet_tree_free(struct joliet_tree_node *root) +{ + size_t i; + + if (root->type == JOLIET_DIR) { + for (i=0; i < root->info.dir.nchildren; i++) { + joliet_tree_free(root->info.dir.children[i]); + } + free(root->info.dir.children); + } + free(root->name); + free(root); +} + +/* ugh. this is mostly C&P */ +static void +write_path_table(struct ecma119_write_target *t, + int l_type, + uint8_t *buf) +{ + void (*write_int)(uint8_t*, uint32_t, int) = l_type ? + iso_lsb : iso_msb; + + size_t i; + struct ecma119_path_table_record *rec; + struct joliet_tree_node *dir; + int parent = 0; + + assert (t->joliet); + + for (i = 0; i < t->dirlist_len; i++) { + dir = t->pathlist_joliet[i]; + while ((i) && t->pathlist_joliet[parent] != dir->parent) + parent++; + assert(parent < i || i == 0); + + 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]); + buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2); + } + +} + +/* if file_id is >= 0, we use it instead of the filename. As a magic number, + * file_id == 3 means that we are writing the root directory record (in order + * to distinguish it from the "." entry in the root directory) */ +static void +write_one_dir_record(struct ecma119_write_target *t, + struct joliet_tree_node *node, + int file_id, + uint8_t *buf) +{ + uint32_t len; + uint32_t block; + uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len; + uint8_t len_fi = (file_id >= 0) ? 1 : ucslen(node->name) * 2; + uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id); + uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name; + struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; + + if (node->type == JOLIET_DIR) { + len = node->info.dir.len; + block = node->info.dir.block; + } else { + /* file */ + len = node->info.file->size; + block = node->info.file->block; + } + + 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); + rec->flags[0] = (node->type == JOLIET_DIR) ? 2 : 0; + iso_bb(rec->vol_seq_number, t->volnum + 1, 2); + rec->len_fi[0] = len_fi; + memcpy(rec->file_id, name, len_fi); +} + +static void +write_l_path_table(struct ecma119_write_target *t, uint8_t *buf) +{ + write_path_table (t, 1, buf); +} + +static void +write_m_path_table(struct ecma119_write_target *t, uint8_t *buf) +{ + write_path_table (t, 0, buf); +} + +static void +write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf) +{ + struct ecma119_sup_vol_desc *vol = (struct ecma119_sup_vol_desc*)buf; + struct iso_volume *volume = t->volset->volume[t->volnum]; + uint16_t *vol_id = str2ucs(volume->volume_id, t->input_charset); + uint16_t *pub_id = str2ucs(volume->publisher_id, t->input_charset); + uint16_t *data_id = str2ucs(volume->data_preparer_id, t->input_charset); + uint16_t *volset_id = str2ucs(t->volset->volset_id, t->input_charset); + int vol_id_len = MIN(32, ucslen(vol_id) * 2); + int pub_id_len = MIN(128, ucslen(pub_id) * 2); + int data_id_len = MIN(128, ucslen(data_id) * 2); + int volset_id_len = MIN(128, ucslen(volset_id) * 2); + + uint16_t *system_id = str2ucs(volume->system_id, t->input_charset); + uint16_t *application_id = str2ucs(volume->application_id, t->input_charset); + uint16_t *copyright_file_id = str2ucs(volume->copyright_file_id, t->input_charset); + uint16_t *abstract_file_id = str2ucs(volume->abstract_file_id, t->input_charset); + uint16_t *biblio_file_id = str2ucs(volume->biblio_file_id, t->input_charset); + int system_id_len = MIN(32, ucslen(system_id) * 2); + int application_id_len = MIN(128, ucslen(application_id) * 2); + int copyright_file_id_len = MIN(37, ucslen(copyright_file_id) * 2); + int abstract_file_id_len = MIN(37, ucslen(abstract_file_id) * 2); + int biblio_file_id_len = MIN(37, ucslen(biblio_file_id) * 2); + + vol->vol_desc_type[0] = 2; + memcpy(vol->std_identifier, "CD001", 5); + vol->vol_desc_version[0] = 1; + if (vol_id) + memcpy(vol->volume_id, vol_id, vol_id_len); + memcpy(vol->esc_sequences, "%/E", 3); + iso_bb(vol->vol_space_size, t->vol_space_size, 4); + iso_bb(vol->vol_set_size, t->volset->volset_size, 2); + iso_bb(vol->vol_seq_number, t->volnum + 1, 2); + iso_bb(vol->block_size, t->block_size, 2); + iso_bb(vol->path_table_size, t->path_table_size_joliet, 4); + iso_lsb(vol->l_path_table_pos, t->l_path_table_pos_joliet, 4); + iso_msb(vol->m_path_table_pos, t->m_path_table_pos_joliet, 4); + + write_one_dir_record(t, t->joliet_root, 3, vol->root_dir_record); + + memcpy(vol->vol_set_id, volset_id, volset_id_len); + memcpy(vol->publisher_id, pub_id, pub_id_len); + memcpy(vol->data_prep_id, data_id, data_id_len); + + memcpy(vol->system_id, system_id, system_id_len); + memcpy(vol->application_id, "APPID", application_id_len); + memcpy(vol->copyright_file_id, copyright_file_id, copyright_file_id_len); + memcpy(vol->abstract_file_id, abstract_file_id, abstract_file_id_len); + memcpy(vol->bibliographic_file_id, biblio_file_id, biblio_file_id_len); + + iso_datetime_17(vol->vol_creation_time, t->now); + iso_datetime_17(vol->vol_modification_time, t->now); + iso_datetime_17(vol->vol_effective_time, t->now); + 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); + +} + +static void +write_one_dir(struct ecma119_write_target *t, + struct joliet_tree_node *dir, + uint8_t *buf) +{ + size_t i; + int j; + size_t len; + uint8_t *orig_buf = buf; + uint8_t *prior_buf = buf; + + assert(dir->type == JOLIET_DIR); + /* write the "." and ".." entries first */ + write_one_dir_record(t, dir, 0, buf); + buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; + + write_one_dir_record(t, dir, 1, buf); + buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; + + for (i = 0; i < dir->info.dir.nchildren; i++) { + write_one_dir_record(t, dir->info.dir.children[i], -1, buf); + len = ((struct ecma119_dir_record*) buf)->len_dr[0]; + if ((buf + len - prior_buf) >= 2048) { + for (j = len - 1; j >= 0; j--) { + prior_buf[2048 + j] = buf[j]; + buf[j] = 0; + } + prior_buf += 2048; + buf = prior_buf + len; + } + else { + buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; + } + } + + assert (buf - orig_buf == dir->info.dir.len); +} + +static void +write_dirs(struct ecma119_write_target *t, uint8_t *buf) +{ + size_t i; + struct joliet_tree_node *dir; + + assert (t->curblock == t->dirlist_joliet[0]->info.dir.block); + for (i = 0; i < t->dirlist_len_joliet; i++) { + dir = t->dirlist_joliet[i]; + write_one_dir(t, dir, buf); + buf += round_up(dir->info.dir.len, t->block_size); + } +} + +void +joliet_wr_sup_vol_desc(struct ecma119_write_target *t, + uint8_t *buf) +{ + ecma119_start_chunking(t, + write_sup_vol_desc, + 2048, + buf); +} + +void +joliet_wr_l_path_table(struct ecma119_write_target *t, + uint8_t *buf) +{ + ecma119_start_chunking(t, + write_l_path_table, + t->path_table_size_joliet, + buf); +} + +void +joliet_wr_m_path_table(struct ecma119_write_target *t, + uint8_t *buf) +{ + ecma119_start_chunking(t, + write_m_path_table, + t->path_table_size_joliet, + buf); +} + +void +joliet_wr_dir_records(struct ecma119_write_target *t, + uint8_t *buf) +{ + ecma119_start_chunking(t, + write_dirs, + t->total_dir_size_joliet, + buf); +} + diff --git a/libisofs/branches/ZeroTwoEight/libisofs/joliet.h b/libisofs/branches/ZeroTwoEight/libisofs/joliet.h new file mode 100644 index 00000000..07f9a3e6 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/joliet.h @@ -0,0 +1,89 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file joliet.h + * + * Declare the filesystems trees that are Joliet-compatible and the public + * functions for tying them into an ecma119 volume. + */ + +#ifndef LIBISO_JOLIET_H +#define LIBISO_JOLIET_H + +#include +#include + +struct ecma119_write_target; +struct iso_tree_node; + +enum joliet_node_type { + JOLIET_FILE, + JOLIET_DIR +}; + +struct joliet_dir_info { + struct joliet_tree_node **children; + size_t nchildren; + size_t len; + size_t block; +}; + +struct joliet_tree_node +{ + uint16_t *name; /**< In UCS-2BE. */ + size_t dirent_len; + + struct joliet_tree_node *parent; + struct ecma119_write_target *target; + + enum joliet_node_type type; + union { + struct iso_file *file; + struct joliet_dir_info dir; + } info; +}; + +/** + * Create a new joliet_tree that corresponds to the tree represented by + * \p iso_root. + */ +struct joliet_tree_node* +joliet_tree_create(struct ecma119_write_target *target, + struct iso_tree_node *iso_root); + +/** + * Calculate the size of each directory in the joliet heirarchy. + */ +void +joliet_calc_dir_size(struct ecma119_write_target *t, struct joliet_tree_node*); + +/** + * Calculate the position of each directory in the joliet heirarchy. + */ +void +joliet_calc_dir_pos(struct ecma119_write_target *t, struct joliet_tree_node*); + +/** + * Calculate the size of the joliet path table and fill in the list of + * directories. + */ +void +joliet_prepare_path_tables(struct ecma119_write_target *t); + +void +joliet_tree_free(struct joliet_tree_node *root); + +void +joliet_wr_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf); + +void +joliet_wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf); + +void +joliet_wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf); + +void +joliet_wr_dir_records(struct ecma119_write_target *t, uint8_t *buf); + +#endif /* LIBISO_JOLIET_H */ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/libisofs.h b/libisofs/branches/ZeroTwoEight/libisofs/libisofs.h new file mode 100755 index 00000000..b9598a79 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/libisofs.h @@ -0,0 +1,544 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * Create an ISO-9660 data volume with Rock Ridge and Joliet extensions. + * Usage is easy: + * - Create a new volume. + * - Add files and directories. + * - Write the volume to a file or create a burn source for use with Libburn. + */ + +#ifndef LIBISO_LIBISOFS_H +#define LIBISO_LIBISOFS_H + +#include + +/* #include */ +struct burn_source; + +/** + * Data volume. + * @see volume.h for details. + */ +struct iso_volume; + +/** + * A set of data volumes. + * @see volume.h for details. + */ +struct iso_volset; + +/** + * A node in the filesystem tree. + * \see tree.h + */ +struct iso_tree_node; + +/** + * El-Torito boot image + * \see eltorito.h + */ +struct el_torito_boot_image; + +/** + * A directory in the filesystem tree. + * The first member of this is an iso_tree_node. + * \see tree.h + */ +struct iso_tree_node_dir; + +/** + * Extensions addition to ECMA-119 (ISO-9660) image. Usage of at least + * one of these flags is highly recommended if the disc will be used on a + * modern OS. + */ +enum ecma119_extension_flag { + /** + * Add the standard Rock Ridge extensions. This adds POSIX filesystem + * features to the ECMA-119 image. 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. + */ + ECMA119_ROCKRIDGE = (1<<0), + /** + * Add the non-standard Joliet extension to the image. This extension is + * 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. + */ + ECMA119_JOLIET = (1<<1) +}; + +/** + * Flag used to hide a file in the RR/ISO or Joliet tree. + * + * \see iso_tree_node_set_hidden + */ +enum hide_node_flag { + LIBISO_HIDE_ON_RR = 1 << 0, + LIBISO_HIDE_ON_JOLIET = 1 << 1 +}; + +/** + * El-Torito bootable image type. + */ +enum eltorito_boot_media_type { + ELTORITO_FLOPPY_EMUL, + ELTORITO_HARD_DISC_EMUL, + ELTORITO_NO_EMUL +}; + +enum ecma119_relaxed_constraints_flag { + ECMA119_OMIT_VERSION_NUMBERS = (1<<0), + /* 37 char filenames involves no version number */ + ECMA119_37_CHAR_FILENAMES = (1<<1) | (1<<0), + ECMA119_NO_DIR_REALOCATION = (1<<2), + ECMA119_RELAXED_FILENAMES = (1<<3) +}; + +/** + * Holds the options for the image generation. + */ +struct ecma119_source_opts { + int volnum; /**< The volume in the set which you want to write (usually 0) */ + int level; /**< ISO level to write at. */ + int flags; /**< Which extensions to support. */ + int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */ + unsigned int no_cache_inodes:1; + /**< If use inode caching or not. Set it to 1 to prevent + * inode caching. + * Usage of inode caching allows detection of hard-links, + * which contents are only written once to disc this way. + * Don't use inode caching in systems with non unique inodes + * per device. + */ + unsigned int sort_files:1; + /**< If files should be sorted based on their weight. */ + unsigned int default_mode:1; + /**< + * The default values for files and directory permissions, + * gid and uid. This option can be overwritten when set + * one of the following. + * 0 to use useful values, 1 to use node modes (this are + * the same as filesystem ones if not changed after added + * to tree). + */ + unsigned int replace_dir_mode:1; + /**< + * When 1, permissions for all dirs will be replaced by the + * specified in dir_mode field. + */ + unsigned int replace_file_mode:1; + /**< + * When 1, permissions for all files will be replaced by the + * specified in file_mode field. + */ + unsigned int replace_uid:1; + /**< + * When 1, uid of all nodes (both files and dirs) will be + * replaced by the specified in uid field. + */ + unsigned int replace_gid:1; + /**< + * When 1, gid of all nodes (both files and dirs) will be + * replaced by the specified in gid field. + */ + mode_t dir_mode; /**< Mode to use on dirs when replace_dir_mode is set. */ + mode_t file_mode; /**< Mode to use on files when replace_file_mode is set. */ + gid_t gid; /**< gid to use when replace_gid is set. */ + uid_t uid; /**< uid to use when replace_uid is set. */ + char *input_charset; /**< NULL to use default charset */ + char *ouput_charset; /**< NULL to use default charset */ +}; + +/** + * This will hold the error code for some functions, if them fail. + */ +int libisofs_errno; + +/* an unexpected internal error */ +#define INTERNAL_ERROR -1 +/* file don't exists, or can't be stat'ed */ +#define NO_FILE 1 +/* user haven't read access to file */ +#define NO_READ_ACCESS 2 +/* unexpected file type, eg., passing a dir instead of a regular file */ +#define UNEXPECTED_FILE_TYPE 3 +/* invalid boot image size */ +#define ELTORITO_WRONG_IMAGE_SIZE 4 + +/** + * Controls the bahavior of iso_tree_radd_dir function + */ +struct iso_tree_radd_dir_behavior { + char** excludes; /**< List of paths (file or directory) to be ignored. */ + //int follow_sym_link; + int stop_on_error; /**< Stop when an error was found?. */ + int error; /**< set to 1 on error */ + //int notify_errors; + //char** errors; +}; + +/** + * Create a new volume. + * The parameters can be set to NULL if you wish to set them later. + */ +struct iso_volume *iso_volume_new(const char *volume_id, + const char *publisher_id, + const char *data_preparer_id); + +struct iso_volume *iso_volume_new_with_root(const char *volume_id, + const char *publisher_id, + const char *data_preparer_id, + struct iso_tree_node_dir *root); + +/** + * Free a volume. + */ +void iso_volume_free(struct iso_volume *volume); + +/** + * Free a set of data volumes. + */ +void iso_volset_free(struct iso_volset *volume); + +/** + * Get the root directory for a volume. + */ +struct iso_tree_node_dir *iso_volume_get_root(const struct iso_volume *volume); + +/** + * Fill in the volume identifier for a volume. + */ +void iso_volume_set_volume_id(struct iso_volume *volume, + const char *volume_id); + +/** + * Fill in the publisher for a volume. + */ +void iso_volume_set_publisher_id(struct iso_volume *volume, + const char *publisher_id); + +/** + * Fill in the data preparer for a volume. + */ +void iso_volume_set_data_preparer_id(struct iso_volume *volume, + const char *data_preparer_id); + +/** + * Fill in the system id for a volume. Up to 32 characters. + */ +void iso_volume_set_system_id(struct iso_volume *volume, + const char *system_id); + +/** + * Fill in the application id for a volume. Up to 128 chars. + */ +void iso_volume_set_application_id(struct iso_volume *volume, + const char *application_id); + +/** + * Fill copyright information for the volume. Usually this refers + * to a file on disc. Up to 37 characters. + */ +void iso_volume_set_copyright_file_id(struct iso_volume *volume, + const char *copyright_file_id); + +/** + * Fill abstract information for the volume. Usually this refers + * to a file on disc. Up to 37 characters. + */ +void iso_volume_set_abstract_file_id(struct iso_volume *volume, + const char *abstract_file_id); + +/** + * Fill biblio information for the volume. Usually this refers + * to a file on disc. Up to 37 characters. + */ +void iso_volume_set_biblio_file_id(struct iso_volume *volume, + const char *biblio_file_id); + +/** + * Create a bootable volume by adding a El-Torito boot image. + * + * \param volume The volume to make bootable. + * \param image The tree node with the file to use as default boot image. + * \param type The boot media type. This can be one of 3 types: + * - Floppy emulation: Boot image files 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 dir The directory node where the boot catalog will be located + * in image. Usually both boot catalog and boot image will be + * located in the same dir, maybe /boot. + * \param name The name of the boot catalog. + * + * \return The default El-Torito bootable image. If specified image file + * seems to be not correct, this returns NULL and libisofs_errno + * is set propertly. + * + * \pre \p volume is a volume without any boot catalog yet + * \pre \p image is a file tree node already inserted in the volume tree. + * \pre \p dir is a directory node already inserted in the volume tree. + * \pre \p name There isn't any dir child with the same name. + * + */ +struct el_torito_boot_image * +iso_volume_create_boot_catalog(struct iso_volume *volume, + struct iso_tree_node *image, + enum eltorito_boot_media_type type, + struct iso_tree_node_dir *dir, + char *name); + +/** + * 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(struct el_torito_boot_image *bootimg, int 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(struct el_torito_boot_image *bootimg, int sectors); + +/** + * Marks the specified boot image as not bootable + */ +void +el_torito_set_no_bootable(struct el_torito_boot_image *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. + * Be aware that libisofs will modify original boot image file, so do a backup + * if needed. + * This is needed for isolinux boot images. + */ +void +el_torito_set_write_boot_info(struct el_torito_boot_image *bootimg); + +/** + * Locate a node by its path on disc. + * + * \param volume The volume to search in. + * \param path The path, in the image, of the file. + * + * \return The node found or NULL. + * + * TODO we need a way to allow developers know which kind of node is. + * Think about this when designing the read api + */ +struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path); + +/** + * Add a file or a directory (recursively) to a volume by specifying its path on the volume. + * + * \param volume The volume to add the file to. + * \param disc_path The path on the disc at which to add the disc. + * \param path The path, on the local filesystem, of the file. + * + * \return The node for the file or NULL if the parent doesn't exists on the disc. + */ +//struct iso_tree_node *iso_tree_volume_add_path(struct iso_volume *volume, +// const char *disc_path, +// const char *path); + +/** + * Creates a new, empty directory on the volume. + * + * \param volume The volume to add the directory to. + * \param disc_path The path on the volume at which to add the directory. + * + * \return A pointer to the newly created directory. + */ +//struct iso_tree_node *iso_tree_volume_add_new_dir(struct iso_volume *volume, +// const char *disc_path); + +/** + * Create a new Volume Set consisting of only one volume. + * @param volume The first and only volume for the volset to contain. + * @param volset_id The Volume Set ID. + * @return A new iso_volset. + */ +struct iso_volset *iso_volset_new(struct iso_volume *volume, + const char *volset_id); + + +/** + * Creates a new root dir for a filesystem tree + */ +struct iso_tree_node_dir *iso_tree_new_root(); + +/** + * Add a file to a directory. + * + * \param path The path, on the local filesystem, of the file. + * + * \pre \p parent is non-NULL. + * \pre \p path is non-NULL. + * \return An iso_tree_node_file whose path is \p path and whose parent is + * \p parent. + * On error, returns NULL and libisofs_errno is set appropriately: + * NO_FILE if path doesn't point to a valid file. + * NO_READ_ACCESS if user haven't read access on file + * UNEXPECTED_FILE_TYPE if path doesn't point to a regular file + */ +struct iso_tree_node *iso_tree_add_file(struct iso_tree_node_dir *parent, + const char *path); + +/** + * Add a symbolic link to a directory. + * + * \param name The name of the symbolic link + * \param dest The distination of the link, i.e., the file this link points + * to + * + * \pre \p parent, name and dest are non-NULL. + * + * \return An iso_tree_node_symlink + */ +struct iso_tree_node *iso_tree_add_symlink(struct iso_tree_node_dir *parent, + const char *name, const char *dest); + +/** + * Add a new, empty directory to the tree. + * + * \pre \p parent is non-NULL. + * \pre \p name is unique among the children and files belonging to \p parent. + * Also, it doesn't contain '/' characters. + * + * \post \p parent contains a child directory whose name is \p name and whose + * POSIX attributes are the same as \p parent's. + * \return a pointer to the newly created directory. + */ +struct iso_tree_node_dir *iso_tree_add_dir(struct iso_tree_node_dir *parent, + const char *name); + +/* TODO iso_tree_new_special */ + +/** + * Add a file to a directory. + * + * \param path The path, on the local filesystem, of the file. + * + * \pre \p parent is non-NULL. + * \pre \p path is non-NULL and is a valid path to a file or directory on the local + * filesystem. + * \return An iso_tree_node whose path is \p path and whose parent is \p parent. + * On error, returns NULL and libisofs_errno is set appropriately: + * NO_FILE if path doesn't point to a valid file. + * NO_READ_ACCESS if user haven't read access on file + * UNEXPECTED_FILE_TYPE if path refers to non supported file type + * (at the momment, only dirs, symlinks and regular + * files are supported). + */ +struct iso_tree_node *iso_tree_add_node(struct iso_tree_node_dir *parent, + const char *path); + +/** + * Recursively add an existing directory to the tree. + * Warning: when using this, you'll lose pointers to files or subdirectories. + * If you want to have pointers to all files and directories, + * use iso_tree_add_file, iso_tree_add_node and iso_tree_add_dir. + * + * \param path The path, on the local filesystem, of the directory to add. + * + * \pre \p parent is non-NULL. + * \pre \p path is non-NULL and is a valid path to a directory on the local + * filesystem. + */ +void iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path, + struct iso_tree_radd_dir_behavior *behavior); + +/** + * Set the name of a tree node (using the current locale). + */ +void iso_tree_node_set_name(struct iso_tree_node *node, const char *name); + +/** + * 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 hide_node_flag's to set the trees in which file + * will be hidden. + */ +void iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs); + +/** + * Set the group id for the node. This attribute is only useful when + * Rock Ridge extensions are enabled. + */ +void iso_tree_node_set_gid(struct iso_tree_node *node, gid_t gid); + +/** + * Set the user id for the node. This attribute is only useful when + * Rock Ridge extensions are enabled. + */ +void iso_tree_node_set_uid(struct iso_tree_node *node, uid_t uid); + +/** + * 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_tree_node_set_permissions(struct iso_tree_node *node, mode_t mode); + +/** + * 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_tree_node_set_sort_weight(struct iso_tree_node *node, int w); + +/** + * Recursively print a directory to stdout. + * \param spaces The initial number of spaces on the left. Set to 0 if you + * supply a root directory. + */ +void iso_tree_print(const struct iso_tree_node *root, int spaces); + +/** Create a burn_source which can be used as a data source for a track + * + * The volume set used to create the libburn_source can _not_ be modified + * until the libburn_source is freed. + * + * \param volumeset The volume set from which you want to write + * \param opts The options for image generation + * + * \pre \p volumeset is non-NULL + * \pre \p volnum is less than \p volset->volset_size. + * \return A burn_source to be used for the data source for a track + */ +struct burn_source* iso_source_new_ecma119(struct iso_volset *volumeset, + struct ecma119_source_opts *opts); + +#endif /* LIBISO_LIBISOFS_H */ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/rockridge.c b/libisofs/branches/ZeroTwoEight/libisofs/rockridge.c new file mode 100755 index 00000000..c2d7b4b9 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/rockridge.c @@ -0,0 +1,313 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "rockridge.h" +#include "util.h" +#include "ecma119.h" +#include "ecma119_tree.h" +#include "tree.h" +#include "susp.h" + +#include +#include +#include +#include +#include +#include + +/** See IEEE P1281 Draft Version 1.12/5.5 FIXME: this is rockridge */ +void +rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir) +{ + unsigned char *ER = malloc(182); + + assert(dir->type == ECMA119_DIR); + + 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); + susp_append(t, &dir->info.dir.self_susp, ER); +} + +/* create a PX field from the permissions on the current node. */ +uint8_t *rrip_make_PX(struct ecma119_write_target *t, + struct ecma119_tree_node *node) +{ + uint8_t *PX = malloc(44); + + PX[0] = 'P'; + PX[1] = 'X'; + PX[2] = 44; + PX[3] = 1; + iso_bb(&PX[4], node->attrib.st_mode, 4); + iso_bb(&PX[12], node->attrib.st_nlink, 4); + iso_bb(&PX[20], node->attrib.st_uid, 4); + iso_bb(&PX[28], node->attrib.st_gid, 4); + iso_bb(&PX[36], node->attrib.st_ino, 4); + return PX; +} + +/** See IEEE 1282 4.1.1 */ +void rrip_add_PX(struct ecma119_write_target *t, struct ecma119_tree_node *node) +{ + susp_append(t, &node->susp, rrip_make_PX(t, node)); + if (node->type == ECMA119_DIR) { + susp_append(t, &node->info.dir.self_susp, rrip_make_PX(t, node)); + susp_append(t, &node->info.dir.parent_susp, rrip_make_PX(t, node)); + } +} + +void rrip_add_PN(struct ecma119_write_target *t, struct ecma119_tree_node *node) +{ + uint8_t *PN = malloc(20); + + PN[0] = 'P'; + PN[1] = 'N'; + PN[2] = 20; + PN[3] = 1; + iso_bb(&PN[4], node->attrib.st_dev >> 32, 4); + iso_bb(&PN[12], node->attrib.st_dev & 0xffffffff, 4); + susp_append(t, &node->susp, PN); +} + +static void rrip_SL_append_comp(int *n, uint8_t ***comps, + char *s, int size, char fl) +{ + uint8_t *comp = malloc(size + 2); + + (*n)++; + comp[0] = fl; + comp[1] = size; + *comps = realloc(*comps, (*n) * sizeof(void*)); + (*comps)[(*n) - 1] = comp; + + if (size) { + memcpy(&comp[2], s, size); + } +} + +static void rrip_SL_add_component(char *prev, char *cur, int *n_comp, + uint8_t ***comps) +{ + int size = cur - prev; + + if (size == 0) { + rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 3); + return; + } + + if (size == 1 && prev[0] == '.') { + rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 1); + return; + } + if (size == 2 && !strncmp(prev, "..", 2)) { + rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 2); + return; + } + + /* we can't make a component any bigger than 250 (is this really a + problem)? because then it won't fit inside the SL field */ + while (size > 248) { + size -= 248; + rrip_SL_append_comp(n_comp, comps, prev, 248, 1 << 0); + } + + rrip_SL_append_comp(n_comp, comps, prev, size, 0); +} + +void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node) +{ + int path_size; + char *path = NULL, *cur, *prev; + int i, j; + + uint8_t **comp = NULL; + int n_comp = 0; + int total_comp_len = 0; + int written = 0, pos; + + uint8_t *SL; + + path = node->info.dest; + path_size = strlen(path); + + prev = path; + for (cur = strchr(path, '/'); cur && *cur; cur = strchr(cur, '/')) { + rrip_SL_add_component(prev, cur, &n_comp, &comp); + cur++; + prev = cur; + } + + /* if there was no trailing '/', we need to add the last component. */ + if (prev == path || prev != &path[path_size - 1]) { + rrip_SL_add_component(prev, &path[path_size], &n_comp, &comp); + } + + for (i = 0; i < n_comp; i++) { + total_comp_len += comp[i][1] + 2; + if (total_comp_len > 250) { + total_comp_len -= comp[i][1] + 2; + SL = malloc(total_comp_len + 5); + 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][2]); + pos += comp[j][2]; + } + susp_append(t, &node->susp, SL); + written = i - 1; + total_comp_len = comp[i][1]; + } + } + SL = malloc(total_comp_len + 5); + 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_comp; j++) { + memcpy(&SL[pos], comp[j], comp[j][1] + 2); + pos += comp[j][1] + 2; + } + susp_append(t, &node->susp, SL); + + /* free the components */ + for (i = 0; i < n_comp; i++) { + free(comp[i]); + } + free(comp); +} + +static void rrip_add_NM_single(struct ecma119_write_target *t, + struct susp_info *susp, + char *name, int size, int flags) +{ + uint8_t *NM = malloc(size + 5); + + NM[0] = 'N'; + NM[1] = 'M'; + NM[2] = size + 5; + NM[3] = 1; + NM[4] = flags; + if (size) { + memcpy(&NM[5], name, size); + } + susp_append(t, susp, NM); +} + +void +rrip_add_NM(struct ecma119_write_target *t, struct ecma119_tree_node *node) +{ + char *name = node->full_name; + int len = name ? strlen(name) : 0; + char *pos = name; + + if (!len) + return; + + if (node->type == ECMA119_DIR) { + rrip_add_NM_single(t, &node->info.dir.self_susp, pos, 0, 1 << 1); + rrip_add_NM_single(t, &node->info.dir.parent_susp, pos, 0, 1 << 2); + } + + while (len > 250) { + rrip_add_NM_single(t, &node->susp, pos, 250, 1); + len -= 250; + pos += 250; + } + rrip_add_NM_single(t, &node->susp, pos, len, 0); +} + +void rrip_add_CL(struct ecma119_write_target *t, struct ecma119_tree_node *node) +{ + uint8_t *CL = calloc(1, 12); + + CL[0] = 'C'; + CL[1] = 'L'; + CL[2] = 12; + CL[3] = 1; + susp_append(t, &node->susp, CL); +} + +void +rrip_add_PL(struct ecma119_write_target *t, struct ecma119_tree_node *node) +{ + uint8_t *PL = calloc(1, 12); + + PL[0] = 'P'; + PL[1] = 'L'; + PL[2] = 12; + PL[3] = 1; + susp_append(t, &node->info.dir.parent_susp, PL); +} + +void +rrip_add_RE(struct ecma119_write_target *t, struct ecma119_tree_node *node) +{ + uint8_t *RE = malloc(4); + + RE[0] = 'R'; + RE[1] = 'E'; + RE[2] = 4; + RE[3] = 1; + susp_append(t, &node->susp, RE); +} + +void +rrip_add_TF(struct ecma119_write_target *t, struct ecma119_tree_node *node) +{ + uint8_t *TF = malloc(5 + 3 * 7); + + TF[0] = 'T'; + TF[1] = 'F'; + TF[2] = 5 + 3 * 7; + TF[3] = 1; + TF[4] = (1 << 1) | (1 << 2) | (1 << 3); + iso_datetime_7(&TF[5], node->attrib.st_mtime); + iso_datetime_7(&TF[12], node->attrib.st_atime); + iso_datetime_7(&TF[19], node->attrib.st_ctime); + susp_append(t, &node->susp, TF); +} + +void +rrip_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir) +{ + int i; + + assert(dir->type == ECMA119_DIR); + + if (dir->parent != dir->info.dir.real_parent) { + uint8_t *PL = susp_find(&dir->info.dir.parent_susp, "PL"); + + assert(PL); + iso_bb(&PL[4], dir->info.dir.real_parent->info.dir.block, 4); + } + + for (i = 0; i < dir->info.dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->info.dir.children[i]; + + if (ch->type == ECMA119_PLACEHOLDER) { + uint8_t *CL = susp_find(&ch->susp, "CL"); + + assert(CL); + iso_bb(&CL[4], ch->info.real_me->info.dir.block, 4); + } else if (ch->type == ECMA119_DIR) { + rrip_finalize(t, ch); + } + } +} diff --git a/libisofs/branches/ZeroTwoEight/libisofs/rockridge.h b/libisofs/branches/ZeroTwoEight/libisofs/rockridge.h new file mode 100755 index 00000000..63e4f6ae --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/rockridge.h @@ -0,0 +1,112 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * Functions and structures used for Rock Ridge support. + * + * See IEEE P1282, Rock Ridge Interchange Protocol, Draft Standard version + * 1.12 for further details. + */ + +#ifndef ISO_ROCKRIDGE_H +#define ISO_ROCKRIDGE_H + +struct ecma119_write_target; +struct ecma119_tree_node; + +/** + * 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 IEEE P1281, section 5.5 and IEEE P1282, section 4.3 for more details. + */ +void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * Add a PX System Use Entry to the given tree node and, if that node is + * a directory, to its "." and ".." entries. 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. + * + * See IEEE P1282, section 4.1.1 for more details. + */ +void rrip_add_PX(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * 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 IEEE P1282, section 4.1.2 for more details. + */ +void rrip_add_PN(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * 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. + * + * See IEEE P1282, section 4.1.3 for more details. + */ +void rrip_add_SL(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * 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 IEEE P1282, section 4.1.4 for more details. + */ +void rrip_add_NM(struct ecma119_write_target *, struct ecma119_tree_node *); + +/* + * The next 3 System Use Entries are used to handle Deep Directory + * Hierarchies, i.e., hierarchies where the number of directory levels + * exceed the eight limit of ECMA-119. + */ + +/** + * 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 IEEE P1282, section 4.1.5.1 for more details. + */ +void rrip_add_CL(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * 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 IEEE P1282, section 4.1.5.2 for more details. + */ +void rrip_add_PL(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * 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 IEEE P1282, section 4.1.5.3 for more details. + */ +void rrip_add_RE(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * Add to the given tree node a TF System Use Entry, used to record some + * time stamps related to the file. + * + * See IEEE P1282, section 4.1.6 for more details. + */ +void rrip_add_TF(struct ecma119_write_target *, struct ecma119_tree_node *); + + +void rrip_finalize(struct ecma119_write_target *, struct ecma119_tree_node *); + +#endif /* ISO_ROCKRIDGE_H */ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/susp.c b/libisofs/branches/ZeroTwoEight/libisofs/susp.c new file mode 100755 index 00000000..5e9889b3 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/susp.c @@ -0,0 +1,256 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "susp.h" +#include "util.h" +#include "ecma119.h" +#include "ecma119_tree.h" + +#include +#include +#include + +void susp_insert(struct ecma119_write_target *t, + struct susp_info *susp, + uint8_t *data, + int pos) +{ + int i; + + if (pos < 0) { + pos = susp->n_susp_fields; + } + + assert(pos <= susp->n_susp_fields); + susp->n_susp_fields++; + susp->susp_fields = realloc(susp->susp_fields, + sizeof(void*) * susp->n_susp_fields); + + for (i = susp->n_susp_fields-1; i > pos; i--) { + susp->susp_fields[i] = susp->susp_fields[i - 1]; + } + susp->susp_fields[pos] = data; +} + +void susp_append(struct ecma119_write_target *t, + struct susp_info *susp, + uint8_t *data) +{ + susp_insert(t, susp, data, susp->n_susp_fields); +} + +uint8_t *susp_find(struct susp_info *susp, const char *name) +{ + int i; + + for (i = 0; i < susp->n_susp_fields; i++) { + if (!strncmp((char *)susp->susp_fields[i], name, 2)) { + return susp->susp_fields[i]; + } + } + return NULL; +} + +/** Utility function for susp_add_CE because susp_add_CE needs to act 3 times + * on directories (for the "." and ".." entries. + * + * \param len The amount of space available for the System Use area. + */ +#define CE_LEN 28 +static unsigned char *susp_add_single_CE(struct ecma119_write_target *t, + struct susp_info *susp, + int len) +{ + int susp_length = 0, tmp_len; + int i; + unsigned char *CE; + + for (i = 0; i < susp->n_susp_fields; i++) { + susp_length += susp->susp_fields[i][2]; + } + if (susp_length <= len) { + /* no need for a CE field */ + susp->non_CE_len = susp_length; + susp->n_fields_fit = susp->n_susp_fields; + return NULL; + } + + tmp_len = susp_length; + for (i = susp->n_susp_fields - 1; i >= 0; i--) { + tmp_len -= susp->susp_fields[i][2]; + if (tmp_len + CE_LEN <= len) { + susp->non_CE_len = tmp_len + CE_LEN; + susp->CE_len = susp_length - tmp_len; + + /* i+1 because we have to count the CE field */ + susp->n_fields_fit = i + 1; + + CE = calloc(1, CE_LEN); + /* we don't fill in the BLOCK LOCATION or OFFSET + fields yet. */ + CE[0] = 'C'; + CE[1] = 'E'; + CE[2] = (char)CE_LEN; + CE[3] = (char)1; + iso_bb(&CE[20], susp_length - tmp_len, 4); + + return CE; + } + } + assert(0); + return NULL; +} + +static void +try_add_CE(struct ecma119_write_target *t, + struct susp_info *susp, + size_t dirent_len) +{ + uint8_t *CE = susp_add_single_CE(t, susp, 255 - dirent_len); + if (CE) + susp_insert(t, susp, CE, susp->n_fields_fit - 1); +} + +/** See IEEE P1281 Draft Version 1.12/5.2. Because this function depends on the + * length of the other SUSP fields, it should always be calculated last. */ +void +susp_add_CE(struct ecma119_write_target *t, struct ecma119_tree_node *node) +{ + try_add_CE(t, &node->susp, node->dirent_len); + if (node->type == ECMA119_DIR) { + try_add_CE(t, &node->info.dir.self_susp, 34); + try_add_CE(t, &node->info.dir.parent_susp, 34); + } +} + +/** See IEEE P1281 Draft Version 1.12/5.3 */ +void +susp_add_SP(struct ecma119_write_target *t, struct ecma119_tree_node *dir) +{ + unsigned char *SP = malloc(7); + + assert(dir->type == ECMA119_DIR); + + SP[0] = 'S'; + SP[1] = 'P'; + SP[2] = (char)7; + SP[3] = (char)1; + SP[4] = 0xbe; + SP[5] = 0xef; + SP[6] = 0; + susp_append(t, &dir->info.dir.self_susp, SP); +} + +#if 0 +/** See IEEE P1281 Draft Version 1.12/5.4 */ +static void susp_add_ST(struct ecma119_write_target *t, + struct iso_tree_node *node) +{ + unsigned char *ST = malloc(4); + + ST[0] = 'S'; + ST[1] = 'T'; + ST[2] = (char)4; + ST[3] = (char)1; + susp_append(t, node, ST); +} +#endif + +/* calculate the location of the CE areas. Since CE areas don't need to be + * aligned to a block boundary, we contatenate all CE areas from a single + * directory and dump them immediately after all the directory records. + * + * Requires that the following be known: + * - position of the current directory (dir->info.dir.block) + * - length of the current directory (dir->info.dir.len) + * - sum of the children's CE lengths (dir->info.dir.CE_len) + */ +static void +susp_fin_1_CE(struct ecma119_write_target *t, + struct susp_info *susp, + size_t block, + size_t *offset) +{ + uint8_t *CE = susp->susp_fields[susp->n_fields_fit - 1]; + + if (!susp->CE_len) { + return; + } + iso_bb(&CE[4], block + (*offset) / t->block_size, 4); + iso_bb(&CE[12], (*offset) % t->block_size, 4); + *offset += susp->CE_len; +} + +static void susp_fin_CE(struct ecma119_write_target *t, + struct ecma119_tree_node *dir) +{ + int i; + size_t CE_offset = dir->info.dir.len; + + assert(dir->type == ECMA119_DIR); + + susp_fin_1_CE(t, &dir->info.dir.self_susp, dir->info.dir.block, &CE_offset); + susp_fin_1_CE(t, &dir->info.dir.parent_susp, dir->info.dir.block, &CE_offset); + + for (i = 0; i < dir->info.dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->info.dir.children[i]; + susp_fin_1_CE(t, &ch->susp, dir->info.dir.block, &CE_offset); + } + assert(CE_offset == dir->info.dir.len + dir->info.dir.CE_len); +} + +void +susp_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir) +{ + int i; + + assert(dir->type = ECMA119_DIR); + + if (dir->info.dir.depth != 1) { + susp_fin_CE(t, dir); + } + + for (i = 0; i < dir->info.dir.nchildren; i++) { + if (dir->info.dir.children[i]->type == ECMA119_DIR) + susp_finalize(t, dir->info.dir.children[i]); + } +} + +void susp_write(struct ecma119_write_target *t, + struct susp_info *susp, + unsigned char *buf) +{ + int i; + int pos = 0; + + for (i = 0; i < susp->n_fields_fit; i++) { + memcpy(&buf[pos], susp->susp_fields[i], + susp->susp_fields[i][2]); + pos += susp->susp_fields[i][2]; + } +} + +void susp_write_CE(struct ecma119_write_target *t, struct susp_info *susp, + unsigned char *buf) +{ + int i; + int pos = 0; + + for (i = susp->n_fields_fit; i < susp->n_susp_fields; i++) { + memcpy(&buf[pos], susp->susp_fields[i], + susp->susp_fields[i][2]); + pos += susp->susp_fields[i][2]; + } +} + +void susp_free_fields(struct susp_info *susp) +{ + int i; + + for (i=0; in_susp_fields; i++) { + free(susp->susp_fields[i]); + } + if (susp->susp_fields) { + free(susp->susp_fields); + } + memset(susp, 0, sizeof(struct susp_info)); +} diff --git a/libisofs/branches/ZeroTwoEight/libisofs/susp.h b/libisofs/branches/ZeroTwoEight/libisofs/susp.h new file mode 100755 index 00000000..5e7b95e1 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/susp.h @@ -0,0 +1,76 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * Functions and structures used for SUSP (IEEE 1281). + * + * Please refer to IEEE P1281 System Use Sharing Protocol, draft standard + * version 1.12 for more details. + */ + +#ifndef __ISO_SUSP +#define __ISO_SUSP + +#include + +/* SUSP is only present in standard ecma119 */ +struct ecma119_write_target; +struct ecma119_tree_node; + +/** This contains the information that needs to go in the SUSP area of a file. + */ +struct susp_info +{ + int n_susp_fields; /**< Number of SUSP fields */ + uint8_t **susp_fields; /**< Data for each SUSP field */ + + /* the next 3 relate to CE and are filled out by susp_add_CE. */ + int n_fields_fit; /**< How many of the above SUSP fields fit + * within this node's dirent. */ + int non_CE_len; /**< Length of the part of the SUSP area that + * fits in the dirent. */ + int CE_len; /**< Length of the part of the SUSP area that + * will go in a CE area. */ +}; + +/** + * 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. + * See IEEE P1281, section 5.1. + */ +void susp_add_CE(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * Add a SP System Use Entry to the "." entry of the directory. 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 IEEE P1281, section 5.3 for more details. + * + * this is special because it doesn't modify the susp fields of the + * directory; it modifies the susp fields of the "." entry in the directory. + */ +void susp_add_SP(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** Once all the directories and files are laid out, recurse through the tree + * and finalize all SUSP CE entries. */ +void susp_finalize(struct ecma119_write_target *, struct ecma119_tree_node *); + +void susp_append(struct ecma119_write_target *, + struct susp_info *, + uint8_t *); +void susp_insert(struct ecma119_write_target *, + struct susp_info *, + uint8_t *, + int pos); +uint8_t *susp_find(struct susp_info *, + const char *); + +void susp_write(struct ecma119_write_target *, + struct susp_info *, + uint8_t *); +void susp_write_CE(struct ecma119_write_target *, + struct susp_info *, + uint8_t *); + +void susp_free_fields(struct susp_info *); + +#endif /* __ISO_SUSP */ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/tree.c b/libisofs/branches/ZeroTwoEight/libisofs/tree.c new file mode 100755 index 00000000..eddfbc66 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/tree.c @@ -0,0 +1,401 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file tree.c + * + * Implement filesystem trees. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tree.h" +#include "exclude.h" + +static void +set_default_stat(struct stat *s) +{ + time_t now = time(NULL); + + memset(s, 0, sizeof(struct stat)); + s->st_mode = 0555; + s->st_atime = s->st_mtime = s->st_ctime = now; +} + +void +iso_tree_add_child(struct iso_tree_node_dir *parent, + struct iso_tree_node *child) +{ + assert( parent && child); + + parent->nchildren++; + parent->children = + realloc(parent->children, parent->nchildren * sizeof(void*)); + parent->children[parent->nchildren-1] = child; + child->parent = parent; +} + +struct iso_tree_node_dir* +iso_tree_new_root() +{ + struct iso_tree_node_dir *root; + + root = calloc(1, sizeof(struct iso_tree_node_dir)); + + set_default_stat(&root->node.attrib); + root->node.attrib.st_mode = S_IFDIR | 0777; + root->node.type = LIBISO_NODE_DIR; + return root; +} + +struct iso_tree_node* +iso_tree_add_file(struct iso_tree_node_dir *parent, const char *path) +{ + struct iso_tree_node_file *f; + char *p; + struct stat st; + + assert( parent && path); + + if (lstat(path, &st) == -1) { + libisofs_errno = NO_FILE; + return NULL; + } + + if ( !S_ISREG(st.st_mode) ) { + libisofs_errno = UNEXPECTED_FILE_TYPE; + return NULL; + } + + if ( access(path, R_OK) ) { + libisofs_errno = NO_READ_ACCESS; + return NULL; + } + + f = calloc(1, sizeof(struct iso_tree_node_file)); + + /* fill fields */ + f->node.attrib = st; + f->path = strdup(path); + f->node.type = LIBISO_NODE_FILE; + + p = strdup(path); /* because basename() might modify its arg */ + f->node.name = strdup( basename(p) ); + free(p); + + /* add to parent (this also sets f->node->parent) */ + iso_tree_add_child(parent, (struct iso_tree_node*) f); + + return (struct iso_tree_node*) f; +} + +struct iso_tree_node* +iso_tree_add_symlink(struct iso_tree_node_dir *parent, + const char *name, const char *dest) +{ + struct iso_tree_node_symlink *link; + + assert( parent && name && dest); + + link = calloc(1, sizeof(struct iso_tree_node_symlink)); + + /* fill fields */ + set_default_stat(&link->node.attrib); + link->node.attrib.st_mode |= S_IFLNK;//TODO Not needed + link->node.name = strdup(name); + link->node.type = LIBISO_NODE_SYMLINK; + link->dest = strdup(dest); + + /* add to parent (this also sets link->node->parent) */ + iso_tree_add_child(parent, (struct iso_tree_node*) link); + + return (struct iso_tree_node*) link; +} + +struct iso_tree_node_dir* +iso_tree_add_dir(struct iso_tree_node_dir *parent, + const char *name) +{ + struct iso_tree_node_dir *dir; + + assert( parent && name ); + + dir = calloc(1, sizeof(struct iso_tree_node_dir)); + + dir->node.attrib = parent->node.attrib; + dir->node.type = LIBISO_NODE_DIR; + dir->node.name = strdup(name); + + iso_tree_add_child(parent, (struct iso_tree_node*) dir); + return dir; +} + +void +iso_tree_node_set_name(struct iso_tree_node *node, const char *name) +{ + free(node->name); + node->name = strdup(name); +} + +void +iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs) +{ + assert(node); + node->hide_flags = hide_attrs; +} + +void +iso_tree_node_set_gid(struct iso_tree_node *node, gid_t gid) +{ + assert(node); + node->attrib.st_gid = gid; +} + +void +iso_tree_node_set_uid(struct iso_tree_node *node, uid_t uid) +{ + assert(node); + node->attrib.st_uid = uid; +} + +void +iso_tree_node_set_permissions(struct iso_tree_node *node, mode_t mode) +{ + assert(node); + node->attrib.st_mode = (node->attrib.st_mode & S_IFMT) | + (mode & ~S_IFMT); +} + +void +iso_tree_node_set_sort_weight(struct iso_tree_node *node, int w) +{ + assert(node); + if ( ISO_ISDIR(node) ) { + size_t i; + struct iso_tree_node_dir *dir; + dir = (struct iso_tree_node_dir *) node; + for (i=0; i < dir->nchildren; i++) { + iso_tree_node_set_sort_weight(dir->children[i], w); + } + } else if ( ISO_ISREG(node) ) { + struct iso_tree_node_file *file; + file = (struct iso_tree_node_file *) node; + file->sort_weight = w; + } +} + +struct iso_tree_node* +iso_tree_add_node(struct iso_tree_node_dir *parent, + const char *path) +{ + struct stat st; + struct iso_tree_node *node; + + assert( parent && path); + + if (lstat(path, &st) == -1) { + libisofs_errno = NO_FILE; + return NULL; + } + + if ( access(path, R_OK) ) { + libisofs_errno = NO_READ_ACCESS; + return NULL; + } + + switch (st.st_mode & S_IFMT) { + case S_IFREG: + /* regular file */ + node = iso_tree_add_file(parent, path); + break; + case S_IFLNK: + /* symlink */ + { + char dest[PATH_MAX]; + char *p; + int n; + + n = readlink(path, dest, PATH_MAX); + if ( n == -1 ) { + libisofs_errno = INTERNAL_ERROR; + return NULL; + } + dest[n] = '\0'; + p = strdup(path); /* because basename() might modify its arg */ + node = iso_tree_add_symlink(parent, basename(p), dest); + free(p); + node->attrib = st; + } + break; + case S_IFDIR: + /* directory */ + { + char *p; + p = strdup(path); /* because basename() might modify its arg */ + node = (struct iso_tree_node*) iso_tree_add_dir(parent, basename(p)); + free(p); + node->attrib = st; + } + break; + default: + libisofs_errno = UNEXPECTED_FILE_TYPE; + node = NULL; + break; + } + return node; +} + +void +iso_tree_free(struct iso_tree_node *root) +{ + if ( ISO_ISDIR(root) ) { + size_t i; + struct iso_tree_node_dir *dir; + dir = (struct iso_tree_node_dir *) root; + for (i=0; i < dir->nchildren; i++) { + iso_tree_free(dir->children[i]); + } + free(dir->children); + } else if ( ISO_ISLNK(root) ) { + struct iso_tree_node_symlink *link; + link = (struct iso_tree_node_symlink *) root; + free(link->dest); + } else if ( ISO_ISREG(root) ) { + struct iso_tree_node_file *file; + file = (struct iso_tree_node_file *) root; + free(file->path); + } + free(root->name); + free(root); +} + +static void +iso_tree_radd_dir_aux(struct iso_tree_node_dir *parent, const char *path, + struct iso_tree_radd_dir_behavior *behavior, + struct iso_hash_table *excludes) +{ + struct iso_tree_node *new; + DIR *dir; + struct dirent *ent; + + dir = opendir(path); + if (!dir) { + warn("couldn't open directory %s: %s\n", path, strerror(errno)); + return; + } + + while ((ent = readdir(dir))) { + char child[strlen(ent->d_name) + strlen(path) + 2]; + + if (behavior->stop_on_error & behavior->error) + break; + + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + continue; + + //TODO check if path already finished in '/' + sprintf(child, "%s/%s", path, ent->d_name); + + /* see if this child is excluded. */ + if (iso_exclude_lookup(excludes, child)) + continue; + + new = iso_tree_add_node(parent, child); + if (!new || !ISO_ISDIR(new)) { + if (!new) + behavior->error = 1; + continue; + } + + iso_tree_radd_dir_aux( (struct iso_tree_node_dir *) new, child, + behavior, excludes); + + } + closedir(dir); + + return; +} + +void +iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path, + struct iso_tree_radd_dir_behavior *behavior) +{ + struct iso_tree_node_dir *dir; + struct iso_hash_table table = { {0,}, 0}; + + assert ( parent && path ); + + behavior->error = 0; + + /* initialize exclude hash_table */ + if ( behavior->excludes ) { + char *exclude; + int i = 0; + while ( (exclude = behavior->excludes[i++]) ) { + iso_exclude_add_path(&table, exclude); + } + } + + /* recurse into dir */ + iso_tree_radd_dir_aux(parent, path, behavior, &table); + + /* clear hashtable */ + iso_exclude_empty(&table); + + return dir; +} + +void +iso_tree_print(const struct iso_tree_node *root, int spaces) +{ + char sp[spaces+1]; + + memset(sp, ' ', spaces); + sp[spaces] = '\0'; + + printf("%s%s\n", sp, root->name); + + if ( ISO_ISDIR(root) ) { + size_t i; + struct iso_tree_node_dir *dir; + + dir = (struct iso_tree_node_dir *) root; + for (i=0; i < dir->nchildren; i++) { + iso_tree_print(dir->children[i], spaces+2); + } + } +} + +void +iso_tree_print_verbose(const struct iso_tree_node *root, + print_dir_callback dir, + print_file_callback file, + void *callback_data, + int spaces) +{ + + (ISO_ISDIR(root) ? dir : file) + (root, callback_data, spaces); + + if ( ISO_ISDIR(root) ) { + size_t i; + struct iso_tree_node_dir *dir_node; + + dir_node = (struct iso_tree_node_dir *) root; + for (i=0; i < dir_node->nchildren; i++) { + iso_tree_print_verbose(dir_node->children[i], dir, + file, callback_data, spaces+2); + } + } +} diff --git a/libisofs/branches/ZeroTwoEight/libisofs/tree.h b/libisofs/branches/ZeroTwoEight/libisofs/tree.h new file mode 100755 index 00000000..3f89570f --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/tree.h @@ -0,0 +1,177 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file tree.h + * + * Declare the structure of a libisofs filesystem tree. The files in this + * tree can come from either the local filesystem or from another .iso image + * (for multisession). + * + * This tree preserves as much information as it can about the files; names + * are stored in wchar_t and we preserve POSIX attributes. This tree does + * *not* include information that is necessary for writing out, for example, + * an ISO level 1 tree. That information will go in a different tree because + * the structure is sufficiently different. + */ + +#ifndef LIBISO_TREE_H +#define LIBISO_TREE_H + +#include +#include +#include +#include + +#include "libisofs.h" + +//enum file_location { +// LIBISO_FILESYS, +// LIBISO_PREVSESSION, +// LIBISO_NONE /**< for files/dirs that were added with +// * iso_tree_add_new_XXX. */ +//}; + +/** + * This tells us where to read the data from a file. Either we read from the + * local filesystem or we just point to the block on a previous session. + */ +//struct iso_file_location +//{ +// enum file_location type; +// /* union {*/ +// char *path; /* in the current locale */ +// uint32_t block; +// /* };*/ +//}; + +enum iso_tree_node_type { + LIBISO_NODE_DIR, + LIBISO_NODE_FILE, + LIBISO_NODE_SYMLINK, + LIBISO_NODE_BOOTCATALOG +}; + +/** + * A node in the filesystem tree. + */ +struct iso_tree_node +{ + struct iso_tree_node_dir *parent; + char *name; + struct stat attrib; /**< The POSIX attributes of this node as + * documented in "man 2 stat". */ + + int hide_flags; /**< If the node is to be hidden in RR/ISO or + * Joilet tree */ + enum iso_tree_node_type type; +}; + +/** + * A node in the filesystem tree that represents a regular file + */ +struct iso_tree_node_file +{ + struct iso_tree_node node; + + char *path; /**< the path of the file on local filesystem */ + int sort_weight; /**< 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 */ + + /* when read from an existing ISO image, we need to store the + * block where file contents are written, and not the path. + * Maybe instead of a char *path we will need to go back to + * struct iso_file_location loc; + */ + /* struct iso_file_location loc; */ + /**< Only used for regular files and symbolic + * links (ie. files for which we might have to + * copy data). */ +}; + +/** + * A node in the filesystem tree that represents a symbolic link + */ +struct iso_tree_node_symlink +{ + struct iso_tree_node node; + + char *dest; /**< Destination of the link */ +}; + +/** + * A directory on the filesystem tree + */ +struct iso_tree_node_dir +{ + struct iso_tree_node node; + + size_t nchildren; /**< The number of children of this + * directory (if this is a directory). */ + struct iso_tree_node **children; +}; + +/** + * Recursively free a directory. + * + * \param root The root of the directory heirarchy to free. + * + * \pre \p root is non-NULL. + */ +void iso_tree_free(struct iso_tree_node *root); + +/** + * Adds a child to a directory + */ +void iso_tree_add_child(struct iso_tree_node_dir *parent, + struct iso_tree_node *child); + +/** + * A function that prints verbose information about a directory. + * + * \param dir The directory about which to print information. + * \param data Unspecified function-dependent data. + * \param spaces The number of spaces to prepend to the output. + * + * \see iso_tree_print_verbose + */ +typedef void (*print_dir_callback) (const struct iso_tree_node *dir, + void *data, + int spaces); +/** + * A function that prints verbose information about a file. + * + * \param dir The file about which to print information. + * \param data Unspecified function-dependent data. + * \param spaces The number of spaces to prepend to the output. + * + * \see iso_tree_print_verbose + */ +typedef void (*print_file_callback) (const struct iso_tree_node *file, + void *data, + int spaces); + +/** + * Recursively print a directory heirarchy. For each node in the directory + * heirarchy, call a callback function to print information more verbosely. + * + * \param root The root of the directory heirarchy to print. + * \param dir The callback function to call for each directory in the tree. + * \param file The callback function to call for each file in the tree. + * \param callback_data The data to pass to the callback functions. + * \param spaces The number of spaces to prepend to the output. + * + * \pre \p root is not NULL. + * \pre Neither of the callback functions modifies the directory heirarchy. + */ +void iso_tree_print_verbose(const struct iso_tree_node *root, + print_dir_callback dir, + print_file_callback file, + void *callback_data, + int spaces); + +#define ISO_ISDIR(n) (n->type == LIBISO_NODE_DIR) +#define ISO_ISREG(n) (n->type == LIBISO_NODE_FILE) +#define ISO_ISLNK(n) (n->type == LIBISO_NODE_SYMLINK) + +#endif /* LIBISO_TREE_H */ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/util.c b/libisofs/branches/ZeroTwoEight/libisofs/util.c new file mode 100755 index 00000000..c9e68a9a --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/util.c @@ -0,0 +1,842 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * Utility functions for the Libisofs library. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "libisofs.h" + +/* avoids warning and names in iso, joliet and rockridge can't be > 255 bytes + * anyway. There are at most 31 characters in iso level 1, 255 for rockridge, + * 64 characters (* 2 since UCS) for joliet. */ +#define NAME_BUFFER_SIZE 255 + +int div_up(int n, int div) +{ + return (n + div - 1) / div; +} + +int round_up(int n, int mul) +{ + return div_up(n, mul) * mul; +} + + +/** + * Convert a string between charsets. + * This assumes '\0' means end-of-string, what is not necessarily true, + * but given there are lots of strdup around there, it will fail in other + * places anyway... + */ +char * +convert_str(const char *str, const char *icharset, const char *ocharset) +{ + char *ret; + size_t inbytes; + size_t outbytes; + + inbytes = strlen(str); + outbytes = (inbytes+1) * MB_LEN_MAX; + { + /* ensure enought space */ + char out[outbytes]; + char *src; + size_t n; + + iconv_t conv = iconv_open(ocharset, icharset); + if (conv == (iconv_t)(-1)) { + printf("Can't convert from %s to %s\n", icharset, ocharset); + return NULL; + } + src = (char *)str; + ret = (char *)out; + + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + if (n == -1) { + /* error just return input stream */ + perror("Convert error."); + printf("Maybe string %s is not encoded in %s\n", str, icharset); + + iconv_close(conv); + return strdup(str); + } + iconv_close(conv); + *ret = '\0'; + + ret = strdup(out); + } + return ret; +} + +/** + * Convert a str in a specified codeset to WCHAR_T. + * The result must be free() when no more needed + */ +static wchar_t *str2wchar(const char *str, const char *codeset) +{ + iconv_t conv; + size_t inbytes; + size_t outbytes; + char *ret; + char *src; + wchar_t *wstr; + size_t n; + + conv = iconv_open("WCHAR_T", codeset); + if (conv == (iconv_t)-1) { + perror("Invalid encodings\n"); + return NULL; + } + + inbytes = strlen(str); + outbytes = (inbytes + 1) * sizeof(wchar_t); + + /* we are sure that numchars <= inbytes */ + wstr = malloc(outbytes); + ret = (char *)wstr; + src = (char *)str; + + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + while (n == -1) { + + if( errno != EINVAL ) { + /* error, should never occur */ + iconv_close(conv); + perror("Convert error\n"); + return NULL; + } + + /* invalid input string charset, just log and ignore */ + printf("String %s is not encoded in %s\n", str, codeset); + inbytes--; + + if(!inbytes) + break; + n = iconv(conv, &src, &inbytes, &ret, &outbytes); + } + iconv_close(conv); + + *( (wchar_t *)ret )='\0'; + return wstr; +} + +/* this function must always return a name + * since the caller never checks if a NULL + * is returned. It also avoids some warnings. */ +char *str2ascii(const char *src_arg, const char *icharset) +{ + wchar_t *wsrc_; + char *ret; + char *ret_; + char *src; + iconv_t conv; + size_t numchars; + size_t outbytes; + size_t inbytes; + size_t n; + + assert(icharset); + + if (!src_arg) + return NULL; + + /* 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. + * + * For now, just assume input to be in UTF-8, we can change + * this later. + */ + + wsrc_ = str2wchar(src_arg, icharset); + + if (!wsrc_) + return NULL; + + src = (char *)wsrc_; + numchars = wcslen(wsrc_); + + + inbytes = numchars * sizeof(wchar_t); + + ret_ = malloc(numchars+1); + outbytes = numchars; + ret = ret_; + + /* initialize iconv */ + conv = iconv_open("ASCII", "WCHAR_T"); + if (conv == (iconv_t)-1) { + free(wsrc_); + return NULL; + } + + 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_); + + return ret_; +} + +/* FIXME: C&P */ +uint16_t *str2ucs(const char *src_arg, const char *icharset) +{ + wchar_t *wsrc_; + char *src; + char *ret_; + char *ret; + iconv_t conv; + size_t outbytes; + size_t numchars; + size_t inbytes; + size_t n; + + if (!src_arg) + return calloc(2, 1); /* empty UCS string */ + + + /* 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. + */ + + wsrc_ = str2wchar(src_arg, icharset); + if (!wsrc_) + return calloc(2, 1); /* empty UCS string */ + src = (char*)wsrc_; + numchars = wcslen(wsrc_); + + inbytes = numchars * sizeof(wchar_t); + + outbytes = numchars * sizeof(uint16_t); + ret_ = malloc ((numchars+1) * sizeof(uint16_t)); + ret = ret_; + + /* initialize iconv */ + conv = iconv_open("UCS-2BE", "WCHAR_T"); + if (conv == (iconv_t)-1) + return calloc(2, 1); /* empty UCS string */ + + 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. */ + *((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 */ + *((uint16_t*) ret) = 0; + + return (uint16_t*)ret_; +} + +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 !(c < (uint16_t)' ' || c == (uint16_t)'*' || c == (uint16_t)'/' + || c == (uint16_t)':' || c == (uint16_t)';' + || c == (uint16_t)'?' || c == (uint16_t)'\\'); +} + +/* FIXME: where are these documented? */ +static int valid_p_char(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c == '.') || (c == '_') || (c == '-'); +} + +char *str2d_char(const char *str, const char *icharset) { + char *ret; + size_t len, i; + + if (!str) + return NULL; + + ret = str2ascii(str, icharset); + len = strlen(ret); + + for (i = 0; i < len; ++i) { + char c = toupper(ret[i]); + ret[i] = valid_d_char(c) ? c : '_'; + } + + return ret; +} + +char *str2a_char(const char *str, const char *icharset) { + char *ret; + size_t len, i; + + if (!str) + return NULL; + + ret = str2ascii(str, icharset); + len = strlen(ret); + + for (i = 0; i < len; ++i) { + char c = toupper(ret[i]); + ret[i] = valid_a_char(c) ? c : '_'; + } + + return ret; +} + +static char *iso_dirid(const char *src, int size, const char *icharset) +{ + char *ret = str2ascii(src, icharset); + size_t len, i; + + if (!ret) + return NULL; + + len = strlen(ret); + if (len > size) { + ret[size] = '\0'; + len = size; + } + for (i = 0; i < len; i++) { + char c = toupper(ret[i]); + ret[i] = valid_d_char(c) ? c : '_'; + } + + return ret; +} + +char *iso_1_dirid(const char *src, const char *icharset) +{ + return iso_dirid(src, 8, icharset); +} + +char *iso_2_dirid(const char *src, const char *icharset) +{ + return iso_dirid(src, 31, icharset); +} + +char *iso_r_dirid(const char *src, const char *icharset, int flags) +{ + char *ret = str2ascii(src, icharset); + size_t size, len, i; + + if (!ret) + return NULL; + + size = flags & ECMA119_37_CHAR_FILENAMES ? 37 : 31; + + len = strlen(ret); + if (len > size) { + ret[size] = '\0'; + len = size; + } + + if (flags & ECMA119_RELAXED_FILENAMES) + return ret; + + for (i = 0; i < len; i++) { + char c = toupper(ret[i]); + ret[i] = valid_d_char(c) ? c : '_'; + } + + return ret; +} + +char *iso_1_fileid(const char *src_arg, const char *icharset) +{ + char *src = str2ascii(src_arg, icharset); + char *dest; + char *dot; /* Position of the last dot in the + filename, will be used to calculate + lname and lext. */ + int lname, lext, pos, i; + + if (!src) + return NULL; + + dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2 + (;1) + 1 (\0) */ + 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) { + free(src); + free(dest); + 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 : '_'; + } + /* File versions are mandatory, even if they aren't used. */ + dest[pos++] = ';'; + dest[pos++] = '1'; + dest[pos] = '\0'; + dest = (char *)realloc(dest, pos + 1); + + free(src); + return dest; +} + +char *iso_2_fileid(const char *src_arg, const char *icharset) +{ + char *src = str2ascii(src_arg, icharset); + char *dest; + char *dot; + int lname, lext, lnname, lnext, pos, i; + + if (!src) + return NULL; + + dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2 + (;1) + 1 (\0) */ + 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) { + free(src); + free(dest); + 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++] = ';'; + dest[pos++] = '1'; + dest[pos] = '\0'; + dest = (char *)realloc(dest, pos + 1); + + free(src); + return dest; +} + +char * +iso_r_fileid(const char *src_arg, const char *icharset, int flag) +{ + char *src = str2ascii(src_arg, icharset); + char *dest; + char *dot; + int lname, lext, lnname, lnext, pos, i; + + size_t size = flag & (1<<1) ? 37 : 33; + + if (!src) + return NULL; + + dest = malloc(size+1); + + if (flag & ECMA119_RELAXED_FILENAMES) { + strncpy(dest, src, size); + dest[size] = '\0'; /* ensure 37 / 33 max length */ + pos = strlen(dest); + pos = pos < (size == 37 ? 37 : 31) ? pos : (size == 37 ? 37 : 31); + if ( !(flag & ECMA119_OMIT_VERSION_NUMBERS) ) { + dest[pos++] = ';'; + dest[pos++] = '1'; + } + dest[pos] = '\0'; + return dest; + } + + /* no relaxed filenames */ + dot = strrchr(src, '.'); + + size_t max = size == 37 ? 36 : 30; + /* 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 > max) ? max : lname; + lext = lnext = 0; + } else { + lext = strlen(dot + 1); + lname = strlen(src) - lext - 1; + lnext = (strlen(src) > max + 1 && lext > 3) + ? (lname < max - 3 ? max - lname : 3) : lext; + lnname = (strlen(src) > max +1) ? max - lnext : lname; + } + + if (lnname == 0 && lnext == 0) { + free(src); + free(dest); + 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 : '_'; + } + if ( !(flag & ECMA119_OMIT_VERSION_NUMBERS) ) { + dest[pos++] = ';'; + dest[pos++] = '1'; + } + dest[pos] = '\0'; + dest = (char *)realloc(dest, pos + 1); + + free(src); + return dest; +} + +char * +iso_p_fileid(const char *src, const char *icharset) +{ + char *ret = str2ascii(src, icharset); + size_t i, len; + + if (!ret) + return NULL; + len = strlen(ret); + for (i = 0; i < len; i++) { + if (!valid_p_char(ret[i])) { + ret[i] = (uint16_t)'_'; + } + } + return ret; +} + +uint16_t * +iso_j_id(const char *src_arg, const char *icharset) +{ + uint16_t *j_str = str2ucs(src_arg, icharset); + size_t len = ucslen(j_str); + size_t n; + + if (len > 128) { + j_str[128] = '\0'; + len = 128; + } + + for (n = 0; n < len; n++) + if (!valid_j_char(j_str[n])) + j_str[n] = '_'; + return j_str; +} + +void iso_lsb(uint8_t *buf, uint32_t num, int bytes) +{ + int i; + + assert(bytes <= 4); + + 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; + + assert(bytes <= 4); + + 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); +} + +void iso_datetime_7(unsigned char *buf, time_t t) +{ + static int tzsetup = 0; + int tzoffset; + struct tm tm; + + if (!tzsetup) { + tzset(); + tzsetup = 1; + } + + localtime_r(&t, &tm); + + 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; +#ifdef HAVE_TM_GMTOFF + tzoffset = tm.tm_gmtoff / 60 / 15; +#else + tzoffset = timezone / 60 / 15; +#endif + if (tzoffset > 52) + tzoffset -= 101; + buf[6] = tzoffset; +} + +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 mktime(&tm) - buf[6] * 60 * 60; +} + +void iso_datetime_17(unsigned char *buf, time_t t) +{ + static int tzsetup = 0; + static int tzoffset; + struct tm tm; + + if (t == (time_t) - 1) { + /* unspecified time */ + memset(buf, '0', 16); + buf[16] = 0; + } else { + if (!tzsetup) { + tzset(); + tzsetup = 1; + } + + localtime_r(&t, &tm); + + 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); +#ifdef HAVE_TM_GMTOFF + tzoffset = tm.tm_gmtoff / 60 / 15; +#else + tzoffset = timezone / 60 / 15; +#endif + if (tzoffset > 52) + tzoffset -= 101; + buf[16] = tzoffset; + } +} + +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 mktime(&tm) - buf[16] * 60 * 60; +} + +size_t ucslen(const uint16_t *str) +{ + int i; + + for (i=0; str[i]; i++) + ; + return i; +} + +/** + * 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; +} + +uint32_t iso_read_lsb(const uint8_t *buf, int bytes) +{ + int i; + uint32_t ret = 0; + + for (i=0; i +#include +#include + +#ifndef MAX +# define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef MIN +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +extern inline int div_up(int n, int div) +{ + return (n + div - 1) / div; +} + +extern inline int round_up(int n, int mul) +{ + return div_up(n, mul) * mul; +} + +char *convert_str(const char *str, const char *icharset, const char *ocharset); + +wchar_t *towcs(const char *); +char *str2ascii(const char*, const char *); +uint16_t *str2ucs(const char *, const char *); + +char *str2d_char(const char*, const char *); +char *str2a_char(const char*, const char *); + +/** + * Create a level 1 directory identifier. + */ +char *iso_1_dirid(const char *src, const char *); + +/** + * Create a level 2 directory identifier. + */ +char *iso_2_dirid(const char *src, const char *); + +/** + * Create a directory identifier with relaxed constraints + */ +char *iso_r_dirid(const char *src, const char *icharset, int flags); + +/** + * Create a level 1 file identifier that consists of a name, extension and + * version number. The resulting string will have a file name of maximum + * length 8, followed by a separator (.), an optional extension of maximum + * length 3, followed by a separator (;) and a version number (digit 1). + * @return NULL if the original name and extension both are of length 0. + */ +char *iso_1_fileid(const char *src, const char *); + +/** + * Create a level 2 file identifier that consists of a name, extension and + * version number. The combined file name and extension length will not exceed + * 30, the name and extension will be separated (.), and the extension will be + * followed by a separator (;) and a version number (digit 1). + * @return NULL if the original name and extension both are of length 0. + */ +char *iso_2_fileid(const char *src, const char *); + +/** + * Create a file identifier with relaxed constraints. + */ +char *iso_r_fileid(const char *src, const char *icharset, int flags); + +/** + * Create a Joliet file or directory identifier that consists of a name, + * extension and version number. The combined name and extension length will + * not exceed 128 bytes, the name and extension will be separated (.), + * and the extension will be followed by a separator (;) and a version number + * (digit 1). All characters consist of 2 bytes and the resulting string is + * NULL-terminated by a 2-byte NULL. Requires the locale to be set correctly. + * + * @param size will be set to the size (in bytes) of the identifier. + * @return NULL if the original name and extension both are of length 0 or the conversion from the current codeset to UCS-2BE is not available. + */ +uint16_t *iso_j_id(const char *src, const char *icharset); + +/** + * FIXME: what are the requirements for these next two? Is this for RR? + * + * Create a POSIX portable file name that consists of a name and extension. + * The resulting file name will not exceed 250 characters. + * @return NULL if the original name and extension both are of length 0. + */ +char *iso_p_fileid(const char *src, const char *); + +/** + * Create a POSIX portable directory name. + * The resulting directory name will not exceed 250 characters. + * @return NULL if the original name is of length 0. + */ +char *iso_p_dirid(const char *src); + +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); +uint32_t iso_read_bb(const uint8_t *buf, int bytes); + +/** Records the date/time into a 7 byte buffer (9.1.5) */ +void iso_datetime_7(uint8_t *buf, time_t t); + +/** Records the date/time into a 17 byte buffer (8.4.26.1) */ +void iso_datetime_17(uint8_t *buf, time_t t); + +time_t iso_datetime_read_7(const uint8_t *buf); +time_t iso_datetime_read_17(const uint8_t *buf); + +/** + * Like strlen, but for Joliet strings. + */ +size_t ucslen(const uint16_t *str); + +/** + * Like strcmp, but for Joliet strings. + */ +int ucscmp(const uint16_t *s1, const uint16_t *s2); + +#endif /* LIBISO_UTIL_H */ diff --git a/libisofs/branches/ZeroTwoEight/libisofs/volume.c b/libisofs/branches/ZeroTwoEight/libisofs/volume.c new file mode 100755 index 00000000..30e18dfd --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/volume.c @@ -0,0 +1,258 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set ts=8 sts=8 sw=8 noet : */ + +#include +#include +#include + +#include "libisofs.h" +#include "tree.h" +#include "util.h" +#include "volume.h" +#include "eltorito.h" + +struct iso_volset* +iso_volset_new(struct iso_volume *vol, const char *id) +{ + struct iso_volset *volset = calloc(1, sizeof(struct iso_volset)); + + volset->volset_size = 1; + volset->refcount = 1; + volset->volume = malloc(sizeof(void *)); + volset->volume[0] = vol; + volset->volset_id = strdup(id); + return volset; +} + +void +iso_volset_free(struct iso_volset *volset) +{ + if (--volset->refcount < 1) { + int i; + for (i = 0; i < volset->volset_size; i++) { + iso_volume_free(volset->volume[i]); + } + free(volset->volume); + free(volset->volset_id); + free(volset); + } +} + +struct iso_volume* +iso_volume_new(const char *volume_id, + const char *publisher_id, + const char *data_preparer_id) +{ + return iso_volume_new_with_root(volume_id, + publisher_id, + data_preparer_id, + NULL); +} + +struct iso_volume* +iso_volume_new_with_root(const char *volume_id, + const char *publisher_id, + const char *data_preparer_id, + struct iso_tree_node_dir *root) +{ + struct iso_volume *volume; + + volume = calloc(1, sizeof(struct iso_volume)); + volume->refcount = 1; + + volume->root = root ? root : iso_tree_new_root(); + + if (volume_id != NULL) + volume->volume_id = strdup(volume_id); + if (publisher_id != NULL) + volume->publisher_id = strdup(publisher_id); + if (data_preparer_id != NULL) + volume->data_preparer_id = strdup(data_preparer_id); + return volume; +} + +void +iso_volume_free(struct iso_volume *volume) +{ + /* Only free if no references are in use. */ + if (--volume->refcount < 1) { + iso_tree_free( (struct iso_tree_node*) volume->root); + + free(volume->volume_id); + free(volume->publisher_id); + free(volume->data_preparer_id); + free(volume->system_id); + free(volume->application_id); + free(volume->copyright_file_id); + free(volume->abstract_file_id); + free(volume->biblio_file_id); + if (volume->bootcat) + el_torito_boot_catalog_free(volume->bootcat); + free(volume); + } +} + +struct iso_tree_node_dir * +iso_volume_get_root(const struct iso_volume *volume) +{ + return volume->root; +} + +void iso_volume_set_volume_id(struct iso_volume *volume, + const char *volume_id) +{ + free(volume->volume_id); + volume->volume_id = strdup(volume_id); +} + +void iso_volume_set_publisher_id(struct iso_volume *volume, + const char *publisher_id) +{ + free(volume->publisher_id); + volume->publisher_id = strdup(publisher_id); +} + +void iso_volume_set_data_preparer_id(struct iso_volume *volume, + const char *data_preparer_id) +{ + free(volume->data_preparer_id); + volume->data_preparer_id = strdup(data_preparer_id); +} + +void iso_volume_set_system_id(struct iso_volume *volume, + const char *system_id) +{ + free(volume->system_id); + volume->system_id = strdup(system_id); +} + +void iso_volume_set_application_id(struct iso_volume *volume, + const char *application_id) +{ + free(volume->application_id); + volume->application_id = strdup(application_id); +} + +void iso_volume_set_copyright_file_id(struct iso_volume *volume, + const char *copyright_file_id) +{ + free(volume->copyright_file_id); + volume->copyright_file_id = strdup(copyright_file_id); +} + +void iso_volume_set_abstract_file_id(struct iso_volume *volume, + const char *abstract_file_id) +{ + free(volume->abstract_file_id); + volume->abstract_file_id = strdup(abstract_file_id); +} + +void iso_volume_set_biblio_file_id(struct iso_volume *volume, + const char *biblio_file_id) +{ + free(volume->biblio_file_id); + volume->biblio_file_id = strdup(biblio_file_id); +} + +struct iso_tree_node * +iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path) +{ + struct iso_tree_node *node; + struct iso_tree_node_dir *dir; + char *ptr, *brk_info, *component; + + /* get the first child at the root of the volume + * that is "/" */ + dir = iso_volume_get_root(volume); + node = (struct iso_tree_node *)dir; + if (!strcmp(path, "/")) + return node; + + if (!dir->nchildren) + return NULL; + + ptr = strdup(path); + + /* get the first component of the path */ + component=strtok_r(ptr, "/", &brk_info); + while (component) { + size_t max; + size_t i; + + if ( !ISO_ISDIR(node) ) { + node=NULL; + break; + } + dir = (struct iso_tree_node_dir *)node; + + /* search among all the children of this directory if this path component exists */ + max=dir->nchildren; + for (i=0; i < max; i++) { + if (!strcmp(component, dir->children[i]->name)) { + node=dir->children[i]; + break; + } + } + + /* see if a node could be found */ + if (i==max) { + node=NULL; + break; + } + + component=strtok_r(NULL, "/", &brk_info); + } + + free(ptr); + return node; +} + +// +//struct iso_tree_node * +//iso_tree_volume_add_path(struct iso_volume *volume, +// const char *disc_path, +// const char *path) +//{ +// char *tmp; +// struct iso_tree_node *node; +// struct iso_tree_node *parent_node; +// +// tmp=strdup(disc_path); +// parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp)); +// free(tmp); +// +// if (!parent_node) +// return NULL; +// +// node = iso_tree_radd_dir(parent_node, path); +// if (!node) +// return NULL; +// +// tmp=strdup(disc_path); +// iso_tree_node_set_name(node, basename(tmp)); +// free(tmp); +// +// return node; +//} +// +//struct iso_tree_node * +//iso_tree_volume_add_new_dir(struct iso_volume *volume, +// const char *disc_path) +//{ +// char *tmp; +// struct iso_tree_node *node; +// struct iso_tree_node *parent_node; +// +// tmp=strdup(disc_path); +// parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp)); +// free(tmp); +// +// if (!parent_node) +// return NULL; +// +// tmp=strdup(disc_path); +// node = iso_tree_add_new_dir(parent_node, basename(tmp)); +// free(tmp); +// +// return node; +//} diff --git a/libisofs/branches/ZeroTwoEight/libisofs/volume.h b/libisofs/branches/ZeroTwoEight/libisofs/volume.h new file mode 100755 index 00000000..7603c0df --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/libisofs/volume.h @@ -0,0 +1,53 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet sts=8 ts=8 sw=8 : */ + +/** + * Extra declarations for use with the iso_volume structure. + */ + +#ifndef LIBISO_VOLUME_H +#define LIBISO_VOLUME_H + +#include "libisofs.h" + +/** + * Data volume. + */ +struct iso_volume +{ + int refcount; /**< Number of used references to this + volume. */ + + struct iso_tree_node_dir *root; /**< Root of the directory tree for the + volume. */ + + 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; + + struct el_torito_boot_catalog *bootcat; /**< El-Torito boot catalog */ +}; + +/** + * A set of data volumes. + */ +struct iso_volset +{ + int refcount; + + struct iso_volume **volume; /**< The volumes belonging to this + volume set. */ + int volset_size; /**< The number of volumes in this + volume set. */ + + char *volset_id; /**< The id of this volume set, encoded + in the current locale. */ +}; + +#endif /* __ISO_VOLUME */ diff --git a/libisofs/branches/ZeroTwoEight/test.sh b/libisofs/branches/ZeroTwoEight/test.sh new file mode 100644 index 00000000..67b6df39 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +TEST_ROOT=/tmp/libisofs_test + +rm -rf $TEST_ROOT + +#create test folders +mkdir -p $TEST_ROOT +mkdir -p $TEST_ROOT/dir1/dir11 +chmod 755 $TEST_ROOT/dir1 +chmod 755 $TEST_ROOT/dir1/dir11 + +touch $TEST_ROOT/dir1/dir11/a +echo "This file is to check correct file permissions. set them to 754" > $TEST_ROOT/dir1/permtest +chmod 754 $TEST_ROOT/dir1/permtest + +mkdir -p $TEST_ROOT/dir2 +ln -s $TEST_ROOT/dir1 "$TEST_ROOT/link to dir1" + +echo "README file" > $TEST_ROOT/README +chmod 555 $TEST_ROOT/README +ln -s $TEST_ROOT/README "$TEST_ROOT/link to readme" + +echo "No read file" > $TEST_ROOT/no_read +chmod 000 $TEST_ROOT/no_read + +if ! make check +then + exit 1 +fi + +test/test + diff --git a/libisofs/branches/ZeroTwoEight/test/Makefile b/libisofs/branches/ZeroTwoEight/test/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libisofs/branches/ZeroTwoEight/test/iso.c b/libisofs/branches/ZeroTwoEight/test/iso.c new file mode 100644 index 00000000..da3d12df --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/iso.c @@ -0,0 +1,141 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set ts=8 sts=8 sw=8 noet : */ + +#define _GNU_SOURCE + +#include "libisofs.h" +#include "libburn/libburn.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SECSIZE 2048 + +const char * const optstring = "JRL:b:h"; +extern char *optarg; +extern int optind; + +void usage() +{ + printf("test [OPTIONS] DIRECTORY OUTPUT\n"); +} + +void help() +{ + printf( +"Options:\n" +" -J Add Joliet support\n" +" -R Add Rock Ridge support\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 main(int argc, char **argv) +{ + struct iso_volset *volset; + struct iso_volume *volume; + struct iso_tree_node_dir *root; + struct burn_source *src; + unsigned char buf[2048]; + FILE *fd; + int c; + struct iso_tree_radd_dir_behavior behav = {0,0,0}; + int level=1, flags=0; + char *boot_img = NULL; + + while ((c = getopt(argc, argv, optstring)) != -1) { + switch(c) { + case 'h': + usage(); + help(); + exit(0); + break; + case 'J': + flags |= ECMA119_JOLIET; + break; + case 'R': + flags |= ECMA119_ROCKRIDGE; + break; + case 'L': + level = atoi(optarg); + break; + case 'b': + boot_img = optarg; + break; + case '?': + usage(); + exit(1); + break; + } + } + + if (argc < 2) { + printf ("Please pass directory from which to build ISO\n"); + usage(); + return 1; + } + if (argc < 3) { + printf ("Please supply output file\n"); + usage(); + return 1; + } + fd = fopen(argv[optind+1], "w"); + if (!fd) { + err(1, "error opening output file"); + } + + root = iso_tree_new_root(); + iso_tree_radd_dir(root, argv[optind], &behav); + if (!root) { + err(1, "error opening input directory"); + } + volume = iso_volume_new_with_root( "VOLID", "PUBID", "PREPID", root ); + + if ( boot_img ) { + /* adds El-Torito boot info. Tunned for isolinux */ + struct iso_tree_node_dir *boot = (struct iso_tree_node_dir *) + iso_tree_volume_path_to_node(volume, "isolinux"); + struct iso_tree_node *img = iso_tree_volume_path_to_node(volume, boot_img); + if (!img) { + err(1, "boot image patch is not valid"); + } + struct el_torito_boot_image *bootimg = + iso_volume_create_boot_catalog(volume, img, ELTORITO_NO_EMUL, + boot, "boot.cat"); + el_torito_set_load_size(bootimg, 4); + el_torito_set_write_boot_info(bootimg); + } + + volset = iso_volset_new( volume, "VOLSETID" ); + + /* some tests */ + iso_volume_set_application_id(volume, "Libburnia"); + iso_volume_set_copyright_file_id(volume, "LICENSE"); + + int constraints = ECMA119_OMIT_VERSION_NUMBERS | + ECMA119_37_CHAR_FILENAMES | ECMA119_NO_DIR_REALOCATION | + ECMA119_RELAXED_FILENAMES; + + struct ecma119_source_opts opts = {0, level, flags, constraints, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, "UTF-8", "UTF-8"}; + + src = iso_source_new_ecma119(volset, &opts); + + while (src->read(src, buf, 2048) == 2048) { + fwrite(buf, 1, 2048, fd); + } + fclose(fd); + + return 0; +} diff --git a/libisofs/branches/ZeroTwoEight/test/iso.py b/libisofs/branches/ZeroTwoEight/test/iso.py new file mode 100644 index 00000000..61da5fd7 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/iso.py @@ -0,0 +1,297 @@ + +import struct +import tree +import sys + +voldesc_fmt = "B" "5s" "B" "2041x" + +# all these fields are common between the pri and sec voldescs +privoldesc_fmt = "B" "5s" "B" "x" "32s" "32s" "8x" "8s" "32x" "4s" "4s" "4s" "8s" "4s4s" "4s4s" "34s" "128s" \ + "128s" "128s" "128s" "37s" "37s" "37s" "17s" "17s" "17s" "17s" "B" "x" "512s" "653x" + +# the fields unique to the sec_vol_desc +secvoldesc_fmt = "x" "5x" "x" "B" "32x" "32x" "8x" "8x" "32s" "4x" "4x" "4x" "8x" "4x4x" "4x4x" "34x" "128x" \ + "128x" "128x" "128x" "37x" "37x" "37x" "17x" "17x" "17x" "17x" "x" "x" "512x" "653x" + +dirrecord_fmt = "B" "B" "8s" "8s" "7s" "B" "B" "B" "4s" "B" # + file identifier, padding field and SU area + +pathrecord_fmt = "B" "B" "4s" "2s" # + directory identifier and padding field + +def read_bb(str, le, be): + val1, = struct.unpack(le, str) + val2, = struct.unpack(be, str) + if val1 != val2: + print "val1=%d, val2=%d" % (val1, val2) + raise AssertionError, "values are not equal in dual byte-order field" + return val1 + +def read_bb4(str): + return read_bb(str, "4xI") + +def read_bb2(str): + return read_bb(str, "2xH") + +def read_lsb4(str): + return struct.unpack("I", str)[0] + +def read_msb2(str): + return struct.unpack(">H", str)[0] + +class VolDesc(object): + def __init__(self, data): + print "fmt len=%d, data len=%d" % ( struct.calcsize(voldesc_fmt), len(data) ) + self.vol_desc_type, self.standard_id, self.vol_desc_version = struct.unpack(voldesc_fmt, data) + +class PriVolDesc(VolDesc): + def __init__(self, data): + self.vol_desc_type, \ + self.standard_id, \ + self.vol_desc_version, \ + self.system_id, \ + self.volume_id, \ + self.vol_space_size, \ + self.vol_set_size, \ + self.vol_seq_num, \ + self.block_size, \ + self.path_table_size, \ + self.l_table_pos, \ + self.l_table2_pos, \ + self.m_table_pos, \ + self.m_table2_pos, \ + self.root_record, \ + self.volset_id, \ + self.publisher_id, \ + self.preparer_id, \ + self.application_id, \ + self.copyright_file, \ + self.abstract_file, \ + self.bibliographic_file, \ + self.creation_timestamp, \ + self.modification_timestamp, \ + self.expiration_timestamp, \ + self.effective_timestamp, \ + self.file_struct_version, \ + self.application_use = struct.unpack(privoldesc_fmt, data) + + # take care of reading the integer types + self.vol_space_size = read_bb4(self.vol_space_size) + self.vol_set_size = read_bb2(self.vol_set_size) + self.vol_seq_num = read_bb2(self.vol_seq_num) + self.block_size = read_bb2(self.block_size) + self.path_table_size = read_bb4(self.path_table_size) + self.l_table_pos = read_lsb4(self.l_table_pos) + self.l_table2_pos = read_lsb4(self.l_table2_pos) + self.m_table_pos = read_msb4(self.m_table_pos) + self.m_table2_pos = read_msb4(self.m_table2_pos) + + # parse the root directory record + self.root_record = DirRecord(self.root_record) + + def readPathTables(self, file): + file.seek( self.block_size * self.l_table_pos ) + self.l_table = PathTable( file.read(self.path_table_size), 0 ) + file.seek( self.block_size * self.m_table_pos ) + self.m_table = PathTable( file.read(self.path_table_size), 1 ) + + if self.l_table2_pos: + file.seek( self.block_size * self.l_table2_pos ) + self.l_table2 = PathTable( file.read(self.path_table_size), 0 ) + else: + self.l_table2 = None + + if self.m_table2_pos: + file.seek( self.block_size * self.m_table2_pos ) + self.m_table2 = PathTable( file.read(self.path_table_size), 1 ) + else: + self.m_table2 = None + + def toTree(self, isofile): + ret = tree.Tree(isofile=isofile.name) + ret.root = self.root_record.toTreeNode(parent=None, isofile=isofile) + return ret + +class SecVolDesc(PriVolDesc): + def __init__(self, data): + super(SecVolDesc,self).__init__(data) + self.flags, self.escape_sequences = struct.unpack(secvoldesc_fmt, data) + +# return a single volume descriptor of the appropriate type +def readVolDesc(data): + desc = VolDesc(data) + if desc.standard_id != "CD001": + print "Unexpected standard_id " +desc.standard_id + return None + if desc.vol_desc_type == 1: + return PriVolDesc(data) + elif desc.vol_desc_type == 2: + return SecVolDesc(data) + elif desc.vol_desc_type == 3: + print "I don't know about partitions yet!" + return None + elif desc.vol_desc_type == 255: + return desc + else: + print "Unknown volume descriptor type %d" % (desc.vol_desc_type,) + return None + +def readVolDescSet(file): + ret = [ readVolDesc(file.read(2048)) ] + while ret[-1].vol_desc_type != 255: + ret.append( readVolDesc(file.read(2048)) ) + + for vol in ret: + if vol.vol_desc_type == 1 or vol.vol_desc_type == 2: + vol.readPathTables(file) + + return ret + +class DirRecord: + def __init__(self, data): + self.len_dr, \ + self.len_xa, \ + self.block, \ + self.len_data, \ + self.timestamp, \ + self.flags, \ + self.unit_size, \ + self.gap_size, \ + self.vol_seq_number, \ + self.len_fi = struct.unpack(dirrecord_fmt, data[:33]) + self.children = [] + + if self.len_dr > len(data): + raise AssertionError, "Error: not enough data to read in DirRecord()" + elif self.len_dr < 34: + raise AssertionError, "Error: directory record too short" + + fmt = str(self.len_fi) + "s" + if self.len_fi % 2 == 0: + fmt += "1x" + len_su = self.len_dr - (33 + self.len_fi + 1 - (self.len_fi % 2)) + fmt += str(len_su) + "s" + + if len(data) >= self.len_dr: + self.file_id, self.su = struct.unpack(fmt, data[33 : self.len_dr]) + else: + print "Error: couldn't read file_id: not enough data" + self.file_id = "BLANK" + self.su = "" + + # convert to integers + self.block = read_bb4(self.block) + self.len_data = read_bb4(self.len_data) + self.vol_seq_number = read_bb2(self.vol_seq_number) + + def toTreeNode(self, parent, isofile, path=""): + ret = tree.TreeNode(parent=parent, isofile=isofile.name) + if len(path) > 0: + path += "/" + path += self.file_id + ret.path = path + + if self.flags & 2: # we are a directory, recurse + isofile.seek( 2048 * self.block ) + data = isofile.read( self.len_data ) + pos = 0 + while pos < self.len_data: + try: + child = DirRecord( data[pos:] ) + pos += child.len_dr + if child.len_fi == 1 and (child.file_id == "\x00" or child.file_id == "\x01"): + continue + print "read child named " +child.file_id + self.children.append( child ) + ret.children.append( child.toTreeNode(ret, isofile, path) ) + except AssertionError: + print "Couldn't read child of directory %s, position is %d, len is %d" % \ + (path, pos, self.len_data) + raise + + return ret + +class PathTableRecord: + def __init__(self, data, readint2, readint4): + self.len_di, self.len_xa, self.block, self.parent_number = struct.unpack(pathrecord_fmt, data[:8]) + + if len(data) < self.len_di + 8: + raise AssertionError, "Error: not enough data to read path table record" + + fmt = str(self.len_di) + "s" + self.dir_id, = struct.unpack(fmt, data[8:8+self.len_di]) + + self.block = readint4(self.block) + self.parent_number = readint2(self.parent_number) + +class PathTable: + def __init__(self, data, m_type): + if m_type: + readint2 = read_msb2 + readint4 = read_msb4 + else: + readint2 = read_lsb2 + readint4 = read_lsb4 + pos = 0 + self.records = [] + while pos < len(data): + try: + self.records.append( PathTableRecord(data[pos:], readint2, readint4) ) + print "Read path record %d: dir_id %s, block %d, parent_number %d" %\ + (len(self.records), self.records[-1].dir_id, self.records[-1].block, self.records[-1].parent_number) + pos += self.records[-1].len_di + 8 + pos += pos % 2 + except AssertionError: + print "Last successfully read path table record had dir_id %s, block %d, parent_number %d" % \ + (self.records[-1].dir_id, self.records[-1].block, self.records[-1].parent_number) + print "Error was near offset %x" % (pos,) + raise + + def findRecord(self, dir_id, block, parent_number): + number=1 + for record in self.records: + if record.dir_id == dir_id and record.block == block and record.parent_number == parent_number: + return number, record + number += 1 + + return None, None + + # check this path table for consistency against the actual directory heirarchy + def crossCheckDirRecords(self, root, parent_number=1): + number, rec = self.findRecord(root.file_id, root.block, parent_number) + + if not rec: + print "Error: directory record parent_number %d, dir_id %s, block %d doesn't match a path table record" % \ + (parent_number, root.file_id, root.block) + parent = self.records[parent_number] + print "Parent has parent_number %d, dir_id %s, block %d" % (parent.parent_number, parent.dir_id, parent.block) + return 0 + + for child in root.children: + if child.flags & 2: + self.crossCheckDirRecords(child, number) + + +if len(sys.argv) != 2: + print "Please enter the name of the .iso file to open" + sys.exit(1) + +f = file(sys.argv[1]) +f.seek(2048 * 16) # system area +volumes = readVolDescSet(f) +vol = volumes[0] +t = vol.toTree(f) +vol.l_table.crossCheckDirRecords(vol.root_record) +vol.m_table.crossCheckDirRecords(vol.root_record) + +vol = volumes[1] +try: + t = vol.toTree(f) + vol.l_table.crossCheckDirRecords(vol.root_record) + vol.m_table.crossCheckDirRecords(vol.root_record) +except AttributeError: + pass diff --git a/libisofs/branches/ZeroTwoEight/test/test.c b/libisofs/branches/ZeroTwoEight/test/test.c new file mode 100644 index 00000000..dd546c7a --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/test.c @@ -0,0 +1,28 @@ +#include "test.h" + +static void create_test_suite() +{ + add_util_suite(); + add_tree_suite(); + add_exclude_suite(); + add_file_hashtable_suite(); + add_ecma119_tree_suite(); + add_volume_suite(); +} + +int main(int argc, char **argv) +{ + CU_pSuite pSuite = NULL; + + /* 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/ZeroTwoEight/test/test.h b/libisofs/branches/ZeroTwoEight/test/test.h new file mode 100644 index 00000000..7ac76f89 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/test.h @@ -0,0 +1,24 @@ +#ifndef TEST_H_ +#define TEST_H_ + +#include +#include +#include + +#include + +#include "libisofs.h" + +void add_tree_suite(); + +void add_exclude_suite(); + +void add_file_hashtable_suite(); + +void add_util_suite(); + +void add_ecma119_tree_suite(); + +void add_volume_suite(); + +#endif /*TEST_H_*/ diff --git a/libisofs/branches/ZeroTwoEight/test/test_ecma119_tree.c b/libisofs/branches/ZeroTwoEight/test/test_ecma119_tree.c new file mode 100644 index 00000000..a3bb2ff2 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/test_ecma119_tree.c @@ -0,0 +1,34 @@ +/* + * Unit test for ecma119_tree.h + */ +//FIXME not implemented yet!! + +#include "libisofs.h" +#include "tree.h" +#include "test.h" +//#include "ecma119_tree.h" + +/* + * Also including C file, testing internal functions + */ +//#include "ecma119_tree.c" + +#include +#include +#include +#include +#include +#include + + +static void test_calc_dirent_len() +{ + +} + +void add_ecma119_tree_suite() +{ + CU_pSuite pSuite = CU_add_suite("Ecma119TreeSuite", NULL, NULL); + + //CU_add_test(pSuite, "test of calc_dirent_len()", test_calc_dirent_len); +} diff --git a/libisofs/branches/ZeroTwoEight/test/test_exclude.c b/libisofs/branches/ZeroTwoEight/test/test_exclude.c new file mode 100644 index 00000000..1102b0f7 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/test_exclude.c @@ -0,0 +1,33 @@ +/* + * Unit test for exclude.h + */ + + +#include "exclude.h" +#include "test.h" + +#include +#include + +static void test_exclude() +{ + struct iso_hash_table table = { {0,}, 0}; + CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/dir") ); + CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/otherdir") ); + iso_exclude_add_path(&table, "/otherdir"); + CU_ASSERT_TRUE( iso_exclude_lookup(&table, "/otherdir") ); + CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/dir") ); + iso_exclude_add_path(&table, "/dir"); + CU_ASSERT_TRUE( iso_exclude_lookup(&table, "/otherdir") ); + CU_ASSERT_TRUE( iso_exclude_lookup(&table, "/dir") ); + iso_exclude_empty(&table); + CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/dir") ); + CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/otherdir") ); +} + +void add_exclude_suite() +{ + CU_pSuite pSuite = CU_add_suite("ExcludeSuite", NULL, NULL); + + CU_add_test(pSuite, "test of exclude", test_exclude); +} diff --git a/libisofs/branches/ZeroTwoEight/test/test_file_hashtable.c b/libisofs/branches/ZeroTwoEight/test/test_file_hashtable.c new file mode 100644 index 00000000..d95fb80d --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/test_file_hashtable.c @@ -0,0 +1,248 @@ +/* + * Unit test for file.h + */ + +#include "test.h" +#include "file.h" +#include "tree.h" + +#include +#include +#include +#include + +static void test_iso_file_new() +{ + struct iso_tree_node_file *file; + struct iso_file *iso; + + file = calloc(1, sizeof(struct iso_tree_node_file) ); + file->node.name = "fileName"; + file->node.attrib.st_size = 12; + file->node.attrib.st_dev = 15; + file->node.attrib.st_ino = 204; + file->path = "/tmp/filename"; + file->sort_weight = 1; + + iso = iso_file_new(file); + + CU_ASSERT_PTR_NOT_NULL(iso); + CU_ASSERT_STRING_EQUAL(iso->path, "/tmp/filename"); + CU_ASSERT_EQUAL(iso->size, 12); + CU_ASSERT_EQUAL(iso->ino, 0); + CU_ASSERT_EQUAL(iso->nlink, 1); + CU_ASSERT_EQUAL(iso->sort_weight, 1); + CU_ASSERT_EQUAL(iso->real_dev, 15); + CU_ASSERT_EQUAL(iso->real_ino, 204); +} + +static void test_add_lookup() +{ + struct iso_file_table *table; + struct iso_tree_node_file *file1; + struct iso_tree_node_file *file2; + struct iso_file *iso1; + struct iso_file *iso2; + struct iso_file *iso3; + int r; + + table = iso_file_table_new(1); + + CU_ASSERT_PTR_NOT_NULL( table ); + CU_ASSERT_TRUE( table->cache_inodes ); + CU_ASSERT_EQUAL(table->count, 0); + + file1 = calloc(1, sizeof(struct iso_tree_node_file) ); + file1->node.name = "fileName"; + file1->node.attrib.st_dev = 15; + file1->node.attrib.st_ino = 204; + file1->path = "/tmp/filename"; + + iso1 = iso_file_new(file1); + + r = iso_file_table_add_file(table, iso1); + CU_ASSERT_EQUAL(r, 1); + CU_ASSERT_EQUAL(table->count, 1); + + iso2 = iso_file_table_lookup(table, file1); + CU_ASSERT_PTR_NOT_NULL(iso2); + CU_ASSERT_PTR_EQUAL(iso2, iso1); + + file2 = calloc(1, sizeof(struct iso_tree_node_file) ); + file2->node.name = "fileName2"; + file2->node.attrib.st_dev = 152; + file2->node.attrib.st_ino = 2042; + file2->path = "/tmp/filename2"; + + iso3 = iso_file_new(file2); + r = iso_file_table_add_file(table, iso3); + CU_ASSERT_EQUAL(r, 1); + CU_ASSERT_EQUAL(table->count, 2); + + /* treat to add the same file again */ + r = iso_file_table_add_file(table, iso3); + CU_ASSERT_EQUAL(r, 0); + CU_ASSERT_EQUAL(table->count, 2); + + iso2 = iso_file_table_lookup(table, file1); + CU_ASSERT_PTR_NOT_NULL(iso2); + CU_ASSERT_PTR_EQUAL(iso2, iso1); + + iso2 = iso_file_table_lookup(table, file2); + CU_ASSERT_PTR_NOT_NULL(iso2); + CU_ASSERT_PTR_EQUAL(iso2, iso3); + + iso3 = iso_file_new(file2); + r = iso_file_table_add_file(table, iso3); + CU_ASSERT_EQUAL(r, 0); + CU_ASSERT_EQUAL(table->count, 2); + + iso_file_table_clear(table); + CU_ASSERT_EQUAL(table->count, 0); + + iso2 = iso_file_table_lookup(table, file2); + CU_ASSERT_PTR_NULL(iso2); + + free( file1 ); + free( file2 ); + free( table ); +} + +static void test_cache_inodes() +{ + struct iso_file_table *table; + struct iso_tree_node_file *file1; + struct iso_tree_node_file *file2; + struct iso_file *iso1; + struct iso_file *iso2; + struct iso_file *iso3; + int r; + + table = iso_file_table_new(1); + + CU_ASSERT_PTR_NOT_NULL( table ); + CU_ASSERT_TRUE( table->cache_inodes ); + CU_ASSERT_EQUAL(table->count, 0); + + file1 = calloc(1, sizeof(struct iso_tree_node_file) ); + file1->node.name = "fileName"; + file1->node.attrib.st_dev = 15; + file1->node.attrib.st_ino = 204; + file1->path = "/tmp/filename"; + + iso1 = iso_file_new(file1); + + r = iso_file_table_add_file(table, iso1); + CU_ASSERT_EQUAL(r, 1); + + /* another file, different but with the same inode id */ + file2 = calloc(1, sizeof(struct iso_tree_node_file) ); + file2->node.name = "another file"; + file2->node.attrib.st_dev = 15; + file2->node.attrib.st_ino = 204; + file2->path = "/tmp/another"; + iso2 = iso_file_new(file2); + + /* ensure it's not added again... */ + r = iso_file_table_add_file(table, iso2); + CU_ASSERT_EQUAL(r, 0); + + /* ...and the lookup returns the first */ + iso3 = iso_file_table_lookup(table, file2); + CU_ASSERT_PTR_EQUAL(iso1, iso3); + + free(iso2); + free(file2); + + /* and now a file with same inode but different device */ + file2 = calloc(1, sizeof(struct iso_tree_node_file) ); + file2->node.name = "different file"; + file2->node.attrib.st_dev = 16; /* different dev id */ + file2->node.attrib.st_ino = 204; + file2->path = "/tmp/different"; + iso2 = iso_file_new(file2); + + r = iso_file_table_add_file(table, iso2); + CU_ASSERT_EQUAL(r, 1); + iso3 = iso_file_table_lookup(table, file2); + CU_ASSERT_PTR_NOT_EQUAL(iso3, iso1); + CU_ASSERT_PTR_EQUAL(iso3, iso2); + + iso_file_table_clear(table); + free( file1 ); + free( file2 ); + free( table ); +} + +static void test_no_cache_inodes() +{ + struct iso_file_table *table; + struct iso_tree_node_file *file1; + struct iso_tree_node_file *file2; + struct iso_tree_node_file *file3; + struct iso_file *iso1; + struct iso_file *iso2; + struct iso_file *iso3; + int r; + + table = iso_file_table_new(0); + + CU_ASSERT_PTR_NOT_NULL( table ); + CU_ASSERT_FALSE( table->cache_inodes ); + CU_ASSERT_EQUAL(table->count, 0); + + file1 = calloc(1, sizeof(struct iso_tree_node_file) ); + file1->node.name = "fileName"; + file1->node.attrib.st_dev = 15; + file1->node.attrib.st_ino = 204; + file1->path = "/tmp/filename"; + + iso1 = iso_file_new(file1); + + r = iso_file_table_add_file(table, iso1); + CU_ASSERT_EQUAL(r, 1); + + /* another file, different but with the same inode id */ + file2 = calloc(1, sizeof(struct iso_tree_node_file) ); + file2->node.name = "another file"; + file2->node.attrib.st_dev = 15; + file2->node.attrib.st_ino = 204; + file2->path = "/tmp/another"; + iso2 = iso_file_new(file2); + + /* ensure is added */ + r = iso_file_table_add_file(table, iso2); + CU_ASSERT_EQUAL(r, 1); + + iso3 = iso_file_table_lookup(table, file2); + CU_ASSERT_PTR_EQUAL(iso3, iso2); + + /* and now a file with same inode and path */ + file3 = calloc(1, sizeof(struct iso_tree_node_file) ); + file3->node.name = "different file"; + file3->node.attrib.st_dev = 15; + file3->node.attrib.st_ino = 204; + file3->path = "/tmp/filename"; + iso3 = iso_file_new(file3); + + r = iso_file_table_add_file(table, iso3); + CU_ASSERT_EQUAL(r, 0); + iso3 = iso_file_table_lookup(table, file3); + CU_ASSERT_PTR_EQUAL(iso3, iso1); + + iso_file_table_clear(table); + free(file1); + free(file2); + free(file3); + free(table); +} + + +void add_file_hashtable_suite() +{ + CU_pSuite pSuite = CU_add_suite("FileHashtableSuite", NULL, NULL); + CU_add_test(pSuite, "test of iso_file_new()", test_iso_file_new); + CU_add_test(pSuite, "test of add and lookup", test_add_lookup); + CU_add_test(pSuite, "test with cache_inodes", test_cache_inodes); + CU_add_test(pSuite, "test without cache_inodes", test_no_cache_inodes); +} diff --git a/libisofs/branches/ZeroTwoEight/test/test_tree.c b/libisofs/branches/ZeroTwoEight/test/test_tree.c new file mode 100644 index 00000000..0debaec8 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/test_tree.c @@ -0,0 +1,395 @@ +/* + * Unit test for tree.h + */ + + +#include "libisofs.h" +#include "tree.h" +#include "test.h" + +#include +#include +#include +#include +#include +#include + + +static void test_new_root() { + struct iso_tree_node_dir *root; + root = iso_tree_new_root(); + CU_ASSERT_PTR_NOT_NULL(root); + CU_ASSERT_EQUAL(root->nchildren, 0); + CU_ASSERT_PTR_NULL(root->children); + CU_ASSERT_PTR_NULL(root->node.parent); + CU_ASSERT_PTR_NULL(root->node.name); + CU_ASSERT(S_ISDIR(root->node.attrib.st_mode) ); +} + +static void test_add_dir() { + struct iso_tree_node_dir *root; + struct iso_tree_node_dir *dir; + + root = iso_tree_new_root(); + CU_ASSERT_PTR_NOT_NULL(root); + dir = iso_tree_add_dir(root, "New dir name"); + CU_ASSERT_PTR_NOT_NULL(root); + CU_ASSERT_PTR_NOT_NULL(dir); + CU_ASSERT_PTR_EQUAL(dir->node.parent, root); + CU_ASSERT_EQUAL(root->nchildren, 1); + CU_ASSERT_PTR_EQUAL(root->children[0], (struct iso_tree_node *)dir); + CU_ASSERT_STRING_EQUAL( dir->node.name, "New dir name"); + CU_ASSERT( S_ISDIR(dir->node.attrib.st_mode) ); + iso_tree_free((struct iso_tree_node *)root); +} + +static void test_add_file() { + struct iso_tree_node_dir *root; + struct iso_tree_node_file *file; + + root = iso_tree_new_root(); + file = (struct iso_tree_node_file *) + iso_tree_add_file(root, "/tmp/libisofs_test/README"); + CU_ASSERT_PTR_NOT_NULL(root); + CU_ASSERT_PTR_NOT_NULL(file); + CU_ASSERT_PTR_EQUAL(file->node.parent, root); + CU_ASSERT_EQUAL(root->nchildren, 1); + CU_ASSERT_PTR_EQUAL(root->children[0], (struct iso_tree_node *)file); + CU_ASSERT_STRING_EQUAL( file->node.name, "README" ); + CU_ASSERT_STRING_EQUAL( file->path, "/tmp/libisofs_test/README" ); + CU_ASSERT( S_ISREG(file->node.attrib.st_mode) ); + iso_tree_free((struct iso_tree_node *)root); +} + +static void test_add_symlink() { + struct iso_tree_node_dir *root; + struct iso_tree_node *lnk; + + root = iso_tree_new_root(); + lnk = iso_tree_add_symlink(root, "read", "/tmp/libisofs_test/README"); + CU_ASSERT_PTR_NOT_NULL(root); + CU_ASSERT_PTR_NOT_NULL(lnk); + CU_ASSERT_PTR_EQUAL(lnk->parent, root); + CU_ASSERT_EQUAL(root->nchildren, 1); + CU_ASSERT_PTR_EQUAL(root->children[0], (struct iso_tree_node *)lnk); + CU_ASSERT_STRING_EQUAL( lnk->name, "read"); + CU_ASSERT_STRING_EQUAL( ((struct iso_tree_node_symlink*)lnk)->dest, + "/tmp/libisofs_test/README" ); + CU_ASSERT( S_ISLNK(lnk->attrib.st_mode) ); + iso_tree_free((struct iso_tree_node *)root); +} + +static void test_add_node() { + struct iso_tree_node_dir *root; + struct iso_tree_node *node; + + root = iso_tree_new_root(); + + /* test addition of a dir */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_PTR_EQUAL(node->parent, root); + CU_ASSERT_EQUAL(root->nchildren, 1); + CU_ASSERT_PTR_EQUAL(root->children[0], node); + CU_ASSERT_STRING_EQUAL( node->name, "dir1"); + CU_ASSERT( ISO_ISDIR(node) ); + CU_ASSERT( S_ISDIR(node->attrib.st_mode) ); + + /* test addition of a link */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_PTR_EQUAL(node->parent, root); + CU_ASSERT_EQUAL(root->nchildren, 2); + CU_ASSERT_PTR_EQUAL(root->children[1], node); + CU_ASSERT( ISO_ISLNK(node) ); + CU_ASSERT( S_ISLNK(node->attrib.st_mode) ); + CU_ASSERT_STRING_EQUAL( node->name, "link to readme"); + CU_ASSERT_STRING_EQUAL( ((struct iso_tree_node_symlink*)node)->dest, + "/tmp/libisofs_test/README" ); + + /* test addition of a file */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_PTR_EQUAL(node->parent, root); + CU_ASSERT_EQUAL(root->nchildren, 3); + CU_ASSERT_PTR_EQUAL(root->children[2], node); + CU_ASSERT( S_ISREG(node->attrib.st_mode) ); + CU_ASSERT( ISO_ISREG(node) ); + CU_ASSERT_STRING_EQUAL( node->name, "README" ); + CU_ASSERT_STRING_EQUAL( ((struct iso_tree_node_file *) node)->path, + "/tmp/libisofs_test/README" ); + + /* test no exiting file */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/THISNOTEXIST"); + CU_ASSERT_PTR_NULL(node); + CU_ASSERT_EQUAL(libisofs_errno, NO_FILE); + CU_ASSERT_EQUAL(root->nchildren, 3); + + /* test no valid file */ + node = iso_tree_add_node(root, "/dev/zero"); + CU_ASSERT_PTR_NULL(node); + CU_ASSERT_EQUAL(libisofs_errno, UNEXPECTED_FILE_TYPE); + CU_ASSERT_EQUAL(root->nchildren, 3); + + /* test no read perm file */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/no_read"); + CU_ASSERT_PTR_NULL(node); + CU_ASSERT_EQUAL(libisofs_errno, NO_READ_ACCESS); + CU_ASSERT_EQUAL(root->nchildren, 3); + + iso_tree_free((struct iso_tree_node *)root); +} + +static void test_radd_dir() { + struct iso_tree_node_dir *root; + struct iso_tree_node_dir *child; + struct iso_tree_node_file *file; + struct iso_tree_radd_dir_behavior behavior = {0,0,0}; + + //TODO write really full test + + root = iso_tree_new_root(); + CU_ASSERT_PTR_NOT_NULL(root); + + iso_tree_radd_dir(root, "/tmp/libisofs_test", &behavior); + + /* test _root_ children */ + /* + child = (struct iso_tree_node_dir *)root->children[0]; + CU_ASSERT( S_ISDIR(child->node.attrib.st_mode) ); + CU_ASSERT_EQUAL( child->nchildren, 2); + CU_ASSERT_STRING_EQUAL( child->node.name, "dir1" ); + + child = (struct iso_tree_node_dir *)root->children[1]; + CU_ASSERT( S_ISDIR(child->node.attrib.st_mode) ); + CU_ASSERT_EQUAL( child->nchildren, 0); + CU_ASSERT_STRING_EQUAL( child->node.name, "dir2" ); + + file = (struct iso_tree_node_file *)root->children[2]; + CU_ASSERT( S_ISREG(file->node.attrib.st_mode) ); + CU_ASSERT_STRING_EQUAL( file->node.name, "README" ); + */ + //iso_tree_print( (struct iso_tree_node *)root, 4 ); +} + +static void test_set_name() { + struct iso_tree_node_dir *root; + struct iso_tree_node *node; + + root = iso_tree_new_root(); + + /* test on a dir */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_STRING_EQUAL( node->name, "dir1"); + iso_tree_node_set_name(node, "newname"); + CU_ASSERT_STRING_EQUAL( node->name, "newname"); + + /* test on a link */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_STRING_EQUAL( node->name, "link to readme"); + iso_tree_node_set_name(node, "new link name"); + CU_ASSERT_STRING_EQUAL( node->name, "new link name"); + + /* test on a file */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_STRING_EQUAL( node->name, "README" ); + iso_tree_node_set_name(node, "new file name"); + CU_ASSERT_STRING_EQUAL( node->name, "new file name"); + + iso_tree_free((struct iso_tree_node *)root); +} + +static void test_set_hidden() { + struct iso_tree_node_dir *root; + struct iso_tree_node *node; + + root = iso_tree_new_root(); + + /* test on a dir */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->hide_flags, 0); + iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR); + CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR); + iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET); + CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_JOLIET); + iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); + CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); + + /* test on a link */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->hide_flags, 0); + iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR); + CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR); + iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET); + CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_JOLIET); + iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); + CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); + + /* test on a file */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->hide_flags, 0); + iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR); + CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR); + iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET); + CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_JOLIET); + iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); + CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); + + iso_tree_free((struct iso_tree_node *)root); +} + +static void test_set_gid() { + struct iso_tree_node_dir *root; + struct iso_tree_node *node; + gid_t mygid = getgid(); + + root = iso_tree_new_root(); + + /* test on a dir */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->attrib.st_gid, mygid); + iso_tree_node_set_gid(node, 1234); + CU_ASSERT_EQUAL(node->attrib.st_gid, 1234); + + /* test on a link */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->attrib.st_gid, mygid); + iso_tree_node_set_gid(node, 1234); + CU_ASSERT_EQUAL(node->attrib.st_gid, 1234); + + /* test on a file */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->attrib.st_gid, mygid); + iso_tree_node_set_gid(node, 1234); + CU_ASSERT_EQUAL(node->attrib.st_gid, 1234); + + iso_tree_free((struct iso_tree_node *)root); +} + +static void test_set_uid() { + struct iso_tree_node_dir *root; + struct iso_tree_node *node; + uid_t myuid = getuid(); + + root = iso_tree_new_root(); + + /* test on a dir */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->attrib.st_uid, myuid); + iso_tree_node_set_uid(node, 1234); + CU_ASSERT_EQUAL(node->attrib.st_uid, 1234); + + /* test on a link */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->attrib.st_uid, myuid); + iso_tree_node_set_uid(node, 1234); + CU_ASSERT_EQUAL(node->attrib.st_uid, 1234); + + /* test on a file */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->attrib.st_uid, myuid); + iso_tree_node_set_uid(node, 1234); + CU_ASSERT_EQUAL(node->attrib.st_uid, 1234); + + //TODO + + iso_tree_free((struct iso_tree_node *)root); +} + +static void test_set_permissions() { + struct iso_tree_node_dir *root; + struct iso_tree_node *node; + + root = iso_tree_new_root(); + + /* test on a dir */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0755); + iso_tree_node_set_permissions(node, 0777); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0777); + iso_tree_node_set_permissions(node, 0744); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0744); + iso_tree_node_set_permissions(node, 0411); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0411); + + /* test on a link */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0777); + iso_tree_node_set_permissions(node, 0555); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0555); + iso_tree_node_set_permissions(node, 0744); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0744); + iso_tree_node_set_permissions(node, 0411); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0411); + + /* test on a file */ + node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); + CU_ASSERT_PTR_NOT_NULL(node); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0555); + iso_tree_node_set_permissions(node, 0777); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0777); + iso_tree_node_set_permissions(node, 0744); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0744); + iso_tree_node_set_permissions(node, 0411); + CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0411); + + iso_tree_free((struct iso_tree_node *)root); +} + +static void test_set_sort_weight() { + struct iso_tree_node_dir *root; + struct iso_tree_node_dir *dir; + struct iso_tree_node_file *file; + + root = iso_tree_new_root(); + + dir = iso_tree_add_dir(root, "New dir name"); + CU_ASSERT_PTR_NOT_NULL(dir); + + file = (struct iso_tree_node_file *) + iso_tree_add_file(dir, "/tmp/libisofs_test/README"); + CU_ASSERT_EQUAL(file->sort_weight, 0); + iso_tree_node_set_sort_weight((struct iso_tree_node *) file, 15); + CU_ASSERT_EQUAL(file->sort_weight, 15); + iso_tree_node_set_sort_weight((struct iso_tree_node *) file, -15); + CU_ASSERT_EQUAL(file->sort_weight, -15); + + /* changes to dir involve update files inside it */ + iso_tree_node_set_sort_weight((struct iso_tree_node *) dir, 28); + CU_ASSERT_EQUAL(file->sort_weight, 28); + + iso_tree_free((struct iso_tree_node *)root); +} + +void add_tree_suite() +{ + CU_pSuite pSuite = CU_add_suite("TreeSuite", NULL, NULL); + + CU_add_test(pSuite, "test of iso_tree_new_root()", test_new_root); + CU_add_test(pSuite, "test of iso_tree_add_dir()", test_add_dir); + CU_add_test(pSuite, "test of iso_tree_add_file()", test_add_file); + CU_add_test(pSuite, "test of iso_tree_add_symlink()", test_add_symlink); + CU_add_test(pSuite, "test of iso_tree_add_node()", test_add_node); + CU_add_test(pSuite, "test of iso_tree_radd_dir()", test_radd_dir); + + CU_add_test(pSuite, "test of iso_tree_node_set_name()", test_set_name); + CU_add_test(pSuite, "test of iso_tree_node_set_hidden()", test_set_hidden); + CU_add_test(pSuite, "test of iso_tree_node_set_gid()", test_set_gid); + CU_add_test(pSuite, "test of iso_tree_node_set_uid()", test_set_uid); + CU_add_test(pSuite, "test of iso_tree_node_set_permissions()", test_set_permissions); + CU_add_test(pSuite, "test of iso_tree_node_set_sort_weight()", test_set_sort_weight); +} diff --git a/libisofs/branches/ZeroTwoEight/test/test_util.c b/libisofs/branches/ZeroTwoEight/test/test_util.c new file mode 100644 index 00000000..274ebd31 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/test_util.c @@ -0,0 +1,265 @@ +/* + * Unit test for util.h + * + * This test utiliy functions + * + */ +#include "test.h" + +#include "util.h" + +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_1_dirid() +{ + CU_ASSERT_STRING_EQUAL( iso_1_dirid("dir1", "UTF-8"), "DIR1" ); + CU_ASSERT_STRING_EQUAL( iso_1_dirid("dIR1", "UTF-8"), "DIR1" ); + CU_ASSERT_STRING_EQUAL( iso_1_dirid("DIR1", "UTF-8"), "DIR1" ); + CU_ASSERT_STRING_EQUAL( iso_1_dirid("dirwithbigname", "UTF-8"), "DIRWITHB"); + CU_ASSERT_STRING_EQUAL( iso_1_dirid("dirwith8", "UTF-8"), "DIRWITH8"); + CU_ASSERT_STRING_EQUAL( iso_1_dirid("dir.1", "UTF-8"), "DIR_1"); + CU_ASSERT_STRING_EQUAL( iso_1_dirid("4f<0KmM::xcvf", "UTF-8"), "4F_0KMM_"); +} + +static void test_iso_2_dirid() +{ + CU_ASSERT_STRING_EQUAL( iso_2_dirid("dir1", "UTF-8"), "DIR1" ); + CU_ASSERT_STRING_EQUAL( iso_2_dirid("dIR1", "UTF-8"), "DIR1" ); + CU_ASSERT_STRING_EQUAL( iso_2_dirid("DIR1", "UTF-8"), "DIR1" ); + CU_ASSERT_STRING_EQUAL( iso_2_dirid("dirwithbigname", "UTF-8"), "DIRWITHBIGNAME"); + CU_ASSERT_STRING_EQUAL( iso_2_dirid("dirwith8", "UTF-8"), "DIRWITH8"); + CU_ASSERT_STRING_EQUAL( iso_2_dirid("dir.1", "UTF-8"), "DIR_1"); + CU_ASSERT_STRING_EQUAL( iso_2_dirid("4f<0KmM::xcvf", "UTF-8"), "4F_0KMM__XCVF"); + CU_ASSERT_STRING_EQUAL( iso_2_dirid("directory with 31 characters ok", "UTF-8"), "DIRECTORY_WITH_31_CHARACTERS_OK"); + CU_ASSERT_STRING_EQUAL( iso_2_dirid("directory with more than 31 characters", "UTF-8"), "DIRECTORY_WITH_MORE_THAN_31_CHA"); +} + +static void test_iso_r_dirid() +{ + int flag; + + /* 1. only ECMA119_37_CHAR_FILENAMES */ + flag = ECMA119_37_CHAR_FILENAMES; + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "DIR1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "DIR1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "DIRWITHBIGNAME"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "DIRWITH8"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "DIR_1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4F_0KMM__XCVF"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "DIRECTORY_WITH_31_CHARACTERS_OK"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "DIRECTORY_WITH_MORE_THAN_37_CHARACTER"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "DIRECTORY_WITH_JUST_37_CHARACTERS__OK"); + + /* 2. only ECMA119_RELAXED_FILENAMES */ + flag = ECMA119_RELAXED_FILENAMES; + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "dir1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "dIR1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "dirwithbigname"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "dirwith8"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "dir.1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4f<0KmM::xcvf"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "directory with 31 characters ok"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "directory with more than 37 cha"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "directory with just 37 characte"); + + /* 3. both ECMA119_RELAXED_FILENAMES and ECMA119_37_CHAR_FILENAMES */ + flag = ECMA119_RELAXED_FILENAMES | ECMA119_37_CHAR_FILENAMES; + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "dir1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "dIR1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "dirwithbigname"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "dirwith8"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "dir.1"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4f<0KmM::xcvf"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "directory with 31 characters ok"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "directory with more than 37 character"); + CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "directory with just 37 characters ok"); +} + +static void test_iso_1_fileid() +{ + CU_ASSERT_STRING_EQUAL( iso_1_fileid("file1", "UTF-8"), "FILE1.;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("fILe1", "UTF-8"), "FILE1.;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("FILE1", "UTF-8"), "FILE1.;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid(".EXT", "UTF-8"), ".EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ext", "UTF-8"), "FILE.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("fiLE.ext", "UTF-8"), "FILE.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.EXt", "UTF-8"), "FILE.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("FILE.EXT", "UTF-8"), "FILE.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename", "UTF-8"), "BIGFILEN.;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.ext", "UTF-8"), "BIGFILEN.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.e", "UTF-8"), "BIGFILEN.E;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.bigext", "UTF-8"), "FILE.BIG;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid(".bigext", "UTF-8"), ".BIG;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.bigext", "UTF-8"), "BIGFILEN.BIG;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("file<:a.ext", "UTF-8"), "FILE__A.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.<:a", "UTF-8"), "FILE.__A;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("file<:a.--a", "UTF-8"), "FILE__A.__A;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ex1.ex2", "UTF-8"), "FILE_EX1.EX2;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ex1.ex2.ex3", "UTF-8"), "FILE_EX1.EX3;1"); + CU_ASSERT_STRING_EQUAL( iso_1_fileid("fil.ex1.ex2.ex3", "UTF-8"), "FIL_EX1_.EX3;1"); +} + +static void test_iso_2_fileid() +{ + CU_ASSERT_STRING_EQUAL( iso_2_fileid("file1", "UTF-8"), "FILE1.;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("fILe1", "UTF-8"), "FILE1.;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("FILE1", "UTF-8"), "FILE1.;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid(".EXT", "UTF-8"), ".EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ext", "UTF-8"), "FILE.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("fiLE.ext", "UTF-8"), "FILE.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.EXt", "UTF-8"), "FILE.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("FILE.EXT", "UTF-8"), "FILE.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename", "UTF-8"), "BIGFILENAME.;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.ext", "UTF-8"), "BIGFILENAME.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.e", "UTF-8"), "BIGFILENAME.E;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("31 characters filename.extensio", "UTF-8"), "31_CHARACTERS_FILENAME.EXTENSIO;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("32 characters filename.extension", "UTF-8"), "32_CHARACTERS_FILENAME.EXTENSIO;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("more than 30 characters filename.extension", "UTF-8"), "MORE_THAN_30_CHARACTERS_FIL.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.bigext", "UTF-8"), "FILE.BIGEXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid(".bigext", "UTF-8"), ".BIGEXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.bigext", "UTF-8"), "BIGFILENAME.BIGEXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("file<:a.ext", "UTF-8"), "FILE__A.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.<:a", "UTF-8"), "FILE.__A;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("file<:a.--a", "UTF-8"), "FILE__A.__A;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ex1.ex2", "UTF-8"), "FILE_EX1.EX2;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ex1.ex2.ex3", "UTF-8"), "FILE_EX1_EX2.EX3;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid("fil.ex1.ex2.ex3", "UTF-8"), "FIL_EX1_EX2.EX3;1"); + CU_ASSERT_STRING_EQUAL( iso_2_fileid(".file.bigext", "UTF-8"), "_FILE.BIGEXT;1"); +} + +static void test_iso_r_fileid() +{ + int flag; + + /* 1. only ECMA119_OMIT_VERSION_NUMBERS */ + flag = ECMA119_OMIT_VERSION_NUMBERS; + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "FILE1."); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "FILE1."); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31_CHARACTERS_FILENAME.EXTENSIO"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "IT_S_37_CHARACTERS_FILENAME.EXT"); + + /* 2. only ECMA119_37_CHAR_FILENAMES */ + flag = ECMA119_37_CHAR_FILENAMES; + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "FILE1."); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "FILE1."); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1."); + CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "FILE.EXT"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "FILE.EXT"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "FILE.EXT"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "BIGFILENAME."); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "BIGFILENAME.EXT"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "BIGFILENAME.E"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "FILE.BIGEXT"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31_CHARACTERS_FILENAME.EXTENSIO"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "IT_S_37_CHARACTERS_FILENAME.EXTENSION"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("more than 37 characters filename.extension", "UTF-8", flag), "MORE_THAN_37_CHARACTERS_FILENAME.EXTE"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "FILE.THIS_IS_A_37_CHARS_LEN_EXTENSION"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a very very big extension", "UTF-8", flag), "FILE.THIS_IS_A_VERY_VERY_BIG_EXTENSIO"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "FIL_EX1_EX2.EX3"); + + /* 3. only ECMA119_RELAXED_FILENAMES */ + flag = ECMA119_RELAXED_FILENAMES; + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "file1;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "fILe1;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "file.ext;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "fiLE.ext;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "file.EXt;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "bigfilename;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "bigfilename.ext;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "bigfilename.e;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "file.bigext;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31 characters filename.extensio;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "it's 37 characters filename.ext;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "file.this is a 37 chars len ext;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "fil.ex1.ex2.ex3;1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.<:a", "UTF-8", flag), "file.<:a;1"); + + /* 3. ECMA119_RELAXED_FILENAMES and ECMA119_OMIT_VERSION_NUMBERS*/ + flag = ECMA119_RELAXED_FILENAMES | ECMA119_OMIT_VERSION_NUMBERS; + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "file1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "fILe1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "file.ext"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "fiLE.ext"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "file.EXt"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "bigfilename"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "bigfilename.ext"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "bigfilename.e"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "file.bigext"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31 characters filename.extensio"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "it's 37 characters filename.ext"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "file.this is a 37 chars len ext"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "fil.ex1.ex2.ex3"); + CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.<:a", "UTF-8", flag), "file.<:a"); +} + +void add_util_suite() +{ + CU_pSuite pSuite = CU_add_suite("UtilSuite", NULL, NULL); + + CU_add_test(pSuite, "test of div_up()", test_div_up); + CU_add_test(pSuite, "test of round_up()", test_round_up); + CU_add_test(pSuite, "test of iso_lsb_msb()", test_iso_lsb_msb); + CU_add_test(pSuite, "test of iso_1_dirid()", test_iso_1_dirid); + CU_add_test(pSuite, "test of iso_2_dirid()", test_iso_2_dirid); + CU_add_test(pSuite, "test of iso_r_dirid()", test_iso_r_dirid); + CU_add_test(pSuite, "test of iso_1_fileid()", test_iso_1_fileid); + CU_add_test(pSuite, "test of iso_2_fileid()", test_iso_2_fileid); + CU_add_test(pSuite, "test of iso_r_fileid()", test_iso_r_fileid); +} diff --git a/libisofs/branches/ZeroTwoEight/test/test_volume.c b/libisofs/branches/ZeroTwoEight/test/test_volume.c new file mode 100644 index 00000000..22251069 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/test_volume.c @@ -0,0 +1,238 @@ +/* + * Unit test for volume.h + */ + + +#include "libisofs.h" +#include "tree.h" +#include "test.h" +#include "volume.h" + +#include +#include +#include +#include +#include +#include + + +static void test_iso_volume_new() +{ + struct iso_volume *volume; + + volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); + CU_ASSERT_PTR_NOT_NULL(volume); + CU_ASSERT_EQUAL(volume->refcount, 1); + /* a new root must be created */ + CU_ASSERT_PTR_NOT_NULL(volume->root); + + CU_ASSERT_STRING_EQUAL( volume->volume_id, "volume_id" ); + CU_ASSERT_STRING_EQUAL( volume->publisher_id, "publisher_id" ); + CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "data_preparer_id" ); + + CU_ASSERT_PTR_NULL(volume->system_id); + CU_ASSERT_PTR_NULL(volume->application_id); + CU_ASSERT_PTR_NULL(volume->copyright_file_id); + CU_ASSERT_PTR_NULL(volume->abstract_file_id); + CU_ASSERT_PTR_NULL(volume->biblio_file_id); + + CU_ASSERT_PTR_NULL(volume->bootcat); + + iso_volume_free(volume); +} + +static void test_iso_volume_new_with_root() +{ + struct iso_volume *volume; + struct iso_tree_node_dir *root; + + root = iso_tree_new_root(); + volume = iso_volume_new_with_root("volume_id", "publisher_id", + "data_preparer_id", root); + + CU_ASSERT_PTR_NOT_NULL(volume); + CU_ASSERT_EQUAL(volume->refcount, 1); + CU_ASSERT_PTR_NOT_NULL(volume->root); + CU_ASSERT_PTR_EQUAL(volume->root, root); + + CU_ASSERT_STRING_EQUAL( volume->volume_id, "volume_id" ); + CU_ASSERT_STRING_EQUAL( volume->publisher_id, "publisher_id" ); + CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "data_preparer_id" ); + + CU_ASSERT_PTR_NULL(volume->system_id); + CU_ASSERT_PTR_NULL(volume->application_id); + CU_ASSERT_PTR_NULL(volume->copyright_file_id); + CU_ASSERT_PTR_NULL(volume->abstract_file_id); + CU_ASSERT_PTR_NULL(volume->biblio_file_id); + + CU_ASSERT_PTR_NULL(volume->bootcat); + + iso_volume_free(volume); +} + +static void test_iso_volume_get_root() +{ + struct iso_volume *volume; + struct iso_tree_node_dir *root; + struct iso_tree_node_dir *root2; + + root = iso_tree_new_root(); + volume = iso_volume_new_with_root("volume_id", "publisher_id", + "data_preparer_id", root); + + root2 = iso_volume_get_root(volume); + + CU_ASSERT_PTR_NOT_NULL(root2); + CU_ASSERT_PTR_EQUAL(root2, volume->root); + CU_ASSERT_PTR_EQUAL(root2, root); + + iso_volume_free(volume); +} + +static void test_iso_volume_set_volume_id() +{ + struct iso_volume *volume; + + volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); + CU_ASSERT_STRING_EQUAL( volume->volume_id, "volume_id" ); + + char *volid = "new volume id"; + iso_volume_set_volume_id(volume, volid); + CU_ASSERT_STRING_EQUAL( volume->volume_id, "new volume id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( volume->volume_id, volid ); + iso_volume_free(volume); +} + +static void test_iso_volume_set_publisher_id() +{ + struct iso_volume *volume; + + volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); + CU_ASSERT_STRING_EQUAL( volume->publisher_id, "publisher_id" ); + + char *pubid = "new publisher id"; + iso_volume_set_publisher_id(volume, pubid); + CU_ASSERT_STRING_EQUAL( volume->publisher_id, "new publisher id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( volume->publisher_id, pubid ); + iso_volume_free(volume); +} + +static void test_iso_volume_set_data_preparer_id() +{ + struct iso_volume *volume; + + volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); + CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "data_preparer_id" ); + + char *dpid = "new data preparer id"; + iso_volume_set_data_preparer_id(volume, dpid); + CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "new data preparer id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( volume->data_preparer_id, dpid ); + iso_volume_free(volume); +} + +static void test_iso_volume_set_system_id() +{ + struct iso_volume *volume; + + volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); + CU_ASSERT_PTR_NULL(volume->system_id); + + char *sysid = "new system id"; + iso_volume_set_system_id(volume, sysid); + CU_ASSERT_STRING_EQUAL( volume->system_id, "new system id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( volume->system_id, sysid ); + iso_volume_free(volume); +} + +static void test_iso_volume_set_application_id() +{ + struct iso_volume *volume; + + volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); + CU_ASSERT_PTR_NULL(volume->application_id); + + char *appid = "new application id"; + iso_volume_set_application_id(volume, appid); + CU_ASSERT_STRING_EQUAL( volume->application_id, "new application id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( volume->application_id, appid ); + iso_volume_free(volume); +} + +static void test_iso_volume_set_abstract_file_id() +{ + struct iso_volume *volume; + + volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); + CU_ASSERT_PTR_NULL(volume->abstract_file_id); + + char *absid = "new abstract id"; + iso_volume_set_abstract_file_id(volume, absid); + CU_ASSERT_STRING_EQUAL( volume->abstract_file_id, "new abstract id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( volume->abstract_file_id, absid ); + iso_volume_free(volume); +} + +static void test_iso_volume_set_biblio_file_id() +{ + struct iso_volume *volume; + + volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); + CU_ASSERT_PTR_NULL(volume->biblio_file_id); + + char *bibid = "new biblio id"; + iso_volume_set_biblio_file_id(volume, bibid); + CU_ASSERT_STRING_EQUAL( volume->biblio_file_id, "new biblio id" ); + + /* check string was strdup'ed */ + CU_ASSERT_PTR_NOT_EQUAL( volume->biblio_file_id, bibid ); + iso_volume_free(volume); +} + +static void test_iso_volset_new() +{ + struct iso_volume *volume; + struct iso_volset *volset; + + volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); + + volset = iso_volset_new(volume, "volset_id"); + CU_ASSERT_PTR_NOT_NULL(volset); + CU_ASSERT_EQUAL(volset->refcount, 1); + CU_ASSERT_EQUAL(volset->volset_size, 1); + CU_ASSERT_PTR_NOT_NULL(volset->volume); + CU_ASSERT_PTR_NOT_NULL(volset->volume[0]); + CU_ASSERT_PTR_EQUAL(volset->volume[0], volume); + CU_ASSERT_STRING_EQUAL( volset->volset_id, "volset_id" ); + + iso_volset_free(volset); +} + +void add_volume_suite() +{ + CU_pSuite pSuite = CU_add_suite("VolumeSuite", NULL, NULL); + + CU_add_test(pSuite, "test of iso_volume_new()", test_iso_volume_new); + CU_add_test(pSuite, "test of iso_volume_new_with_root()", test_iso_volume_new_with_root); + CU_add_test(pSuite, "test of iso_volume_get_root()", test_iso_volume_get_root); + CU_add_test(pSuite, "test of iso_volume_set_volume_id()", test_iso_volume_set_volume_id); + CU_add_test(pSuite, "test of iso_volume_set_publisher_id()", test_iso_volume_set_publisher_id); + CU_add_test(pSuite, "test of iso_volume_set_data_preparer_id()", test_iso_volume_set_data_preparer_id); + CU_add_test(pSuite, "test of iso_volume_set_system_id()", test_iso_volume_set_system_id); + CU_add_test(pSuite, "test of iso_volume_set_application_id()", test_iso_volume_set_application_id); + CU_add_test(pSuite, "test of iso_volume_set_abstract_file_id()", test_iso_volume_set_abstract_file_id); + CU_add_test(pSuite, "test of iso_volume_set_biblio_file_id()", test_iso_volume_set_biblio_file_id); + CU_add_test(pSuite, "test of iso_volset_new()", test_iso_volset_new); +} diff --git a/libisofs/branches/ZeroTwoEight/test/tree.py b/libisofs/branches/ZeroTwoEight/test/tree.py new file mode 100644 index 00000000..a7be3d80 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/test/tree.py @@ -0,0 +1,77 @@ +# a module to help with handling of filenames, directory trees, etc. + +import os +import os.path +import stat + +def pathsubtract(a, b): + index = a.find(b) + if index == -1: + return None + res = a[ (index + len(b)): ] + + if res.find("/") == 0: + res = res[1:] + return res + +# same as C strcmp() +def strcmp(a, b): + if a < b: + return -1 + if a > b: + return 1 + return 0 + +class TreeNode: + + # path is the location of the file/directory. It is either a full path or + # a path relative to $PWD + def __init__(self, parent, path=".", root=".", isofile=None): + if isofile: + self.root = os.path.abspath(isofile) + self.path = "" + else: + fullpath = os.path.abspath( path ) + fullroot = os.path.abspath( root ) + self.root = fullroot + self.path = pathsubtract( fullpath, fullroot ) + self.parent = parent + self.children = [] + + if self.path == None: + raise NameError, "Invalid paths %s and %s" % (fullpath, fullroot) + + # if this is a directory, add its children recursively + def addchildren(self): + if not stat.S_ISDIR( os.lstat(self.root + "/" + self.path).st_mode ): + return + + children = os.listdir( self.root + "/" + self.path ) + for child in children: + if self.path: + child = self.path + "/" + child + self.children.append( TreeNode(self, child, self.root) ) + for child in self.children: + child.addchildren() + + def printAll(self, spaces=0): + print " "*spaces + self.root + "/" + self.path + for child in self.children: + child.printAll(spaces + 2) + + def isValidISO1(self): + pass + +class Tree: + def __init__(self, root=None, isofile=None): + if isofile: + self.root = TreeNode(parent=None, isofile=isofile) + else: + self.root = TreeNode(parent=None, path=root, root=root) + self.root.addchildren() + + def isValidISO1(self): + return root.isValidISO1(); + +#t = Tree(root=".") +#t.root.printAll() diff --git a/libisofs/branches/ZeroTwoEight/version.h.in b/libisofs/branches/ZeroTwoEight/version.h.in new file mode 100644 index 00000000..13ada991 --- /dev/null +++ b/libisofs/branches/ZeroTwoEight/version.h.in @@ -0,0 +1,3 @@ +#define BURN_MAJOR_VERSION @BURN_MAJOR_VERSION@ +#define BURN_MINOR_VERSION @BURN_MINOR_VERSION@ +#define BURN_MICRO_VERSION @BURN_MICRO_VERSION@ diff --git a/libisofs/trunk/libisofs/ecma119_tree.c b/libisofs/trunk/libisofs/ecma119_tree.c index 56459547..d31ce6a9 100644 --- a/libisofs/trunk/libisofs/ecma119_tree.c +++ b/libisofs/trunk/libisofs/ecma119_tree.c @@ -18,6 +18,12 @@ static size_t calc_dirent_len(struct ecma119_tree_node *n) return ret; } +/** + * Replace the file permissions and user/group id of an ECMA-119 node. + * This is used when a replace mode is selected, i.e., when we want to + * create a disc where the mode of each file or directory will be + * different than the mode in the original source. + */ static void replace_node_mode(struct ecma119_write_target *t, struct stat *st) { @@ -65,6 +71,19 @@ replace_node_mode(struct ecma119_write_target *t, struct stat *st) } } +/** + * Creates a new ECMA-119 node from the given iso tree node, and initializes + * the fields that are common to all kind of nodes (dir, reg file, symlink...). + * + * @param t + * The options for the ECMA-119 tree that is being created + * @param parent + * The parent of the node, or NULL if it's the root. + * @param iso + * The node from which this function creates a ECMA-119 node + * @return + * The created node. + */ static struct ecma119_tree_node* create_ecma119_node(struct ecma119_write_target *t, struct ecma119_tree_node *parent, @@ -92,13 +111,22 @@ create_ecma119_node(struct ecma119_write_target *t, iso_name(iso->name, t->input_charset) ) : NULL; ret->dirent_len = calc_dirent_len(ret); + + /* iso node keeps the same file attribs as the original file. */ ret->attrib = iso->attrib; + + /* + * When using RR extension and replace mode, we will replace the + * permissions and uid/gid of each file with those previously selected + * by the user. + */ if ( t->rockridge && t->replace_mode ) replace_node_mode(t, &ret->attrib); if (!iso->name) ret->full_name = NULL; else if ( strcmp(t->input_charset,t->ouput_charset) ) + /* convert the file name charset */ ret->full_name = convert_str(iso->name, t->input_charset, t->ouput_charset); else @@ -108,6 +136,10 @@ create_ecma119_node(struct ecma119_write_target *t, return ret; } +/** + * Create a new ECMA-119 node representing a directory from a iso directory + * node. + */ static struct ecma119_tree_node* create_dir(struct ecma119_write_target *t, struct ecma119_tree_node *parent, @@ -127,6 +159,10 @@ create_dir(struct ecma119_write_target *t, return ret; } +/** + * Create a new ECMA-119 node representing a regular file from a iso file + * node. + */ static struct ecma119_tree_node* create_file(struct ecma119_write_target *t, struct ecma119_tree_node *parent, @@ -143,6 +179,10 @@ create_file(struct ecma119_write_target *t, /* get iso_file struct */ file = iso_file_table_lookup(t->file_table, iso); if ( file == NULL ) { + /* + * If the file is not already added to the disc, we add it now + * to the file table, and get a new inode number for it. + */ file = iso_file_new(iso); iso_file_table_add_file(t->file_table, file); file->ino = ++t->ino; @@ -157,6 +197,12 @@ create_file(struct ecma119_write_target *t, return ret; } +/** + * Create a new ECMA-119 node representing a placeholder for a relocated + * dir. + * + * See IEEE P1282, section 4.1.5 for details + */ static struct ecma119_tree_node* create_placeholder(struct ecma119_write_target *t, struct ecma119_tree_node *parent, @@ -191,6 +237,10 @@ create_placeholder(struct ecma119_write_target *t, return ret; } +/** + * Create a new ECMA-119 node representing a symbolic link from a iso symlink + * node. + */ static struct ecma119_tree_node* create_symlink(struct ecma119_write_target *t, struct ecma119_tree_node *parent, @@ -212,6 +262,14 @@ create_symlink(struct ecma119_write_target *t, return ret; } +/** + * Create a new ECMA-119 node representing a boot catalog. This is like a + * regular file, but its contents are taken from a El-Torito boot catalog, + * and not from a file in the local filesystem. + * + * See "El Torito" Bootable CD-ROM Format Specification Version 1.0 for + * more details. + */ static struct ecma119_tree_node* create_boot_catalog(struct ecma119_write_target *t, struct ecma119_tree_node *parent, @@ -239,6 +297,11 @@ create_boot_catalog(struct ecma119_write_target *t, return ret; } +/** + * Create a new ECMA-119 node that corresponds to the given iso tree node. + * If that node is a dir, this function recurses over all their children, + * thus creating a ECMA-119 tree whose root is the given iso dir. + */ static struct ecma119_tree_node* create_tree(struct ecma119_write_target *t, struct ecma119_tree_node *parent, @@ -316,6 +379,12 @@ max_child_name_len(struct ecma119_tree_node *root) 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 void reparent(struct ecma119_tree_node *child, struct ecma119_tree_node *parent) @@ -350,6 +419,8 @@ reparent(struct ecma119_tree_node *child, * 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 (see ECMA-119, + * 6.8.2.1). */ static void reorder_tree(struct ecma119_write_target *t, @@ -400,6 +471,9 @@ reorder_tree(struct ecma119_write_target *t, } } +/** + * Compare the iso name of two ECMA-119 nodes + */ static int cmp_node(const void *f1, const void *f2) { @@ -408,6 +482,10 @@ cmp_node(const void *f1, const void *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(struct ecma119_tree_node *root) { @@ -470,6 +548,10 @@ mangle_name(char **name, int num_change, int level, int seq_num) sprintf(*name, fmt, base, seq_num, ext); } +/** + * Ensures that the ISO name of each children of the given dir is unique, + * changing some of them if needed. + */ static void mangle_all(struct ecma119_tree_node *dir) { diff --git a/libisofs/trunk/libisofs/rockridge.c b/libisofs/trunk/libisofs/rockridge.c index 65313012..c2d7b4b9 100755 --- a/libisofs/trunk/libisofs/rockridge.c +++ b/libisofs/trunk/libisofs/rockridge.c @@ -14,6 +14,30 @@ #include #include +/** See IEEE P1281 Draft Version 1.12/5.5 FIXME: this is rockridge */ +void +rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir) +{ + unsigned char *ER = malloc(182); + + assert(dir->type == ECMA119_DIR); + + 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); + susp_append(t, &dir->info.dir.self_susp, ER); +} + /* create a PX field from the permissions on the current node. */ uint8_t *rrip_make_PX(struct ecma119_write_target *t, struct ecma119_tree_node *node) diff --git a/libisofs/trunk/libisofs/rockridge.h b/libisofs/trunk/libisofs/rockridge.h index 7909a0ce..63e4f6ae 100755 --- a/libisofs/trunk/libisofs/rockridge.h +++ b/libisofs/trunk/libisofs/rockridge.h @@ -1,6 +1,11 @@ /* vim: set noet ts=8 sts=8 sw=8 : */ -/** Functions and structures used for Rock Ridge support. */ +/** + * Functions and structures used for Rock Ridge support. + * + * See IEEE P1282, Rock Ridge Interchange Protocol, Draft Standard version + * 1.12 for further details. + */ #ifndef ISO_ROCKRIDGE_H #define ISO_ROCKRIDGE_H @@ -8,18 +13,99 @@ struct ecma119_write_target; struct ecma119_tree_node; +/** + * 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 IEEE P1281, section 5.5 and IEEE P1282, section 4.3 for more details. + */ +void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * Add a PX System Use Entry to the given tree node and, if that node is + * a directory, to its "." and ".." entries. 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. + * + * See IEEE P1282, section 4.1.1 for more details. + */ void rrip_add_PX(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * 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 IEEE P1282, section 4.1.2 for more details. + */ void rrip_add_PN(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * 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. + * + * See IEEE P1282, section 4.1.3 for more details. + */ void rrip_add_SL(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * 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 IEEE P1282, section 4.1.4 for more details. + */ void rrip_add_NM(struct ecma119_write_target *, struct ecma119_tree_node *); + +/* + * The next 3 System Use Entries are used to handle Deep Directory + * Hierarchies, i.e., hierarchies where the number of directory levels + * exceed the eight limit of ECMA-119. + */ + +/** + * 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 IEEE P1282, section 4.1.5.1 for more details. + */ void rrip_add_CL(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * 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 IEEE P1282, section 4.1.5.2 for more details. + */ +void rrip_add_PL(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * 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 IEEE P1282, section 4.1.5.3 for more details. + */ void rrip_add_RE(struct ecma119_write_target *, struct ecma119_tree_node *); + +/** + * Add to the given tree node a TF System Use Entry, used to record some + * time stamps related to the file. + * + * See IEEE P1282, section 4.1.6 for more details. + */ void rrip_add_TF(struct ecma119_write_target *, struct ecma119_tree_node *); -/* 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. */ -void rrip_add_PL(struct ecma119_write_target *, struct ecma119_tree_node *); void rrip_finalize(struct ecma119_write_target *, struct ecma119_tree_node *); diff --git a/libisofs/trunk/libisofs/susp.c b/libisofs/trunk/libisofs/susp.c index a29d8b3f..5e9889b3 100755 --- a/libisofs/trunk/libisofs/susp.c +++ b/libisofs/trunk/libisofs/susp.c @@ -155,30 +155,6 @@ static void susp_add_ST(struct ecma119_write_target *t, } #endif -/** See IEEE P1281 Draft Version 1.12/5.5 FIXME: this is rockridge */ -void -rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir) -{ - unsigned char *ER = malloc(182); - - assert(dir->type == ECMA119_DIR); - - 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); - susp_append(t, &dir->info.dir.self_susp, ER); -} - /* calculate the location of the CE areas. Since CE areas don't need to be * aligned to a block boundary, we contatenate all CE areas from a single * directory and dump them immediately after all the directory records. diff --git a/libisofs/trunk/libisofs/susp.h b/libisofs/trunk/libisofs/susp.h index 31dd2cfd..5e7b95e1 100755 --- a/libisofs/trunk/libisofs/susp.h +++ b/libisofs/trunk/libisofs/susp.h @@ -1,6 +1,10 @@ /* vim: set noet ts=8 sts=8 sw=8 : */ -/** Functions and structures used for SUSP (IEEE 1281). +/** + * Functions and structures used for SUSP (IEEE 1281). + * + * Please refer to IEEE P1281 System Use Sharing Protocol, draft standard + * version 1.12 for more details. */ #ifndef __ISO_SUSP @@ -28,13 +32,23 @@ struct susp_info * will go in a CE area. */ }; +/** + * 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. + * See IEEE P1281, section 5.1. + */ void susp_add_CE(struct ecma119_write_target *, struct ecma119_tree_node *); -/* these next 2 are special because they don't modify the susp fields of the - * directory; they modify the susp fields of the - * "." entry in the directory. */ +/** + * Add a SP System Use Entry to the "." entry of the directory. 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 IEEE P1281, section 5.3 for more details. + * + * this is special because it doesn't modify the susp fields of the + * directory; it modifies the susp fields of the "." entry in the directory. + */ void susp_add_SP(struct ecma119_write_target *, struct ecma119_tree_node *); -void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *); /** Once all the directories and files are laid out, recurse through the tree * and finalize all SUSP CE entries. */