From 445755af40b92635101c628719edbcbf7f866156 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Tue, 31 Jul 2007 10:18:30 +0000 Subject: [PATCH] Trying to prepare 0.2.5 release --- AUTHORS | 4 + CONTRIBUTORS | 2 + COPYING | 280 ++++++++++++++++ COPYRIGHT | 19 ++ ChangeLog | 1 + INSTALL | 234 +++++++++++++ Makefile.am | 113 +++++++ NEWS | 1 + README | 231 +++++++++++++ acinclude.m4 | 22 ++ bootstrap | 10 + configure.ac | 117 +++++++ doc/Makefile | 4 + doc/comments | 123 +++++++ doc/comments_test_ts | 121 +++++++ doc/doxygen.conf.in | 186 +++++++++++ libisofs-1.pc.in | 11 + libisofs/Makefile | 4 + libisofs/ecma119.c | 715 ++++++++++++++++++++++++++++++++++++++++ libisofs/ecma119.h | 267 +++++++++++++++ libisofs/ecma119_tree.c | 312 ++++++++++++++++++ libisofs/ecma119_tree.h | 95 ++++++ libisofs/exclude.c | 42 +++ libisofs/exclude.h | 12 + libisofs/hash.c | 158 +++++++++ libisofs/hash.h | 46 +++ libisofs/joliet.c | 400 ++++++++++++++++++++++ libisofs/joliet.h | 84 +++++ libisofs/libisofs.h | 225 +++++++++++++ libisofs/rockridge.c | 300 +++++++++++++++++ libisofs/rockridge.h | 26 ++ libisofs/susp.c | 280 ++++++++++++++++ libisofs/susp.h | 62 ++++ libisofs/tree.c | 223 +++++++++++++ libisofs/tree.h | 159 +++++++++ libisofs/util.c | 584 ++++++++++++++++++++++++++++++++ libisofs/util.h | 121 +++++++ libisofs/volume.c | 188 +++++++++++ libisofs/volume.h | 45 +++ test/Makefile | 4 + test/iso.c | 107 ++++++ test/iso.py | 297 +++++++++++++++++ test/tree.py | 77 +++++ version.h.in | 3 + 44 files changed, 6315 insertions(+) create mode 100644 AUTHORS create mode 100644 CONTRIBUTORS create mode 100644 COPYING create mode 100644 COPYRIGHT create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 acinclude.m4 create mode 100755 bootstrap create mode 100644 configure.ac create mode 100644 doc/Makefile create mode 100644 doc/comments create mode 100644 doc/comments_test_ts create mode 100644 doc/doxygen.conf.in create mode 100644 libisofs-1.pc.in create mode 100755 libisofs/Makefile create mode 100755 libisofs/ecma119.c create mode 100755 libisofs/ecma119.h create mode 100644 libisofs/ecma119_tree.c create mode 100644 libisofs/ecma119_tree.h create mode 100644 libisofs/exclude.c create mode 100644 libisofs/exclude.h create mode 100644 libisofs/hash.c create mode 100644 libisofs/hash.h create mode 100644 libisofs/joliet.c create mode 100644 libisofs/joliet.h create mode 100755 libisofs/libisofs.h create mode 100755 libisofs/rockridge.c create mode 100755 libisofs/rockridge.h create mode 100755 libisofs/susp.c create mode 100755 libisofs/susp.h create mode 100755 libisofs/tree.c create mode 100755 libisofs/tree.h create mode 100755 libisofs/util.c create mode 100755 libisofs/util.h create mode 100755 libisofs/volume.c create mode 100755 libisofs/volume.h create mode 100644 test/Makefile create mode 100644 test/iso.c create mode 100644 test/iso.py create mode 100644 test/tree.py create mode 100644 version.h.in diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..0f8d838 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +Developers: + +Mario Danic + diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 0000000..c774a03 --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1,2 @@ +Joe Neeman +Philippe Rouquier diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..5a965fb --- /dev/null +++ b/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/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..a6b57b3 --- /dev/null +++ b/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/ChangeLog b/ChangeLog new file mode 100644 index 0000000..7001d0f --- /dev/null +++ b/ChangeLog @@ -0,0 +1 @@ +nothing here now diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..5458714 --- /dev/null +++ b/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/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3597005 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,113 @@ +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 + +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 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/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..861847b --- /dev/null +++ b/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/bootstrap b/bootstrap new file mode 100755 index 0000000..86709bf --- /dev/null +++ b/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/configure.ac b/configure.ac new file mode 100644 index 0000000..96b506d --- /dev/null +++ b/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/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..062350d --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/doc/comments b/doc/comments new file mode 100644 index 0000000..a35270b --- /dev/null +++ b/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/doc/comments_test_ts b/doc/comments_test_ts new file mode 100644 index 0000000..a4e23c5 --- /dev/null +++ b/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/doc/doxygen.conf.in b/doc/doxygen.conf.in new file mode 100644 index 0000000..10ae752 --- /dev/null +++ b/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-1.pc.in b/libisofs-1.pc.in new file mode 100644 index 0000000..4cf18c1 --- /dev/null +++ b/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/Makefile b/libisofs/Makefile new file mode 100755 index 0000000..062350d --- /dev/null +++ b/libisofs/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c new file mode 100755 index 0000000..8de00dd --- /dev/null +++ b/libisofs/ecma119.c @@ -0,0 +1,715 @@ +/* -*- 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 "libisofs.h" +#include "libburn/libburn.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, + 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, + 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; + + if (!node->iso_self) + return; + + rrip_add_PX(t, node); + rrip_add_NM(t, node); + rrip_add_TF(t, node); + if (node->iso_self->attrib.st_rdev) + rrip_add_PN(t, node); + if (S_ISLNK(node->iso_self->attrib.st_mode)) + rrip_add_SL(t, node); + if (node->type == ECMA119_FILE && node->file.real_me) + rrip_add_CL(t, node); + if (node->type == ECMA119_DIR + && node->dir.real_parent != node->parent) { + rrip_add_RE(t, node); + rrip_add_PL(t, node); + } + susp_add_CE(t, node); + + if (node->type == ECMA119_DIR) { + for (i = 0; i < node->dir.nchildren; i++) { + add_susp_fields_rec(t, node->dir.children[i]); + } + } +} + +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->dir.len = 34 + dir->dir.self_susp.non_CE_len + + 34 + dir->dir.parent_susp.non_CE_len; + dir->dir.CE_len = dir->dir.self_susp.CE_len + + dir->dir.parent_susp.CE_len; + for (i = 0; i < dir->dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->dir.children[i]; + + newlen = dir->dir.len + ch->dirent_len + ch->susp.non_CE_len; + if ((newlen % 2048) < (dir->dir.len % 2048)) { + dir->dir.len = newlen + (2048 - (dir->dir.len % 2048)); + } + else { + dir->dir.len += ch->dirent_len + ch->susp.non_CE_len; + } + dir->dir.CE_len += ch->susp.CE_len; + } + t->total_dir_size += round_up(dir->dir.len + dir->dir.CE_len, + t->block_size); + + for (i = 0; i < dir->dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->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); + + /* we don't need to set iso_self->block since each tree writes + * its own directories */ + dir->block = t->curblock; + t->curblock += div_up(dir->dir.len + dir->dir.CE_len, t->block_size); + t->dirlist[t->curfile++] = dir; + for (i = 0; i < dir->dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->dir.children[i]; + if (ch->type == ECMA119_DIR) + calc_dir_pos(t, ch); + } + + /* reset curfile when we're finished */ + if (!dir->parent) { + t->curfile = 0; + } +} + +/** + * Fill out the block field for each ecma119_tree_node that is a 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); + + for (i = 0; i < dir->dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->dir.children[i]; + if (ch->type == ECMA119_FILE && ch->iso_self) { + struct iso_tree_node *iso = ch->iso_self; + off_t size = iso->attrib.st_size; + + iso->block = ch->block = t->curblock; + t->curblock += div_up(size, t->block_size); + if (size && iso->loc.type == LIBISO_FILESYS + && iso->loc.path) + t->filelist[t->curfile++] = ch; + } + } + + for (i = 0; i < dir->dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->dir.children[i]; + if (ch->type == ECMA119_DIR) + calc_file_pos(t, ch); + } + + /* reset curfile when we're finished */ + if (!dir->parent) { + t->curfile = 0; + } +} + +struct ecma119_write_target* +ecma119_target_new(struct iso_volset *volset, + int volnum, + int level, + int flags) +{ + struct ecma119_write_target *t = + calloc(1, sizeof(struct ecma119_write_target)); + size_t i, j, cur; + struct iso_tree_node *iso_root = volset->volume[volnum]->root; + + volset->refcount++; + t->root = ecma119_tree_create(t, iso_root); + t->joliet = (flags & ECMA119_JOLIET) ? 1 : 0; + if (t->joliet) + t->joliet_root = joliet_tree_create(t, iso_root); + t->volset = volset; + t->volnum = volnum; + t->now = time(NULL); + + t->rockridge = (flags & ECMA119_ROCKRIDGE) ? 1 : 0; + t->iso_level = level; + t->block_size = 2048; + + 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); + t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len); + } + + t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len); + t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len); + t->filelist = calloc(1, sizeof(void*) * t->filelist_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->dir.nchildren; j++) { + struct ecma119_tree_node *ch = dir->dir.children[j]; + if (ch->type == ECMA119_DIR) { + size_t len = 8 + strlen(ch->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->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); + if (t->joliet) + joliet_calc_dir_pos(t, t->joliet_root); + calc_file_pos(t, t->root); + if (t->joliet) + joliet_update_file_pos (t, t->joliet_root); + + 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 void +next_state(struct ecma119_write_target *t) +{ + t->state++; + while (!t->joliet && is_joliet_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 ecma119_tree_node *f = t->filelist[f_st->file]; + const char *path = f->iso_self->loc.path; + + if (!f_st->fd) { + f_st->data_len = f->iso_self->attrib.st_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 = str2ascii(volume->volume_id); + char *pub_id = str2ascii(volume->publisher_id); + char *data_id = str2ascii(volume->data_preparer_id); + char *volset_id = str2ascii(t->volset->volset_id); + + vol->vol_desc_type[0] = 1; + memcpy(vol->std_identifier, "CD001", 5); + vol->vol_desc_version[0] = 1; + memcpy(vol->system_id, "SYSID", 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); + + 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); + strncpy((char*)vol->application_id, "APPID", 128); + + 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); +} + +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]; + 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->name) : 1; + rec->len_xa[0] = 0; + write_int(rec->block, 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); + } +} + +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) +{ + uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len; + uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->name); + uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id); + uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name; + uint32_t len = (node->type == ECMA119_DIR) ? node->dir.len + : node->file.real_me ? 0 : node->iso_self->attrib.st_size; + struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; + + /* we don't write out susp fields for the root node */ + if (t->rockridge) { + if (file_id == 0) { + susp_write(t, &node->dir.self_susp, &buf[len_dr]); + len_dr += node->dir.self_susp.non_CE_len; + } else if (file_id == 1) { + susp_write(t, &node->dir.parent_susp, &buf[len_dr]); + len_dr += node->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, node->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->dir.nchildren; i++) { + write_one_dir_record(t, dir->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->dir.self_susp, buf); + buf += dir->dir.self_susp.CE_len; + susp_write_CE(t, &dir->dir.parent_susp, buf); + buf += dir->dir.parent_susp.CE_len; + for (i = 0; i < dir->dir.nchildren; i++) { + susp_write_CE(t, &dir->dir.children[i]->susp, buf); + buf += dir->dir.children[i]->susp.CE_len; + } + } + assert (buf - orig_buf == dir->dir.len + dir->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->dir.len + dir->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); + free(t->dirlist); + free(t->pathlist); + free(t->dirlist_joliet); + free(t->pathlist_joliet); + free(t->filelist); + free(t->state_data); + if (t->state_files.fd) + fclose(t->state_files.fd); +} + +struct burn_source *iso_source_new_ecma119(struct iso_volset *volset, + int volnum, + int level, + int flags) +{ + 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, volnum, level, flags); + return ret; +} diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h new file mode 100755 index 0000000..7950550 --- /dev/null +++ b/libisofs/ecma119.h @@ -0,0 +1,267 @@ +/* -*- 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_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_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; + + 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 ecma119_tree_node **filelist; + /**< A pre-order list of files with + * non-NULL paths and non-zero sizes. + */ + 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; + + 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; +}; + +/** + * 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. + */ +struct ecma119_write_target *ecma119_target_new(struct iso_volset *volset, + int volnum, + int level, + int flags); + +#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_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/ecma119_tree.c b/libisofs/ecma119_tree.c new file mode 100644 index 0000000..0cbd387 --- /dev/null +++ b/libisofs/ecma119_tree.c @@ -0,0 +1,312 @@ +/* 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" + +static size_t calc_dirent_len(struct ecma119_tree_node *n) +{ + int ret = n->name ? strlen(n->name) + 33 : 34; + if (ret % 2) ret++; + return ret; +} + +static struct ecma119_tree_node* +create_dir(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node *iso) +{ + struct ecma119_tree_node *ret; + + assert(t && (!parent || parent->type == ECMA119_DIR) + && iso && S_ISDIR(iso->attrib.st_mode)); + + ret = calloc(1, sizeof(struct ecma119_tree_node)); + ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_dirid(iso->name) + : iso_2_dirid(iso->name)) + : NULL; + ret->dirent_len = calc_dirent_len(ret); + ret->iso_self = iso; + ret->target = t; + ret->type = ECMA119_DIR; + ret->parent = ret->dir.real_parent = parent; + ret->dir.depth = parent ? parent->dir.depth + 1 : 1; + ret->dir.nchildren = iso->nchildren; + ret->dir.children = calloc(1, sizeof(void*) * iso->nchildren); + return ret; +} + +static struct ecma119_tree_node* +create_file(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node *iso) +{ + struct ecma119_tree_node *ret; + + assert(t && iso && parent && parent->type == ECMA119_DIR); + + ret = calloc(1, sizeof(struct ecma119_tree_node)); + ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_fileid(iso->name) + : iso_2_fileid(iso->name)) + : NULL; + ret->dirent_len = calc_dirent_len(ret); + ret->parent = parent; + ret->iso_self = iso; + ret->target = t; + ret->type = ECMA119_FILE; + return ret; +} + +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; + size_t i; + + assert(t && iso); + + ret = (S_ISDIR(iso->attrib.st_mode) ? create_dir : create_file) + (t, parent, iso); + for (i = 0; i < iso->nchildren; i++) { + ret->dir.children[i] = create_tree(t, ret, iso->children[i]); + } + return ret; +} + +void +ecma119_tree_free(struct ecma119_tree_node *root) +{ + size_t i; + + if (root->type == ECMA119_DIR) { + for (i=0; i < root->dir.nchildren; i++) { + ecma119_tree_free(root->dir.children[i]); + } + free(root->dir.children); + } + free(root->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->dir.nchildren; i++) { + size_t len = strlen(root->dir.children[i]->name); + ret = MAX(ret, len); + } + return ret; +} + +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->dir.nchildren; i++) { + if (child->parent->dir.children[i] == child) { + placeholder = create_file(child->target, + child->parent, + child->iso_self); + placeholder->file.real_me = child; + child->parent->dir.children[i] = placeholder; + found = 1; + break; + } + } + assert(found); + + /* add the child to its new parent */ + child->parent = parent; + parent->dir.nchildren++; + parent->dir.children = realloc( parent->dir.children, + sizeof(void*) * parent->dir.nchildren ); + parent->dir.children[parent->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 + */ +static void +reorder_tree(struct ecma119_tree_node *root, + struct ecma119_tree_node *cur) +{ + size_t max_path; + + assert(root && cur && cur->type == ECMA119_DIR); + + cur->dir.depth = cur->parent ? cur->parent->dir.depth + 1 : 1; + cur->dir.path_len = cur->parent ? cur->parent->dir.path_len + + strlen(cur->name) : 0; + max_path = cur->dir.path_len + cur->dir.depth + max_child_name_len(cur); + + if (cur->dir.depth > 8 || max_path > 255) { + 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 { + size_t i; + + for (i=0; i < cur->dir.nchildren; i++) { + if (cur->dir.children[i]->type == ECMA119_DIR) + reorder_tree(root, cur->dir.children[i]); + } + } +} + +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->name, g->name); +} + +static void +sort_tree(struct ecma119_tree_node *root) +{ + size_t i; + + assert(root && root->type == ECMA119_DIR); + + qsort(root->dir.children, root->dir.nchildren, sizeof(void*), cmp_node); + for (i=0; i < root->dir.nchildren; i++) { + if (root->dir.children[i]->type == ECMA119_DIR) + sort_tree(root->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); +} + +static void +mangle_all(struct ecma119_tree_node *dir) +{ + size_t i, j, k; + struct ecma119_dir_info d = dir->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]->name, + d.children[i+j]->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]->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); + reorder_tree(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->name); + if (root->type == ECMA119_DIR) + for (i=0; i < root->dir.nchildren; i++) + ecma119_tree_print(root->dir.children[i], spaces+2); +} diff --git a/libisofs/ecma119_tree.h b/libisofs/ecma119_tree.h new file mode 100644 index 0000000..1d6023b --- /dev/null +++ b/libisofs/ecma119_tree.h @@ -0,0 +1,95 @@ +/* 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 + +struct ecma119_write_target; + +enum { + ECMA119_FILE, + ECMA119_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 */ + + 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 */ +}; + +struct ecma119_file_info +{ + struct ecma119_tree_node *real_me; + /**< If this is non-NULL, the file is + * a placeholder for a relocated + * directory and this field points to + * that relocated directory. + */ +}; + +/** + * A node for a tree containing all the information necessary for writing + * an ISO9660 volume. + */ +struct ecma119_tree_node +{ + char *name; /**< in ASCII, conforming to the + * current ISO level. */ + size_t dirent_len; /**< Length of the directory record, + * not including SU. */ + size_t block; + + struct ecma119_tree_node *parent; + struct iso_tree_node *iso_self; + struct ecma119_write_target *target; + + struct susp_info susp; + + int type; /**< file or directory */ + /* union {*/ + struct ecma119_dir_info dir; + struct ecma119_file_info file; + /* };*/ +}; + +/** + * 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/exclude.c b/libisofs/exclude.c new file mode 100644 index 0000000..48e7fa2 --- /dev/null +++ b/libisofs/exclude.c @@ -0,0 +1,42 @@ +#include "hash.h" +#include "exclude.h" + +static struct iso_hash_node *table[HASH_NODES]={0,}; +static int num=0; + +void +iso_exclude_add_path(const char *path) +{ + if (!path) + return; + + num += iso_hash_insert(table, path); +} + +void +iso_exclude_remove_path(const char *path) +{ + if (!num || !path) + return; + + num -= iso_hash_remove(table, path); +} + +void +iso_exclude_empty(void) +{ + if (!num) + return; + + iso_hash_empty(table); + num=0; +} + +int +iso_exclude_lookup(const char *path) +{ + if (!num || !path) + return 0; + + return iso_hash_lookup(table, path); +} diff --git a/libisofs/exclude.h b/libisofs/exclude.h new file mode 100644 index 0000000..319c418 --- /dev/null +++ b/libisofs/exclude.h @@ -0,0 +1,12 @@ +#ifndef ISO_EXCLUDE_H +#define ISO_EXCLUDE_H + +/** + * 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(const char *path); + +#endif /* ISO_EXCLUDE */ diff --git a/libisofs/hash.c b/libisofs/hash.c new file mode 100644 index 0000000..1976efa --- /dev/null +++ b/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/hash.h b/libisofs/hash.h new file mode 100644 index 0000000..2d691c7 --- /dev/null +++ b/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/joliet.c b/libisofs/joliet.c new file mode 100644 index 0000000..34be4d1 --- /dev/null +++ b/libisofs/joliet.c @@ -0,0 +1,400 @@ +/* -*- 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 +#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); + ret->dirent_len = 34 + (ret->name ? ucslen(ret->name) * 2 : 0); + ret->len = iso->attrib.st_size; /* for dirs, we'll change this */ + ret->block = iso->block; /* only actually for files, not dirs */ + ret->parent = parent; + ret->iso_self = iso; + ret->target = t; + ret->nchildren = iso->nchildren; + if (ret->nchildren) + ret->children = calloc(sizeof(void*), ret->nchildren); + return ret; +} + +static struct joliet_tree_node* +create_tree(struct ecma119_write_target *t, + struct joliet_tree_node *parent, + struct iso_tree_node *iso_root) +{ + struct joliet_tree_node *root = create_node(t, parent, iso_root); + size_t i; + + for (i = 0; i < root->nchildren; i++) { + struct iso_tree_node *iso_ch = iso_root->children[i]; + if (ISO_ISDIR(iso_ch)) + root->children[i] = create_tree(t, root, iso_ch); + else + root->children[i] = create_node(t, root, iso_ch); + } + 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 && ISO_ISDIR(root->iso_self)); + + qsort(root->children, root->nchildren, sizeof(void*), cmp_node); + for (i = 0; i < root->nchildren; i++) + if (ISO_ISDIR(root->children[i]->iso_self)) + sort_tree(root->children[i]); +} + +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; i++) { + struct joliet_tree_node *dir = t->pathlist_joliet[i]; + for (j = 0; j < dir->nchildren; j++) { + struct joliet_tree_node *ch = dir->children[j]; + if (ISO_ISDIR(ch->iso_self)) { + 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 && ISO_ISDIR(root->iso_self)); + + root->len = 68; /* for "." and ".." entries */ + for (i = 0; i < root->nchildren; i++) { + ch = root->children[i]; + newlen = root->len + ch->dirent_len; + if ((newlen % 2048) < (root->len % 2048)) { + root->len = newlen + (2048 - (root->len % 2048)); + } + else { + root->len += ch->dirent_len; + } + if (ISO_ISDIR(ch->iso_self)) + joliet_calc_dir_size(t, ch); + } + t->total_dir_size_joliet += round_up (root->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 && ISO_ISDIR(root->iso_self)); + + root->block = t->curblock; + t->curblock += div_up(root->len, t->block_size); + + t->dirlist_joliet[t->curfile++] = root; + for (i = 0; i < root->nchildren; i++) { + ch = root->children[i]; + if (ISO_ISDIR(ch->iso_self)) + joliet_calc_dir_pos(t, ch); + } + + /* reset curfile when we're finished */ + if (!root->parent) + t->curfile = 0; +} + +void +joliet_update_file_pos(struct ecma119_write_target *t, + struct joliet_tree_node *dir) +{ + size_t i; + + assert(dir && ISO_ISDIR(dir->iso_self)); + for (i = 0; i < dir->nchildren; i++) { + struct joliet_tree_node *ch; + ch = dir->children[i]; + + if (!ISO_ISDIR (ch->iso_self)) { + struct iso_tree_node *iso = ch->iso_self; + ch->block = iso->block; + } + else + joliet_update_file_pos(t, ch); + } + + /* reset curfile when we're finished */ + if (!dir->parent) + t->curfile = 0; +} + +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; +} + +/* 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->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) +{ + 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 (file_id == 1 && node->parent) + node = node->parent; + + rec->len_dr[0] = len_dr; + iso_bb(rec->block, node->block, 4); + iso_bb(rec->length, node->len, 4); + iso_datetime_7(rec->recording_time, t->now); + rec->flags[0] = ISO_ISDIR(node->iso_self) ? 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); + uint16_t *pub_id = str2ucs(volume->publisher_id); + uint16_t *data_id = str2ucs(volume->data_preparer_id); + uint16_t *volset_id = str2ucs(t->volset->volset_id); + 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); + + vol->vol_desc_type[0] = 2; + memcpy(vol->std_identifier, "CD001", 5); + vol->vol_desc_version[0] = 1; + memcpy(vol->system_id, "SYSID", 5); + 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->application_id, "APPID", app_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); + +} + +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(ISO_ISDIR (dir->iso_self)); + /* 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->nchildren; i++) { + write_one_dir_record(t, 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->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]->block); + for (i = 0; i < t->dirlist_len; i++) { + dir = t->dirlist_joliet[i]; + write_one_dir(t, dir, buf); + buf += round_up(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/joliet.h b/libisofs/joliet.h new file mode 100644 index 0000000..e5b7582 --- /dev/null +++ b/libisofs/joliet.h @@ -0,0 +1,84 @@ +/* -*- 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; + +struct joliet_tree_node +{ + uint16_t *name; /**< In UCS-2BE. */ + size_t dirent_len; + size_t len; + size_t block; + + struct joliet_tree_node *parent; + struct iso_tree_node *iso_self; + struct ecma119_write_target *target; + + struct joliet_tree_node **children; + size_t nchildren; +}; + +/** + * 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*); + +/** + * Update the position of each file in the joliet hierarchy (to be called + * AFTER the file positions in the iso tree have been set). + */ +void +joliet_update_file_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/libisofs.h b/libisofs/libisofs.h new file mode 100755 index 0000000..0d29cad --- /dev/null +++ b/libisofs/libisofs.h @@ -0,0 +1,225 @@ +/* -*- 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 */ +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; + +enum ecma119_extension_flag { + ECMA119_ROCKRIDGE = (1<<0), + ECMA119_JOLIET = (1<<1) +}; + +/** + * 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 *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 *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); + +/** + * 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. + * + */ +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); + +/** + * Add a file to a directory. + * + * \param path The path, on the local filesystem, of the file. + * + * \pre \p parent is NULL or is a directory. + * \pre \p path is non-NULL and is a valid path to a non-directory on the local + * filesystem. + * \return An iso_tree_node whose path is \p path and whose parent is \p parent. + */ +struct iso_tree_node *iso_tree_add_node(struct iso_tree_node *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 and iso_tree_add_dir. + * + * \param path The path, on the local filesystem, of the directory to add. + * + * \pre \p parent is NULL or is a directory. + * \pre \p path is non-NULL and is a valid path to a directory on the local + * filesystem. + * \return a pointer to the newly created directory. + */ +struct iso_tree_node *iso_tree_radd_dir(struct iso_tree_node *parent, + 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(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(const char *path); + +/** + * Remove all paths that were set to be ignored when adding a directory recusively. + */ +void iso_exclude_empty(void); + +/** + * Creates a new, empty directory on the volume. + * + * \pre \p parent is NULL or is a directory. + * \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 *iso_tree_add_new_dir(struct iso_tree_node *parent, + const char *name); + +/** + * Set the name of a file (using the current locale). + */ +void iso_tree_node_set_name(struct iso_tree_node *file, const char *name); + +/** + * 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 volnum The volume in the set which you want to write (usually 0) + * \param level ISO level to write at. + * \param flags Which extensions to support. + * + * \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, + int volnum, + int level, + int flags); + +#endif /* LIBISO_LIBISOFS_H */ diff --git a/libisofs/rockridge.c b/libisofs/rockridge.c new file mode 100755 index 0000000..3662ac6 --- /dev/null +++ b/libisofs/rockridge.c @@ -0,0 +1,300 @@ +/* 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 + +/* 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->iso_self->attrib.st_mode, 4); + iso_bb(&PX[12], node->iso_self->attrib.st_nlink, 4); + iso_bb(&PX[20], node->iso_self->attrib.st_uid, 4); + iso_bb(&PX[28], node->iso_self->attrib.st_gid, 4); + iso_bb(&PX[36], node->iso_self->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->dir.self_susp, rrip_make_PX(t, node)); + susp_append(t, &node->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->iso_self->attrib.st_dev >> 32, 4); + iso_bb(&PN[12], node->iso_self->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 ret, pathsize = 0; + 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; + + do { + pathsize += 128; + path = realloc(path, pathsize); + /* FIXME: what if the file is not on the local fs? */ + ret = readlink(node->iso_self->loc.path, path, pathsize); + } while (ret == pathsize); + if (ret == -1) { + fprintf(stderr, "Error: couldn't read symlink: %s\n", + strerror(errno)); + return; + } + path[ret] = '\0'; + + 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[ret - 1]) { + rrip_SL_add_component(prev, &path[ret], &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(path); + /* 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 = iso_p_fileid(node->iso_self->name); + int len = name ? strlen(name) : 0; + char *pos = name; + + if (!len) + return; + + if (node->type == ECMA119_DIR) { + rrip_add_NM_single(t, &node->dir.self_susp, pos, 0, 1 << 1); + rrip_add_NM_single(t, &node->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->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) | (1 << 7); + iso_datetime_7(&TF[5], node->iso_self->attrib.st_mtime); + iso_datetime_7(&TF[12], node->iso_self->attrib.st_atime); + iso_datetime_7(&TF[19], node->iso_self->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->dir.real_parent) { + uint8_t *PL = susp_find(&dir->dir.parent_susp, "PL"); + + assert(PL); + iso_bb(&PL[4], dir->dir.real_parent->block, 4); + } + + for (i = 0; i < dir->dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->dir.children[i]; + + if (ch->type == ECMA119_FILE && ch->file.real_me) { + uint8_t *CL = susp_find(&ch->susp, "CL"); + + assert(CL); + iso_bb(&CL[4], ch->file.real_me->block, 4); + } else if (ch->type == ECMA119_DIR) { + rrip_finalize(t, ch); + } + } +} diff --git a/libisofs/rockridge.h b/libisofs/rockridge.h new file mode 100755 index 0000000..7909a0c --- /dev/null +++ b/libisofs/rockridge.h @@ -0,0 +1,26 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** Functions and structures used for Rock Ridge support. */ + +#ifndef ISO_ROCKRIDGE_H +#define ISO_ROCKRIDGE_H + +struct ecma119_write_target; +struct ecma119_tree_node; + +void rrip_add_PX(struct ecma119_write_target *, struct ecma119_tree_node *); +void rrip_add_PN(struct ecma119_write_target *, struct ecma119_tree_node *); +void rrip_add_SL(struct ecma119_write_target *, struct ecma119_tree_node *); +void rrip_add_NM(struct ecma119_write_target *, struct ecma119_tree_node *); +void rrip_add_CL(struct ecma119_write_target *, struct ecma119_tree_node *); +void rrip_add_RE(struct ecma119_write_target *, struct ecma119_tree_node *); +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 *); + +#endif /* ISO_ROCKRIDGE_H */ diff --git a/libisofs/susp.c b/libisofs/susp.c new file mode 100755 index 0000000..0d2d6c5 --- /dev/null +++ b/libisofs/susp.c @@ -0,0 +1,280 @@ +/* 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->dir.self_susp, 34); + try_add_CE(t, &node->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->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 + +/** 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->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. + * + * Requires that the following be known: + * - position of the current directory (dir->block) + * - length of the current directory (dir->dir.len) + * - sum of the children's CE lengths (dir->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->dir.len; + + assert(dir->type == ECMA119_DIR); + + susp_fin_1_CE(t, &dir->dir.self_susp, dir->block, &CE_offset); + susp_fin_1_CE(t, &dir->dir.parent_susp, dir->block, &CE_offset); + + for (i = 0; i < dir->dir.nchildren; i++) { + struct ecma119_tree_node *ch = dir->dir.children[i]; + susp_fin_1_CE(t, &ch->susp, dir->block, &CE_offset); + } + assert(CE_offset == dir->dir.len + dir->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->dir.depth != 1) { + susp_fin_CE(t, dir); + } + + for (i = 0; i < dir->dir.nchildren; i++) { + if (dir->dir.children[i]->type == ECMA119_DIR) + susp_finalize(t, dir->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/susp.h b/libisofs/susp.h new file mode 100755 index 0000000..31dd2cf --- /dev/null +++ b/libisofs/susp.h @@ -0,0 +1,62 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** Functions and structures used for SUSP (IEEE 1281). + */ + +#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. */ +}; + +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. */ +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. */ +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/tree.c b/libisofs/tree.c new file mode 100755 index 0000000..57aa0df --- /dev/null +++ b/libisofs/tree.c @@ -0,0 +1,223 @@ +/* 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 "util.h" +#include "volume.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 = 0777 | S_IFREG; + s->st_atime = s->st_mtime = s->st_ctime = now; +} + +static struct stat +get_attrib(const struct iso_tree_node *node) +{ + struct stat st; + + if (node) { + return node->attrib; + } + set_default_stat(&st); + return st; +} + +static void +append_node(struct iso_tree_node *parent, + struct iso_tree_node *child) +{ + assert((!parent || S_ISDIR(parent->attrib.st_mode)) && child); + if (!parent) + return; + + parent->nchildren++; + parent->children = + realloc(parent->children, parent->nchildren * sizeof(void*)); + parent->children[parent->nchildren-1] = child; +} + +struct iso_tree_node* +iso_tree_new_root(struct iso_volume *vol) +{ + assert(vol); + + if (vol->root) { + iso_tree_free(vol->root); + } + + vol->root = calloc(1, sizeof(struct iso_tree_node)); + vol->root->volume = vol; + set_default_stat(&vol->root->attrib); + vol->root->attrib.st_mode = S_IFDIR | 0777; + vol->root->loc.type = LIBISO_NONE; + return vol->root; +} + +struct iso_tree_node* +iso_tree_add_new_file(struct iso_tree_node *parent, const char *name) +{ + struct iso_tree_node *f = calloc(1, sizeof(struct iso_tree_node)); + + assert((!parent || S_ISDIR(parent->attrib.st_mode)) && name); + + f->volume = parent ? parent->volume : NULL; + f->parent = parent; + f->name = parent ? strdup(name) : NULL; + f->attrib = get_attrib(parent); + f->attrib.st_mode = 0777 | S_IFREG; + f->loc.type = LIBISO_NONE; + append_node(parent, f); + return f; +} + +struct iso_tree_node* +iso_tree_add_new_dir(struct iso_tree_node *parent, const char *name) +{ + struct iso_tree_node *d = iso_tree_add_new_file(parent, name); + + assert((!parent || S_ISDIR(parent->attrib.st_mode)) && name); + + d->attrib.st_mode = (d->attrib.st_mode & ~S_IFMT) | S_IFDIR; + return d; +} + +struct iso_tree_node* +iso_tree_add_node(struct iso_tree_node *parent, const char *path) +{ + char *p; + struct stat st; + struct iso_tree_node *ret; + + assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path); + + if (lstat(path, &st) == -1) + return NULL; + + p = strdup(path); /* because basename() might modify its arg */ + + /* it doesn't matter if we add a file or directory since we modify + * attrib anyway. */ + ret = iso_tree_add_new_file(parent, basename(p)); + ret->attrib = st; + ret->loc.type = LIBISO_FILESYS; + ret->loc.path = strdup(path); + free(p); + + return ret; +} + +struct iso_tree_node* +iso_tree_radd_dir (struct iso_tree_node *parent, const char *path) +{ + struct iso_tree_node *new; + DIR *dir; + struct dirent *ent; + + assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path); + + new = iso_tree_add_node(parent, path); + if (!new || !S_ISDIR(new->attrib.st_mode)) { + return new; + } + + dir = opendir(path); + if (!dir) { + warn("couldn't open directory %s: %s\n", path, strerror(errno)); + return new; + } + + while ((ent = readdir(dir))) { + char child[strlen(ent->d_name) + strlen(path) + 2]; + + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + continue; + + sprintf(child, "%s/%s", path, ent->d_name); + + /* see if this child is excluded. */ + if (iso_exclude_lookup(child)) + continue; + + iso_tree_radd_dir(new, child); + } + closedir(dir); + + return new; +} + +void +iso_tree_free(struct iso_tree_node *root) +{ + size_t i; + + for (i=0; i < root->nchildren; i++) { + iso_tree_free(root->children[i]); + } + free(root->name); + free(root->children); + free(root); +} + +void +iso_tree_print(const struct iso_tree_node *root, int spaces) +{ + size_t i; + char sp[spaces+1]; + + memset(sp, ' ', spaces); + sp[spaces] = '\0'; + + printf("%s%sn", sp, root->name); + for (i=0; i < root->nchildren; i++) { + iso_tree_print(root->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) +{ + size_t i; + + (S_ISDIR(root->attrib.st_mode) ? dir : file) + (root, callback_data, spaces); + for (i=0; i < root->nchildren; i++) { + iso_tree_print_verbose(root->children[i], dir, + file, callback_data, spaces+2); + } +} + +void +iso_tree_node_set_name(struct iso_tree_node *file, const char *name) +{ + free(file->name); + file->name = strdup(name); +} diff --git a/libisofs/tree.h b/libisofs/tree.h new file mode 100755 index 0000000..e0e0460 --- /dev/null +++ b/libisofs/tree.h @@ -0,0 +1,159 @@ +/* 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; + /* };*/ +}; + +/** + * A node in the filesystem tree. + */ +struct iso_tree_node +{ + struct iso_volume *volume; + struct iso_tree_node *parent; + char *name; + struct stat attrib; /**< The POSIX attributes of this node as + * documented in "man 2 stat". */ + struct iso_file_location loc; + /**< Only used for regular files and symbolic + * links (ie. files for which we might have to + * copy data). */ + + size_t nchildren; /**< The number of children of this + * directory (if this is a directory). */ + struct iso_tree_node **children; + + size_t block; /**< The block at which this file will + * reside on disk. We store this here as + * well as in the various mangled trees + * because many different trees might point + * to the same file and they need to share the + * block location. */ +}; + +/** + * Create a new root directory for a volume. + * + * \param vol The volume for which to create a new root directory. + * + * \pre \p vol is non-NULL. + * \post \p vol has a non-NULL, empty root directory with permissions 777. + * \return \p vol's new non-NULL, empty root directory. + */ +struct iso_tree_node *iso_tree_new_root(struct iso_volume *vol); + +/** + * Create a new, empty, file. + * + * \param parent The parent directory of the new file. If this is null, create + * and return a new file node without adding it to any tree. + * \param name The name of the new file, encoded in the current locale. + * \pre \p name is non-NULL and it does not match any other file or directory + * name in \p parent. + * \post \p parent (if non-NULL) contains a file with the following properties: + * - the file's name is \p name (converted to wchar_t) + * - the file's POSIX permissions are the same as \p parent's + * - the file is a regular file + * - the file is empty + * + * \return \p parent's newly created file. + */ +struct iso_tree_node *iso_tree_add_new_file(struct iso_tree_node *parent, + const char *name); + +/** + * 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); + +/** + * 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) S_ISDIR(n->attrib.st_mode) + +#endif /* LIBISO_TREE_H */ diff --git a/libisofs/util.c b/libisofs/util.c new file mode 100755 index 0000000..2187b6f --- /dev/null +++ b/libisofs/util.c @@ -0,0 +1,584 @@ +/* -*- 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 "util.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; +} + +/* 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) +{ + wchar_t wsrc_[NAME_BUFFER_SIZE]; + char *src = (char*)wsrc_; + char *ret_; + char *ret; + mbstate_t state; + iconv_t conv; + size_t numchars; + size_t outbytes; + size_t inbytes; + size_t n; + + 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. */ + memset(&state, 0, sizeof(state)); + numchars = mbsrtowcs(wsrc_, &src_arg, NAME_BUFFER_SIZE-1, &state); + if (numchars < 0) + return NULL; + + 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) + 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'; + return ret_; +} + +/* FIXME: C&P */ +uint16_t *str2ucs(const char *src_arg) +{ + wchar_t wsrc_[NAME_BUFFER_SIZE]; + char *src = (char*)wsrc_; + char *ret_; + char *ret; + mbstate_t state; + 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. */ + memset(&state, 0, sizeof(state)); + numchars = mbsrtowcs(wsrc_, &src_arg, NAME_BUFFER_SIZE-1, &state); + if (numchars < 0) + return calloc(2, 1); /* empty UCS string */ + + 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 == '-'); +} + +static char *iso_dirid(const char *src, int size) +{ + char *ret = str2ascii(src); + 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) +{ + return iso_dirid(src, 8); +} + +char *iso_2_dirid(const char *src) +{ + return iso_dirid(src, 31); +} + +char *iso_1_fileid(const char *src_arg) +{ + char *src = str2ascii(src_arg); + 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) +{ + char *src = str2ascii(src_arg); + 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 == src || *(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_p_fileid(const char *src) +{ + char *ret = str2ascii(src); + 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) +{ + uint16_t *j_str = str2ucs(src_arg); + 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 < -48) + 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 < -48) + 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; +} + +wchar_t *towcs(const char *); +char *str2ascii(const char*); +uint16_t *str2ucs(const char*); + +/** + * Create a level 1 directory identifier. + */ +char *iso_1_dirid(const char *src); + +/** + * Create a level 2 directory identifier. + */ +char *iso_2_dirid(const char *src); + +/** + * 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); + +/** + * 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); + +/** + * 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); + +/** + * 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); + +/** + * 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/volume.c b/libisofs/volume.c new file mode 100755 index 0000000..68b2a0c --- /dev/null +++ b/libisofs/volume.c @@ -0,0 +1,188 @@ +/* -*- 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" + +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 *root) +{ + struct iso_volume *volume; + + volume = calloc(1, sizeof(struct iso_volume)); + volume->refcount = 1; + + volume->root = root ? root : iso_tree_new_root(volume); + + 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(volume->root); + + free(volume->volume_id); + free(volume->publisher_id); + free(volume->data_preparer_id); + + free(volume); + } +} + +struct iso_tree_node * +iso_volume_get_root(const struct iso_volume *volume) +{ + return volume->root; +} + +struct iso_tree_node * +iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path) +{ + struct iso_tree_node *node; + char *ptr, *brk_info, *component; + + /* get the first child at the root of the volume + * that is "/" */ + node=iso_volume_get_root(volume); + if (!strcmp (path, "/")) + return node; + + if (!node->nchildren) + return NULL; + + /* the name of the nodes is in wide characters so first convert path + * into wide characters. */ + ptr = strdup(path); + + /* get the first component of the path */ + component=strtok_r(ptr, "/", &brk_info); + while (component) { + size_t max; + size_t i; + + /* search among all the children of this directory if this path component exists */ + max=node->nchildren; + for (i=0; i < max; i++) { + if (!strcmp(component, node->children[i]->name)) { + node=node->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/volume.h b/libisofs/volume.h new file mode 100755 index 0000000..8903186 --- /dev/null +++ b/libisofs/volume.h @@ -0,0 +1,45 @@ +/* -*- 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 *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. */ +}; + +/** + * 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/test/Makefile b/test/Makefile new file mode 100644 index 0000000..062350d --- /dev/null +++ b/test/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/test/iso.c b/test/iso.c new file mode 100644 index 0000000..afa9cf2 --- /dev/null +++ b/test/iso.c @@ -0,0 +1,107 @@ +/* -*- 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: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" +" -h Print this message\n" +); +} + +int main(int argc, char **argv) +{ + struct iso_volset *volset; + struct iso_volume *volume; + struct iso_tree_node *root; + struct burn_source *src; + unsigned char buf[2048]; + FILE *fd; + int c; + int level=1, flags=0; + + 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 '?': + 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_radd_dir(NULL, argv[optind]); + if (!root) { + err(1, "error opening input directory"); + } + volume = iso_volume_new_with_root( "VOLID", "PUBID", "PREPID", root ); + volset = iso_volset_new( volume, "VOLSETID" ); + + src = iso_source_new_ecma119(volset, 0, level, flags); + + while (src->read(src, buf, 2048) == 2048) { + fwrite(buf, 1, 2048, fd); + } + fclose(fd); + + return 0; +} diff --git a/test/iso.py b/test/iso.py new file mode 100644 index 0000000..61da5fd --- /dev/null +++ b/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/test/tree.py b/test/tree.py new file mode 100644 index 0000000..a7be3d8 --- /dev/null +++ b/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/version.h.in b/version.h.in new file mode 100644 index 0000000..13ada99 --- /dev/null +++ b/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@